/**
  * LzMouseKernel.as
  *
  * @copyright Copyright 2001-2009 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @topic Kernel
  * @subtopic swf9
  */

// Receives mouse events from the runtime
class LzMouseKernel  {
    #passthrough (toplevel:true) {
    import flash.display.Sprite;
    import flash.display.DisplayObject;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.text.TextField;
    import flash.ui.*;
    import flash.utils.getDefinitionByName;
    }#

    //////////////////
    // MOUSE EVENTS //
    //////////////////

    // sends mouse events to the callback
    static function handleMouseEvent (view:*, eventname:String) :void {
        if (__callback) __scope[__callback](eventname, view);
        //Debug.write('LzMouseKernel event', eventname);
    }

    static var __callback:String = null;
    static var __scope:* = null;
    static var __lastMouseDown:LzSprite = null;
    static var __mouseLeft:Boolean = false;
    static var __listeneradded:Boolean = false;

    /**
     * Shows or hides the hand cursor for all clickable views.
     */
    static var showhandcursor:Boolean = true;

    static function setCallback (scope:*, funcname:String) :void {
        __scope = scope;
        __callback = funcname;
        if (__listeneradded == false) {
            /* TODO [hqm 2008-01] Do we want to do anything with other
            * events, like click?
            stage.addEventListener(MouseEvent.CLICK, reportClick);
            */

            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_MOVE, __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_UP,   __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_DOWN, __mouseHandler);
            // handled by lz.embed.mousewheel for Windows LFCApplication.stage.addEventListener(MouseEvent.MOUSE_WHEEL, __mouseWheelHandler);
            LFCApplication.stage.addEventListener(Event.MOUSE_LEAVE, __mouseLeaveHandler);
            __listeneradded = true;
        }
    }

    // Handles global mouse events
    static function __mouseHandler (event:MouseEvent) :void {
        var eventname:String = 'on' + event.type.toLowerCase();
        if (eventname == 'onmouseup' && __lastMouseDown != null) {
            // call mouseup on the sprite that got the last mouse down
            __lastMouseDown.__globalmouseup(event);
            __lastMouseDown = null;
        } else {
            if (__mouseLeft) {
                __mouseLeft = false;
                // Mouse reentered the app.
                if (event.buttonDown) __mouseUpOutsideHandler();

                // TODO [hqm 2009-04] LPP-7957 -- this works
                // around Firefox bug; if you are making a text
                // selection, and then drag the mouse outside of
                // the app while the left button is down, and then
                // release it. When you bring the mouse back into
                // the app, the selection is stuck dragging
                // because Flash plugin never gets the mouse up.
                LFCApplication.stage.focus = null;

                // generate a 'onmouseenter' event
                handleMouseEvent(null, 'onmouseenter');
            }
            handleMouseEvent(null, eventname);
        }
    }

    // sends mouseup and calls __globalmouseup when the mouse goes up outside the app - see LPP-7724
    static function __mouseUpOutsideHandler () :void {
        if (__lastMouseDown != null) {
            var ev:MouseEvent = new MouseEvent('mouseup');
            __lastMouseDown.__globalmouseup(ev);
            __lastMouseDown = null;
        }
    }

    // handles MOUSE_LEAVE event
    static function __mouseLeaveHandler (event:Event = null) :void {
        __mouseLeft = true;
        handleMouseEvent(null, 'onmouseleave');
    }

    static function __mouseWheelHandler (event:MouseEvent) :void {
        lz.Keys.__mousewheelEvent(event.delta);
    }

    //////////////////
    // MOUSE CURSOR //
    //////////////////

    /**
    * Shows or hides the hand cursor for all clickable views.
    * @param Boolean show: true shows the hand cursor for buttons, false hides it
    */
    static function showHandCursor (show:Boolean) :void {
        showhandcursor = show;
        LzSprite.rootSprite.setGlobalHandCursor(show);
    }

    static var __amLocked:Boolean = false;
    static var useBuiltinCursor:Boolean = false;
    static var cursorSprite:Sprite = null;
    static var cursorOffsetX:int = 0;
    static var cursorOffsetY:int = 0;
    static var globalCursorResource:String = null;
    static var lastCursorResource:String = null;

    #passthrough {
        private static var __MouseCursor:Object = null;
        private static function get MouseCursor () :Object {
            if (__MouseCursor == null) {
                __MouseCursor = getDefinitionByName('flash.ui.MouseCursor');
            }
            return __MouseCursor;
        }

        private static var __builtinCursors:Object = null;
        static function get builtinCursors () :Object {
            if (__builtinCursors == null) {
                var cursors:Object = {};
                if ($swf10) {
                    cursors[MouseCursor.ARROW] = true;
                    cursors[MouseCursor.AUTO] = true;
                    cursors[MouseCursor.BUTTON] = true;
                    cursors[MouseCursor.HAND] = true;
                    cursors[MouseCursor.IBEAM] = true;
                }
                __builtinCursors = cursors;
            }
            return __builtinCursors;
        }

        static function get hasGlobalCursor () :Boolean {
            var gcursor:String = globalCursorResource;
            if ($swf10) {
                return ! (gcursor == null || (gcursor == MouseCursor.AUTO && useBuiltinCursor));
            } else {
                return ! (gcursor == null);
            }
        }
    }#

    /**
    * Sets the cursor to a resource
    * @param String what: The resource to use as the cursor.
    */
    static function setCursorGlobal (what:String) :void {
        globalCursorResource = what;
        setCursorLocal(what);
    }

    static function setCursorLocal (what:String) :void {
        if ( __amLocked ) { return; }
        if (what == null) {
            // null is invalid, maybe call restoreCursor()?
            return;
        } else if (lastCursorResource != what) {
            var resourceSprite:DisplayObject = getCursorResource(what);
            if (resourceSprite != null) {
                if (cursorSprite.numChildren > 0) {
                    cursorSprite.removeChildAt(0);
                }
                cursorSprite.addChild( resourceSprite );
                useBuiltinCursor = false;
            } else if (builtinCursors[what] != null) {
                useBuiltinCursor = true;
            } else {
                // invalid cursor?
                return;
            }
            lastCursorResource = what;
        }
        if (useBuiltinCursor) {
            if ($swf10) { Mouse['cursor'] = what; }
            cursorSprite.stopDrag();
            cursorSprite.visible = false;
            LFCApplication.stage.removeEventListener(Event.MOUSE_LEAVE, mouseLeaveHandler);
            LFCApplication.stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
            Mouse.show();
        } else {
            // you can only hide the Mouse when Mouse.cursor is AUTO
            if ($swf10) { Mouse['cursor'] = MouseCursor.AUTO; }
            Mouse.hide();
            cursorSprite.x = LFCApplication.stage.mouseX + cursorOffsetX;
            cursorSprite.y = LFCApplication.stage.mouseY + cursorOffsetY;
            LFCApplication.setChildIndex(cursorSprite, LFCApplication._sprite.numChildren-1);
            // respond to mouse move events
            cursorSprite.startDrag();
            LFCApplication.stage.addEventListener(Event.MOUSE_LEAVE, mouseLeaveHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
            cursorSprite.visible = true;
        }
    }

    /**
     * Triggered when cursor leaves screen or context-menu is opened, in both
     * cases we must display the system cursor
     */
    private static function mouseLeaveHandler (event:Event) :void {
        cursorSprite.visible = false;
        Mouse.show();
        // use capture-phase because most sprites call stopPropagation() for mouse-events
        LFCApplication.stage.addEventListener(MouseEvent.MOUSE_OVER, mouseEnterHandler, true);
    }

    /**
     * Triggered when cursor enters screen or context-menu is closed
     */
    private static function mouseEnterHandler (event:Event) :void {
        cursorSprite.visible = true;
        Mouse.hide();
        LFCApplication.stage.removeEventListener(MouseEvent.MOUSE_OVER, mouseEnterHandler, true);
    }

    /**
     * Hide custom cursor over selectable TextFields otherwise both the ibeam-cursor
     * and the custom cursor are displayed
     */
    private static function mouseMoveHandler (event:MouseEvent) :void {
        var target:Object = event.target;
        if (target is TextField && target.selectable) {
            cursorSprite.visible = false;
            Mouse.show();
        } else {
            Mouse.hide();
            cursorSprite.visible = true;
        }
    }

    private static function getCursorResource (resource:String) :DisplayObject {
        var resinfo:Object = LzResourceLibrary[resource];
        var assetclass:Class;
        if (resinfo == null) {
            return null;
        }
        // single frame resources get an entry in LzResourceLibrary which has
        // 'assetclass' pointing to the resource Class object.
        if (resinfo.assetclass is Class) {
            assetclass = resinfo.assetclass;
        } else {
            // Multiframe resources have an array of Class objects in frames[]
            var frames:Array = resinfo.frames;
            assetclass = frames[0];
        }

        var asset:DisplayObject = new assetclass();
        asset.scaleX = 1.0;
        asset.scaleY = 1.0;

        if (resinfo['offsetx'] != null) { cursorOffsetX = resinfo['offsetx']; }
        if (resinfo['offsety'] != null) { cursorOffsetY = resinfo['offsety']; }

        return asset;
    }

    /**
    * This function restores the default cursor if there is no locked cursor on
    * the screen.
    *
    * @access private
    */
    static function restoreCursor () :void {
        if ( __amLocked ) { return; }
        cursorSprite.stopDrag();
        cursorSprite.visible = false;
        LFCApplication.stage.removeEventListener(Event.MOUSE_LEAVE, mouseLeaveHandler);
        LFCApplication.stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
        globalCursorResource = null;
        if ($swf10) { Mouse['cursor'] = MouseCursor.AUTO; }
        Mouse.show();
    }

    /** Called by LzSprite to restore cursor to global value.
     */
    static function restoreCursorLocal () :void {
        if ( __amLocked ) { return; }
        if (globalCursorResource == null) {
            // Restore to system default pointer
            restoreCursor();
        } else {
            // Restore to the last value set by setCursorGlobal
            setCursorLocal(globalCursorResource);
        }
    }

    /**
    * Prevents the cursor from being changed until unlock is called.
    *
    */
    static function lock () :void {
        __amLocked = true;
    }

    /**
    * Restores the default cursor.
    *
    */
    static function unlock () :void {
        __amLocked = false;
        restoreCursor();
    }

    static function initCursor () :void {
        cursorSprite = new Sprite();
        cursorSprite.mouseChildren = false;
        cursorSprite.mouseEnabled = false;
        cursorSprite.visible = false;
        // Add the cursor DisplayObject to the root sprite
        LFCApplication.addChild(cursorSprite);
        cursorSprite.x = -10000;
        cursorSprite.y = -10000;
    }
}

/**
  * LzMouseKernel.as
  *
  * @copyright Copyright 2001-2010 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @topic Kernel
  * @subtopic swf9
  */

// Receives mouse events from the runtime
class LzMouseKernel  {
    #passthrough (toplevel:true) {
    import flash.display.Sprite;
    import flash.display.DisplayObject;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.text.TextField;
    import flash.ui.*;
    import flash.utils.getDefinitionByName;
    }#

    //////////////////
    // MOUSE EVENTS //
    //////////////////

    // sends mouse events to the callback
    static function handleMouseEvent (view:*, eventname:String) :void {
        if (__callback) __scope[__callback](eventname, view);
        //Debug.write('LzMouseKernel event', eventname);
    }

    static var __callback:String = null;
    static var __scope:* = null;
    static var __lastMouseDown:LzSprite = null;
    static var __mouseLeft:Boolean = false;
    static var __listeneradded:Boolean = false;

    /**
     * Shows or hides the hand cursor for all clickable views.
     */
    static var showhandcursor:Boolean = true;

    static function setCallback (scope:*, funcname:String) :void {
        __scope = scope;
        __callback = funcname;
        if (__listeneradded == false) {
            /* TODO [hqm 2008-01] Do we want to do anything with other
            * events, like click?
            stage.addEventListener(MouseEvent.CLICK, reportClick);
            */

            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_MOVE, __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_UP,   __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_DOWN, __mouseHandler);
            // handled by lz.embed.mousewheel for Windows LFCApplication.stage.addEventListener(MouseEvent.MOUSE_WHEEL, __mouseWheelHandler);
            LFCApplication.stage.addEventListener(Event.MOUSE_LEAVE, __mouseLeaveHandler);
            __listeneradded = true;
        }
    }

    // Handles global mouse events
    static function __mouseHandler (event:MouseEvent) :void {
        var eventname:String = 'on' + event.type.toLowerCase();
        if (eventname == 'onmouseup' && __lastMouseDown != null) {
            // call mouseup on the sprite that got the last mouse down
            __lastMouseDown.__globalmouseup(event);
            __lastMouseDown = null;
        } else {
            if (__mouseLeft) {
                __mouseLeft = false;
                // Mouse reentered the app.
                if (event.buttonDown) __mouseUpOutsideHandler();

                if (lz.embed.browser.isFirefox && lz.embed.browser.OS == 'mac' && lz.embed.browser.version < 3.5) {
                    // TODO [hqm 2009-04] LPP-7957 -- this works
                    // around Firefox bug; if you are making a text
                    // selection, and then drag the mouse outside of
                    // the app while the left button is down, and then
                    // release it. When you bring the mouse back into
                    // the app, the selection is stuck dragging
                    // because Flash plugin never gets the mouse up.
                    LFCApplication.stage.focus = null;
                }

                // generate a 'onmouseenter' event
                handleMouseEvent(null, 'onmouseenter');
            }
            handleMouseEvent(null, eventname);
        }
    }

    // sends mouseup and calls __globalmouseup when the mouse goes up outside the app - see LPP-7724
    static function __mouseUpOutsideHandler () :void {
        if (__lastMouseDown != null) {
            var ev:MouseEvent = new MouseEvent('mouseup');
            __lastMouseDown.__globalmouseup(ev);
            __lastMouseDown = null;
        }
    }

    // handles MOUSE_LEAVE event
    static function __mouseLeaveHandler (event:Event = null) :void {
        __mouseLeft = true;
        handleMouseEvent(null, 'onmouseleave');
    }

    static function __mouseWheelHandler (event:MouseEvent) :void {
        lz.Keys.__mousewheelEvent(event.delta);
    }

    //////////////////
    // MOUSE CURSOR //
    //////////////////

    /**
    * Shows or hides the hand cursor for all clickable views.
    * @param Boolean show: true shows the hand cursor for buttons, false hides it
    */
    static function showHandCursor (show:Boolean) :void {
        showhandcursor = show;
        LzSprite.rootSprite.setGlobalHandCursor(show);
    }

    static var __amLocked:Boolean = false;
    static var useBuiltinCursor:Boolean = false;
    static var cursorSprite:Sprite = null;
    static var cursorOffsetX:int = 0;
    static var cursorOffsetY:int = 0;
    static var globalCursorResource:String = null;
    static var lastCursorResource:String = null;

    #passthrough {
        private static var __MouseCursor:Object = null;
        private static function get MouseCursor () :Object {
            if (__MouseCursor == null) {
                __MouseCursor = getDefinitionByName('flash.ui.MouseCursor');
            }
            return __MouseCursor;
        }

        private static var __builtinCursors:Object = null;
        static function get builtinCursors () :Object {
            if (__builtinCursors == null) {
                var cursors:Object = {};
                if ($swf10) {
                    cursors[MouseCursor.ARROW] = true;
                    cursors[MouseCursor.AUTO] = true;
                    cursors[MouseCursor.BUTTON] = true;
                    cursors[MouseCursor.HAND] = true;
                    cursors[MouseCursor.IBEAM] = true;
                }
                __builtinCursors = cursors;
            }
            return __builtinCursors;
        }

        static function get hasGlobalCursor () :Boolean {
            var gcursor:String = globalCursorResource;
            if ($swf10) {
                return ! (gcursor == null || (gcursor == MouseCursor.AUTO && useBuiltinCursor));
            } else {
                return ! (gcursor == null);
            }
        }
    }#

    /**
    * Sets the cursor to a resource
    * @param String what: The resource to use as the cursor.
    */
    static function setCursorGlobal (what:String) :void {
        globalCursorResource = what;
        setCursorLocal(what);
    }

    static function setCursorLocal (what:String) :void {
        if ( __amLocked ) { return; }
        if (what == null) {
            // null is invalid, maybe call restoreCursor()?
            return;
        } else if (lastCursorResource != what) {
            var resourceSprite:DisplayObject = getCursorResource(what);
            if (resourceSprite != null) {
                if (cursorSprite.numChildren > 0) {
                    cursorSprite.removeChildAt(0);
                }
                cursorSprite.addChild( resourceSprite );
                useBuiltinCursor = false;
            } else if (builtinCursors[what] != null) {
                useBuiltinCursor = true;
            } else {
                // invalid cursor?
                return;
            }
            lastCursorResource = what;
        }
        if (useBuiltinCursor) {
            if ($swf10) { Mouse['cursor'] = what; }
            cursorSprite.stopDrag();
            cursorSprite.visible = false;
            LFCApplication.stage.removeEventListener(Event.MOUSE_LEAVE, mouseLeaveHandler);
            LFCApplication.stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
            Mouse.show();
        } else {
            // you can only hide the Mouse when Mouse.cursor is AUTO
            if ($swf10) { Mouse['cursor'] = MouseCursor.AUTO; }
            Mouse.hide();
            cursorSprite.x = LFCApplication.stage.mouseX + cursorOffsetX;
            cursorSprite.y = LFCApplication.stage.mouseY + cursorOffsetY;
            LFCApplication.setChildIndex(cursorSprite, LFCApplication._sprite.numChildren-1);
            // respond to mouse move events
            cursorSprite.startDrag();
            LFCApplication.stage.addEventListener(Event.MOUSE_LEAVE, mouseLeaveHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
            cursorSprite.visible = true;
        }
    }

    /**
     * Triggered when cursor leaves screen or context-menu is opened, in both
     * cases we must display the system cursor
     */
    private static function mouseLeaveHandler (event:Event) :void {
        cursorSprite.visible = false;
        Mouse.show();
        // use capture-phase because most sprites call stopPropagation() for mouse-events
        LFCApplication.stage.addEventListener(MouseEvent.MOUSE_OVER, mouseEnterHandler, true);
    }

    /**
     * Triggered when cursor enters screen or context-menu is closed
     */
    private static function mouseEnterHandler (event:Event) :void {
        cursorSprite.visible = true;
        Mouse.hide();
        LFCApplication.stage.removeEventListener(MouseEvent.MOUSE_OVER, mouseEnterHandler, true);
    }

    /**
     * Hide custom cursor over selectable TextFields otherwise both the ibeam-cursor
     * and the custom cursor are displayed
     */
    private static function mouseMoveHandler (event:MouseEvent) :void {
        var target:Object = event.target;
        if (target is TextField && target.selectable) {
            cursorSprite.visible = false;
            Mouse.show();
        } else {
            Mouse.hide();
            cursorSprite.visible = true;
        }
    }

    private static function getCursorResource (resource:String) :DisplayObject {
        var resinfo:Object = LzResourceLibrary[resource];
        var assetclass:Class;
        if (resinfo == null) {
            return null;
        }
        // single frame resources get an entry in LzResourceLibrary which has
        // 'assetclass' pointing to the resource Class object.
        if (resinfo.assetclass is Class) {
            assetclass = resinfo.assetclass;
        } else {
            // Multiframe resources have an array of Class objects in frames[]
            var frames:Array = resinfo.frames;
            assetclass = frames[0];
        }

        var asset:DisplayObject = new assetclass();
        asset.scaleX = 1.0;
        asset.scaleY = 1.0;

        if (resinfo['offsetx'] != null) { cursorOffsetX = resinfo['offsetx']; }
        if (resinfo['offsety'] != null) { cursorOffsetY = resinfo['offsety']; }

        return asset;
    }

    /**
    * This function restores the default cursor if there is no locked cursor on
    * the screen.
    *
    * @access private
    */
    static function restoreCursor () :void {
        if ( __amLocked ) { return; }
        cursorSprite.stopDrag();
        cursorSprite.visible = false;
        LFCApplication.stage.removeEventListener(Event.MOUSE_LEAVE, mouseLeaveHandler);
        LFCApplication.stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
        globalCursorResource = null;
        if ($swf10) { Mouse['cursor'] = MouseCursor.AUTO; }
        Mouse.show();
    }

    /** Called by LzSprite to restore cursor to global value.
     */
    static function restoreCursorLocal () :void {
        if ( __amLocked ) { return; }
        if (globalCursorResource == null) {
            // Restore to system default pointer
            restoreCursor();
        } else {
            // Restore to the last value set by setCursorGlobal
            setCursorLocal(globalCursorResource);
        }
    }

    /**
    * Prevents the cursor from being changed until unlock is called.
    *
    */
    static function lock () :void {
        __amLocked = true;
    }

    /**
    * Restores the default cursor.
    *
    */
    static function unlock () :void {
        __amLocked = false;
        restoreCursor();
    }

    static function initCursor () :void {
        cursorSprite = new Sprite();
        cursorSprite.mouseChildren = false;
        cursorSprite.mouseEnabled = false;
        cursorSprite.visible = false;
        // Add the cursor DisplayObject to the root sprite
        LFCApplication.addChild(cursorSprite);
        cursorSprite.x = -10000;
        cursorSprite.y = -10000;
    }
}

Change 20100222-maxcarlson-o by maxcarlson@bank on 2010-02-22 19:35:00 PST
    in /Users/maxcarlson/openlaszlo/trunk-clean
    for http://svn.openlaszlo.org/openlaszlo/trunk

Summary: Conditionalize textfield focus workaround from LPP-7957

Bugs Fixed: LPP-8783 - Text field loses focus when mousing out of canvas and then mousing back in (SWF9)

Technical Reviewer: ptw
QA Reviewer: andre.bargull@udo.edu

Details: Conditionalize workaround to only happen for FF < 3.5 on OS X.

Tests: See LPP-8783.

git-svn-id: d62bde4b5aa582fdf07c8d403e53e0399a7ed285@15797 fa20e4f9-1d0a-0410-b5f3-dc9c16b8b17c


package battlecode.client.viewer.render {
    import battlecode.client.viewer.render.DrawRobot;
    import battlecode.client.viewer.render.DrawRobot;
    import battlecode.common.GameConstants;
    import battlecode.common.MapLocation;
    import battlecode.common.ResearchType;
    import battlecode.common.RobotType;
    import battlecode.common.Team;
    import battlecode.serial.RoundDelta;
    import battlecode.serial.RoundStats;
    import battlecode.world.GameMap;
    import battlecode.world.signals.*;

    public class DrawState extends DefaultSignalHandler {
        // state
        private var mines:Array; // Team[][]
        private var neutralEncampments:Object;
        private var encampments:Object;
        private var groundRobots:Object;
        private var hqA:DrawRobot;
        private var hqB:DrawRobot;

        // stats
        private var aPoints:Number;
        private var bPoints:Number;
        private var aFlux:Number;
        private var bFlux:Number;
        private var aGatheredPoints:Number;
        private var bGatheredPoints:Number;
        private var roundNum:uint;
        private var progressA:Array;
        private var progressB:Array;

        // immutables
        private var map:GameMap;
        private var origin:MapLocation;

        public function DrawState(map:GameMap) {
            neutralEncampments = new Object();
            encampments = new Object();
            groundRobots = new Object();

            mines = new Array();
            for (var i:int = 0; i < map.getWidth(); i++) {
                mines[i] = new Array();
                for (var j:int = 0; j < map.getHeight(); j++) {
                    mines[i][j] = null;
                }
            }

            aPoints = 0;
            bPoints = 0;
            aFlux = 0;
            bFlux = 0;
            aGatheredPoints = 0;
            bGatheredPoints = 0;
            roundNum = 1;

            progressA = [ 0.0, 0.0, 0.0, 0.0, 0.0 ];
            progressB = [ 0.0, 0.0, 0.0, 0.0, 0.0 ];

            this.map = map;
            this.origin = map.getOrigin();
        }

        ///////////////////////////////////////////////////////
        ///////////////// PROPERTY GETTERS ////////////////////
        ///////////////////////////////////////////////////////

        public function getMines():Array {
            return mines;
        }

        public function getNeutralEncampments():Object {
            return neutralEncampments;
        }

        public function getEncampments():Object {
            return encampments
        }

        public function getGroundRobots():Object {
            return groundRobots;
        }

        public function getHQ(team:String):DrawRobot {
            return team == Team.A ? hqA : hqB;
        }

        public function getPoints(team:String):uint {
            return (team == Team.A) ? aPoints : bPoints;
        }

        public function getFlux(team:String):uint {
            return (team == Team.A) ? aFlux : bFlux;
        }

        public function getResearchProgress(team:String):Array {
            return team == Team.A ? progressA : progressB;
        }

        ///////////////////////////////////////////////////////
        /////////////////// CORE FUNCTIONS ////////////////////
        ///////////////////////////////////////////////////////

        private function copyStateFrom(state:DrawState):void {
            var a:*;

            mines = new Array();
            for (var i:int = 0; i < map.getWidth(); i++) {
                mines[i] = new Array();
                for (var j:int = 0; j < map.getHeight(); j++) {
                    mines[i][j] = state.mines[i][j];
                }
            }

            neutralEncampments = new Object();
            for (a in state.neutralEncampments) {
                neutralEncampments[a] = state.neutralEncampments[a].clone();
            }

            encampments = new Object();
            for (a in state.encampments) {
                encampments[a] = state.encampments[a].clone();
            }

            groundRobots = new Object();
            for (a in state.groundRobots) {
                groundRobots[a] = state.groundRobots[a].clone();
            }

            hqA = state.hqA ? state.hqA.clone() as DrawRobot : null;
            hqB = state.hqB ? state.hqB.clone() as DrawRobot : null;

            progressA = state.progressA.concat();
            progressB = state.progressB.concat();

            roundNum = state.roundNum;
        }

        public function clone():DrawState {
            var state:DrawState = new DrawState(map);
            state.copyStateFrom(this);
            return state;
        }

        public function applyDelta(delta:RoundDelta):void {
            updateRound();
            for each (var signal:Signal in delta.getSignals()) {
                applySignal(signal);
            }
            processEndOfRound();
        }

        public function applySignal(signal:Signal):void {
            if (signal != null) signal.accept(this);
        }

        public function applyStats(stats:RoundStats):void {
            aPoints = stats.getPoints(Team.A);
            bPoints = stats.getPoints(Team.B);
            aGatheredPoints = stats.getGatheredPoints(Team.A);
            bGatheredPoints = stats.getGatheredPoints(Team.B);
        }

        public function updateRound():void {
            var a:*, i:uint, j:uint;
            var o:DrawRobot;

            for (a in groundRobots) {
                o = groundRobots[a] as DrawRobot;
                o.updateRound();
                if (!o.isAlive()) {
                    if (o.parent) {
                        o.parent.removeChild(o);
                    }
                    delete groundRobots[a];
                }
            }

            for (a in encampments) {
                o = encampments[a] as DrawRobot;
                o.updateRound();
                if (!o.isAlive()) {
                    if (o.parent) {
                        o.parent.removeChild(o);
                    }
                    delete encampments[a];
                }
            }

            if (hqA) {
                hqA.updateRound();
                if (!hqA.isAlive()) {
                    if (hqA.parent) {
                        hqA.parent.removeChild(hqA);
                    }
                    hqA = null;
                }
            }

            if (hqB) {
                hqB.updateRound();
                if (!hqB.isAlive()) {
                    if (hqB.parent) {
                        hqB.parent.removeChild(hqB);
                    }
                    hqB = null;
                }
            }
        }

        private function processEndOfRound():void {
            roundNum++;
        }

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

        private function getRobot(id:uint):DrawRobot {
            if (groundRobots[id]) return groundRobots[id] as DrawRobot;
            if (encampments[id]) return encampments[id] as DrawRobot;
            return null;
        }

        private function removeRobot(id:uint):void {
            if (groundRobots[id]) delete groundRobots[id];
            if (encampments[id]) delete encampments[id];
        }

        private function translateCoordinates(loc:MapLocation):MapLocation {
            return new MapLocation(loc.getX() - origin.getX(), loc.getY() - origin.getY());
        }

        ///////////////////////////////////////////////////////
        /////////////////// SIGNAL HANDLERS ///////////////////
        ///////////////////////////////////////////////////////

        override public function visitBroadcastSignal(s:BroadcastSignal):* {
            getRobot(s.getRobotID()).broadcast();
        }

        override public function visitCaptureSignal(s:CaptureSignal):* {
            var o:DrawRobot = neutralEncampments[s.getLocation()];
            if (o) {
                if (o.parent) {
                    o.parent.removeChild(o);
                }
                delete neutralEncampments[s.getLocation()]; // TODO dont delete just hide
            }

            var robot:DrawRobot = getRobot(s.getParentID());
            robot.capture();
        }

        override public function visitDeathSignal(s:DeathSignal):* {
            var robot:DrawRobot = getRobot(s.getRobotID());
            robot.destroyUnit();

            if (robot.getType() == RobotType.HQ) {
                var hq:DrawRobot = robot.getTeam() == Team.A ? hqA : hqB;
                hq.destroyUnit();
            }
        }

        override public function visitEnergonChangeSignal(s:EnergonChangeSignal):* {
            var robotIDs:Array = s.getRobotIDs();
            var energon:Array = s.getEnergon();

            for (var i:uint; i < robotIDs.length; i++) {
                var robot:DrawRobot = getRobot(robotIDs[i]);
                robot.setEnergon(energon[i]);

                if (robot.getType() == RobotType.HQ) {
                    var hq:DrawRobot = robot.getTeam() == Team.A ? hqA : hqB;
                    hq.setEnergon(energon[i]);
                }
            }
        }

        override public function visitFluxChangeSignal(s:FluxChangeSignal):* {
            aFlux = s.getFlux(Team.A);
            bFlux = s.getFlux(Team.B);
        }

        override public function visitIndicatorStringSignal(s:IndicatorStringSignal):* {
            getRobot(s.getRobotID()).setIndicatorString(s.getIndicatorString(), s.getIndex());
        }

        override public function visitMineSignal(s:MineSignal):* {
            var loc:MapLocation = translateCoordinates(s.getLocation());
            if (s.isBirth()) {
                mines[loc.getX()][loc.getY()] = s.getTeam();
            } else {
                mines[loc.getX()][loc.getY()] = null;
            }
        }

        override public function visitMineLayerSignal(s:MineLayerSignal):* {
            var robot:DrawRobot = getRobot(s.getRobotID());
            if (s.isLaying()) {
                robot.layMine();
            } else {
                // TODO mining stopping - not enough info on the signal?
                var researchProgress:Array = robot.getTeam() == Team.A ? progressA : progressB;
                var hasUpgrade:Boolean = researchProgress[ResearchType.getField(ResearchType.PIXAXE)] == 1.0;
                robot.diffuseMine(hasUpgrade);
            }
        }

        override public function visitMovementSignal(s:MovementSignal):* {
            getRobot(s.getRobotID()).moveToLocation(s.getTargetLoc());
        }

        override public function visitNodeBirthSignal(s:NodeBirthSignal):* {
            var encampment:DrawRobot = new DrawRobot(0, RobotType.ENCAMPMENT, Team.NEUTRAL);
            encampment.setLocation(s.getLocation());
            neutralEncampments[s.getLocation()] = encampment;
        }

        override public function visitResearchChangeSignal(s:ResearchChangeSignal):* {
            progressA = s.getProgress(Team.A);
            progressB = s.getProgress(Team.B);
        }

        override public function visitSpawnSignal(s:SpawnSignal):* {
            var robot:DrawRobot = new DrawRobot(s.getRobotID(), s.getRobotType(), s.getTeam());
            robot.setLocation(s.getLocation());
            groundRobots[s.getRobotID()] = robot;
            if (s.getRobotType() == RobotType.HQ) {
                if (s.getTeam() == Team.A) hqA = robot.clone() as DrawRobot;
                if (s.getTeam() == Team.B) hqB = robot.clone() as DrawRobot;
            }
        }

    }

}

package battlecode.client.viewer.render {
    import battlecode.common.MapLocation;
    import battlecode.common.ResearchType;
    import battlecode.common.RobotType;
    import battlecode.common.Team;
    import battlecode.serial.RoundDelta;
    import battlecode.serial.RoundStats;
    import battlecode.world.GameMap;
    import battlecode.world.signals.*;

    public class DrawState extends DefaultSignalHandler {
        // state
        private var mines:Array; // Team[][]
        private var neutralEncampments:Object;
        private var encampments:Object;
        private var groundRobots:Object;
        private var hqA:DrawRobot;
        private var hqB:DrawRobot;

        // stats
        private var aPoints:Number;
        private var bPoints:Number;
        private var aFlux:Number;
        private var bFlux:Number;
        private var aGatheredPoints:Number;
        private var bGatheredPoints:Number;
        private var roundNum:uint;
        private var progressA:Array;
        private var progressB:Array;

        // immutables
        private var map:GameMap;
        private var origin:MapLocation;

        public function DrawState(map:GameMap) {
            neutralEncampments = new Object();
            encampments = new Object();
            groundRobots = new Object();

            mines = new Array();
            for (var i:int = 0; i < map.getWidth(); i++) {
                mines[i] = new Array();
                for (var j:int = 0; j < map.getHeight(); j++) {
                    mines[i][j] = null;
                }
            }

            aPoints = 0;
            bPoints = 0;
            aFlux = 0;
            bFlux = 0;
            aGatheredPoints = 0;
            bGatheredPoints = 0;
            roundNum = 1;

            progressA = [ 0.0, 0.0, 0.0, 0.0, 0.0 ];
            progressB = [ 0.0, 0.0, 0.0, 0.0, 0.0 ];

            this.map = map;
            this.origin = map.getOrigin();
        }

        ///////////////////////////////////////////////////////
        ///////////////// PROPERTY GETTERS ////////////////////
        ///////////////////////////////////////////////////////

        public function getMines():Array {
            return mines;
        }

        public function getNeutralEncampments():Object {
            return neutralEncampments;
        }

        public function getEncampments():Object {
            return encampments
        }

        public function getGroundRobots():Object {
            return groundRobots;
        }

        public function getHQ(team:String):DrawRobot {
            return team == Team.A ? hqA : hqB;
        }

        public function getPoints(team:String):uint {
            return (team == Team.A) ? aPoints : bPoints;
        }

        public function getFlux(team:String):uint {
            return (team == Team.A) ? aFlux : bFlux;
        }

        public function getResearchProgress(team:String):Array {
            return team == Team.A ? progressA : progressB;
        }

        ///////////////////////////////////////////////////////
        /////////////////// CORE FUNCTIONS ////////////////////
        ///////////////////////////////////////////////////////

        private function copyStateFrom(state:DrawState):void {
            var a:*;

            mines = new Array();
            for (var i:int = 0; i < map.getWidth(); i++) {
                mines[i] = new Array();
                for (var j:int = 0; j < map.getHeight(); j++) {
                    mines[i][j] = state.mines[i][j];
                }
            }

            neutralEncampments = new Object();
            for (a in state.neutralEncampments) {
                neutralEncampments[a] = state.neutralEncampments[a].clone();
            }

            encampments = new Object();
            for (a in state.encampments) {
                encampments[a] = state.encampments[a].clone();
            }

            groundRobots = new Object();
            for (a in state.groundRobots) {
                groundRobots[a] = state.groundRobots[a].clone();
            }

            hqA = state.hqA ? state.hqA.clone() as DrawRobot : null;
            hqB = state.hqB ? state.hqB.clone() as DrawRobot : null;

            progressA = state.progressA.concat();
            progressB = state.progressB.concat();

            roundNum = state.roundNum;
        }

        public function clone():DrawState {
            var state:DrawState = new DrawState(map);
            state.copyStateFrom(this);
            return state;
        }

        public function applyDelta(delta:RoundDelta):void {
            updateRound();
            for each (var signal:Signal in delta.getSignals()) {
                applySignal(signal);
            }
            processEndOfRound();
        }

        public function applySignal(signal:Signal):void {
            if (signal != null) signal.accept(this);
        }

        public function applyStats(stats:RoundStats):void {
            aPoints = stats.getPoints(Team.A);
            bPoints = stats.getPoints(Team.B);
            aGatheredPoints = stats.getGatheredPoints(Team.A);
            bGatheredPoints = stats.getGatheredPoints(Team.B);
        }

        public function updateRound():void {
            var a:*, i:uint, j:uint;
            var o:DrawRobot;

            for (a in groundRobots) {
                o = groundRobots[a] as DrawRobot;
                o.updateRound();
                if (!o.isAlive()) {
                    if (o.parent) {
                        o.parent.removeChild(o);
                    }
                    delete groundRobots[a];
                }
            }

            for (a in encampments) {
                o = encampments[a] as DrawRobot;
                o.updateRound();
                if (!o.isAlive()) {
                    if (o.parent) {
                        o.parent.removeChild(o);
                    }
                    delete encampments[a];
                }
            }

            if (hqA) {
                hqA.updateRound();
                if (!hqA.isAlive()) {
                    if (hqA.parent) {
                        hqA.parent.removeChild(hqA);
                    }
                    hqA = null;
                }
            }

            if (hqB) {
                hqB.updateRound();
                if (!hqB.isAlive()) {
                    if (hqB.parent) {
                        hqB.parent.removeChild(hqB);
                    }
                    hqB = null;
                }
            }
        }

        private function processEndOfRound():void {
            roundNum++;
        }

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

        private function getRobot(id:uint):DrawRobot {
            if (groundRobots[id]) return groundRobots[id] as DrawRobot;
            if (encampments[id]) return encampments[id] as DrawRobot;
            return null;
        }

        private function removeRobot(id:uint):void {
            if (groundRobots[id]) delete groundRobots[id];
            if (encampments[id]) delete encampments[id];
        }

        private function translateCoordinates(loc:MapLocation):MapLocation {
            return new MapLocation(loc.getX() - origin.getX(), loc.getY() - origin.getY());
        }

        ///////////////////////////////////////////////////////
        /////////////////// SIGNAL HANDLERS ///////////////////
        ///////////////////////////////////////////////////////

        override public function visitBroadcastSignal(s:BroadcastSignal):* {
            getRobot(s.getRobotID()).broadcast();
        }

        override public function visitCaptureSignal(s:CaptureSignal):* {
            var o:DrawRobot = neutralEncampments[s.getLocation()];
            if (o) {
                if (o.parent) {
                    o.parent.removeChild(o);
                }
                delete neutralEncampments[s.getLocation()];
            }

            var robot:DrawRobot = getRobot(s.getParentID());
            robot.capture();
        }

        override public function visitDeathSignal(s:DeathSignal):* {
            var robot:DrawRobot = getRobot(s.getRobotID());
            robot.destroyUnit();

            if (robot.getType() == RobotType.HQ) {
                var hq:DrawRobot = robot.getTeam() == Team.A ? hqA : hqB;
                hq.destroyUnit();
            }

            if (RobotType.isEncampment(robot.getType())) {
                var encampment:DrawRobot = new DrawRobot(0, RobotType.ENCAMPMENT, Team.NEUTRAL);
                encampment.setLocation(robot.getLocation());
                neutralEncampments[robot.getLocation()] = encampment;
            }
        }

        override public function visitEnergonChangeSignal(s:EnergonChangeSignal):* {
            var robotIDs:Array = s.getRobotIDs();
            var energon:Array = s.getEnergon();

            for (var i:uint; i < robotIDs.length; i++) {
                var robot:DrawRobot = getRobot(robotIDs[i]);
                robot.setEnergon(energon[i]);

                if (robot.getType() == RobotType.HQ) {
                    var hq:DrawRobot = robot.getTeam() == Team.A ? hqA : hqB;
                    hq.setEnergon(energon[i]);
                }
            }
        }

        override public function visitFluxChangeSignal(s:FluxChangeSignal):* {
            aFlux = s.getFlux(Team.A);
            bFlux = s.getFlux(Team.B);
        }

        override public function visitIndicatorStringSignal(s:IndicatorStringSignal):* {
            getRobot(s.getRobotID()).setIndicatorString(s.getIndicatorString(), s.getIndex());
        }

        override public function visitMineSignal(s:MineSignal):* {
            var loc:MapLocation = translateCoordinates(s.getLocation());
            if (s.isBirth()) {
                mines[loc.getX()][loc.getY()] = s.getTeam();
            } else {
                mines[loc.getX()][loc.getY()] = null;
            }
        }

        override public function visitMineLayerSignal(s:MineLayerSignal):* {
            var robot:DrawRobot = getRobot(s.getRobotID());
            if (s.isLaying()) {
                robot.layMine();
            } else {
                // TODO mining stopping - not enough info on the signal?
                var researchProgress:Array = robot.getTeam() == Team.A ? progressA : progressB;
                var hasUpgrade:Boolean = researchProgress[ResearchType.getField(ResearchType.PIXAXE)] == 1.0;
                robot.diffuseMine(hasUpgrade);
            }
        }

        override public function visitMovementSignal(s:MovementSignal):* {
            getRobot(s.getRobotID()).moveToLocation(s.getTargetLoc());
        }

        override public function visitNodeBirthSignal(s:NodeBirthSignal):* {
            var encampment:DrawRobot = new DrawRobot(0, RobotType.ENCAMPMENT, Team.NEUTRAL);
            encampment.setLocation(s.getLocation());
            neutralEncampments[s.getLocation()] = encampment;
        }

        override public function visitResearchChangeSignal(s:ResearchChangeSignal):* {
            progressA = s.getProgress(Team.A);
            progressB = s.getProgress(Team.B);
        }

        override public function visitSpawnSignal(s:SpawnSignal):* {
            var robot:DrawRobot = new DrawRobot(s.getRobotID(), s.getRobotType(), s.getTeam());
            robot.setLocation(s.getLocation());
            groundRobots[s.getRobotID()] = robot;
            if (s.getRobotType() == RobotType.HQ) {
                if (s.getTeam() == Team.A) hqA = robot.clone() as DrawRobot;
                if (s.getTeam() == Team.B) hqB = robot.clone() as DrawRobot;
            }
        }

    }

}

replace neutral encampments after captured encampment has been destroyed


package battlecode.client.viewer.render {
    import battlecode.common.MapLocation;
    import battlecode.common.ResearchType;
    import battlecode.common.RobotType;
    import battlecode.common.Team;
    import battlecode.serial.RoundDelta;
    import battlecode.serial.RoundStats;
    import battlecode.world.GameMap;
    import battlecode.world.signals.*;

    public class DrawState extends DefaultSignalHandler {
        // state
        private var mines:Array; // Team[][]
        private var neutralEncampments:Object;
        private var encampments:Object;
        private var groundRobots:Object;
        private var hqA:DrawRobot;
        private var hqB:DrawRobot;

        // stats
        private var aPoints:Number;
        private var bPoints:Number;
        private var aFlux:Number;
        private var bFlux:Number;
        private var aGatheredPoints:Number;
        private var bGatheredPoints:Number;
        private var roundNum:uint;
        private var progressA:Array;
        private var progressB:Array;

        // immutables
        private var map:GameMap;
        private var origin:MapLocation;

        public function DrawState(map:GameMap) {
            neutralEncampments = new Object();
            encampments = new Object();
            groundRobots = new Object();

            mines = new Array();
            for (var i:int = 0; i < map.getWidth(); i++) {
                mines[i] = new Array();
                for (var j:int = 0; j < map.getHeight(); j++) {
                    mines[i][j] = null;
                }
            }

            aPoints = 0;
            bPoints = 0;
            aFlux = 0;
            bFlux = 0;
            aGatheredPoints = 0;
            bGatheredPoints = 0;
            roundNum = 1;

            progressA = [ 0.0, 0.0, 0.0, 0.0, 0.0 ];
            progressB = [ 0.0, 0.0, 0.0, 0.0, 0.0 ];

            this.map = map;
            this.origin = map.getOrigin();
        }

        ///////////////////////////////////////////////////////
        ///////////////// PROPERTY GETTERS ////////////////////
        ///////////////////////////////////////////////////////

        public function getMines():Array {
            return mines;
        }

        public function getNeutralEncampments():Object {
            return neutralEncampments;
        }

        public function getEncampments():Object {
            return encampments
        }

        public function getGroundRobots():Object {
            return groundRobots;
        }

        public function getHQ(team:String):DrawRobot {
            return team == Team.A ? hqA : hqB;
        }

        public function getPoints(team:String):uint {
            return (team == Team.A) ? aPoints : bPoints;
        }

        public function getFlux(team:String):uint {
            return (team == Team.A) ? aFlux : bFlux;
        }

        public function getResearchProgress(team:String):Array {
            return team == Team.A ? progressA : progressB;
        }

        ///////////////////////////////////////////////////////
        /////////////////// CORE FUNCTIONS ////////////////////
        ///////////////////////////////////////////////////////

        private function copyStateFrom(state:DrawState):void {
            var a:*;

            mines = new Array();
            for (var i:int = 0; i < map.getWidth(); i++) {
                mines[i] = new Array();
                for (var j:int = 0; j < map.getHeight(); j++) {
                    mines[i][j] = state.mines[i][j];
                }
            }

            neutralEncampments = new Object();
            for (a in state.neutralEncampments) {
                neutralEncampments[a] = state.neutralEncampments[a].clone();
            }

            encampments = new Object();
            for (a in state.encampments) {
                encampments[a] = state.encampments[a].clone();
            }

            groundRobots = new Object();
            for (a in state.groundRobots) {
                groundRobots[a] = state.groundRobots[a].clone();
            }

            hqA = state.hqA ? state.hqA.clone() as DrawRobot : null;
            hqB = state.hqB ? state.hqB.clone() as DrawRobot : null;

            progressA = state.progressA.concat();
            progressB = state.progressB.concat();

            roundNum = state.roundNum;
        }

        public function clone():DrawState {
            var state:DrawState = new DrawState(map);
            state.copyStateFrom(this);
            return state;
        }

        public function applyDelta(delta:RoundDelta):void {
            updateRound();
            for each (var signal:Signal in delta.getSignals()) {
                applySignal(signal);
            }
            processEndOfRound();
        }

        public function applySignal(signal:Signal):void {
            if (signal != null) signal.accept(this);
        }

        public function applyStats(stats:RoundStats):void {
            aPoints = stats.getPoints(Team.A);
            bPoints = stats.getPoints(Team.B);
            aGatheredPoints = stats.getGatheredPoints(Team.A);
            bGatheredPoints = stats.getGatheredPoints(Team.B);
        }

        public function updateRound():void {
            var a:*, i:uint, j:uint;
            var o:DrawRobot;

            for (a in groundRobots) {
                o = groundRobots[a] as DrawRobot;
                o.updateRound();
                if (!o.isAlive()) {
                    if (o.parent) {
                        o.parent.removeChild(o);
                    }
                    delete groundRobots[a];
                }
            }

            for (a in encampments) {
                o = encampments[a] as DrawRobot;
                o.updateRound();
                if (!o.isAlive()) {
                    if (o.parent) {
                        o.parent.removeChild(o);
                    }
                    delete encampments[a];
                }
            }

            if (hqA) {
                hqA.updateRound();
                if (!hqA.isAlive()) {
                    if (hqA.parent) {
                        hqA.parent.removeChild(hqA);
                    }
                    hqA = null;
                }
            }

            if (hqB) {
                hqB.updateRound();
                if (!hqB.isAlive()) {
                    if (hqB.parent) {
                        hqB.parent.removeChild(hqB);
                    }
                    hqB = null;
                }
            }
        }

        private function processEndOfRound():void {
            roundNum++;
        }

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

        private function getRobot(id:uint):DrawRobot {
            if (groundRobots[id]) return groundRobots[id] as DrawRobot;
            if (encampments[id]) return encampments[id] as DrawRobot;
            return null;
        }

        private function removeRobot(id:uint):void {
            if (groundRobots[id]) delete groundRobots[id];
            if (encampments[id]) delete encampments[id];
        }

        private function translateCoordinates(loc:MapLocation):MapLocation {
            return new MapLocation(loc.getX() - origin.getX(), loc.getY() - origin.getY());
        }

        ///////////////////////////////////////////////////////
        /////////////////// SIGNAL HANDLERS ///////////////////
        ///////////////////////////////////////////////////////

        override public function visitAttackSignal(s:AttackSignal):* {
            getRobot(s.getRobotID()).attack(s.getTargetLoc());
        }

        override public function visitBroadcastSignal(s:BroadcastSignal):* {
            getRobot(s.getRobotID()).broadcast();
        }

        override public function visitCaptureSignal(s:CaptureSignal):* {
            var robot:DrawRobot = getRobot(s.getParentID());
            robot.capture();
        }

        override public function visitDeathSignal(s:DeathSignal):* {
            var robot:DrawRobot = getRobot(s.getRobotID());
            robot.destroyUnit();

            if (robot.getType() == RobotType.HQ) {
                var hq:DrawRobot = robot.getTeam() == Team.A ? hqA : hqB;
                hq.destroyUnit();
            }

            if (RobotType.isEncampment(robot.getType())) {
                var encampment:DrawRobot = new DrawRobot(0, RobotType.ENCAMPMENT, Team.NEUTRAL);
                encampment.setLocation(robot.getLocation());
                neutralEncampments[robot.getLocation()] = encampment;
            }
        }

        override public function visitEnergonChangeSignal(s:EnergonChangeSignal):* {
            var robotIDs:Array = s.getRobotIDs();
            var energon:Array = s.getEnergon();

            for (var i:uint; i < robotIDs.length; i++) {
                var robot:DrawRobot = getRobot(robotIDs[i]);
                robot.setEnergon(energon[i]);

                if (robot.getType() == RobotType.HQ) {
                    var hq:DrawRobot = robot.getTeam() == Team.A ? hqA : hqB;
                    hq.setEnergon(energon[i]);
                }
            }
        }

        override public function visitFluxChangeSignal(s:FluxChangeSignal):* {
            aFlux = s.getFlux(Team.A);
            bFlux = s.getFlux(Team.B);
        }

        override public function visitIndicatorStringSignal(s:IndicatorStringSignal):* {
            getRobot(s.getRobotID()).setIndicatorString(s.getIndicatorString(), s.getIndex());
        }

        override public function visitMineSignal(s:MineSignal):* {
            var loc:MapLocation = translateCoordinates(s.getLocation());
            if (map.isOnMap(loc)) {
                if (s.isBirth()) {
                    if (!mines[loc.getX()][loc.getY()]) {
                        mines[loc.getX()][loc.getY()] = s.getTeam();
                    }
                } else {
                    mines[loc.getX()][loc.getY()] = null;
                }
            }
        }

        override public function visitMineLayerSignal(s:MineLayerSignal):* {
            var robot:DrawRobot = getRobot(s.getRobotID());
            if (s.isLaying()) {
                robot.layMine();
            } else {
                var researchProgress:Array = robot.getTeam() == Team.A ? progressA : progressB;
                var hasUpgrade:Boolean = researchProgress[ResearchType.getField(ResearchType.DEFUSION)] == 1.0;
                robot.diffuseMine(hasUpgrade);
            }
        }

        override public function visitMovementSignal(s:MovementSignal):* {
            getRobot(s.getRobotID()).moveToLocation(s.getTargetLoc());
        }

        override public function visitNodeBirthSignal(s:NodeBirthSignal):* {
            var encampment:DrawRobot = new DrawRobot(0, RobotType.ENCAMPMENT, Team.NEUTRAL);
            encampment.setLocation(s.getLocation());
            neutralEncampments[s.getLocation()] = encampment;
        }

        override public function visitResearchChangeSignal(s:ResearchChangeSignal):* {
            progressA = s.getProgress(Team.A);
            progressB = s.getProgress(Team.B);
        }

        override public function visitSpawnSignal(s:SpawnSignal):* {
            var robot:DrawRobot = new DrawRobot(s.getRobotID(), s.getRobotType(), s.getTeam());
            robot.setLocation(s.getLocation());
            groundRobots[s.getRobotID()] = robot;

            if (s.getRobotType() == RobotType.HQ) {
                if (s.getTeam() == Team.A) hqA = robot.clone() as DrawRobot;
                if (s.getTeam() == Team.B) hqB = robot.clone() as DrawRobot;
            }

            if (RobotType.isEncampment(s.getRobotType())) {
                var o:DrawRobot = neutralEncampments[s.getLocation()];
                if (o) {
                    if (o.parent) {
                        o.parent.removeChild(o);
                    }
                    delete neutralEncampments[s.getLocation()];
                }
            }
        }

    }

}

package battlecode.client.viewer.render {
    import battlecode.common.MapLocation;
    import battlecode.common.ResearchType;
    import battlecode.common.RobotType;
    import battlecode.common.Team;
    import battlecode.serial.RoundDelta;
    import battlecode.serial.RoundStats;
    import battlecode.world.GameMap;
    import battlecode.world.signals.*;

    public class DrawState extends DefaultSignalHandler {
        // state
        private var mines:Array; // Team[][]
        private var neutralEncampments:Object;
        private var encampments:Object;
        private var groundRobots:Object;
        private var hqA:DrawRobot;
        private var hqB:DrawRobot;

        // stats
        private var aPoints:Number;
        private var bPoints:Number;
        private var aFlux:Number;
        private var bFlux:Number;
        private var aGatheredPoints:Number;
        private var bGatheredPoints:Number;
        private var roundNum:uint;
        private var progressA:Array;
        private var progressB:Array;

        // immutables
        private var map:GameMap;
        private var origin:MapLocation;

        public function DrawState(map:GameMap) {
            neutralEncampments = new Object();
            encampments = new Object();
            groundRobots = new Object();

            mines = new Array();
            for (var i:int = 0; i < map.getWidth(); i++) {
                mines[i] = new Array();
                for (var j:int = 0; j < map.getHeight(); j++) {
                    mines[i][j] = null;
                }
            }

            aPoints = 0;
            bPoints = 0;
            aFlux = 0;
            bFlux = 0;
            aGatheredPoints = 0;
            bGatheredPoints = 0;
            roundNum = 1;

            progressA = [ 0.0, 0.0, 0.0, 0.0, 0.0 ];
            progressB = [ 0.0, 0.0, 0.0, 0.0, 0.0 ];

            this.map = map;
            this.origin = map.getOrigin();
        }

        ///////////////////////////////////////////////////////
        ///////////////// PROPERTY GETTERS ////////////////////
        ///////////////////////////////////////////////////////

        public function getMines():Array {
            return mines;
        }

        public function getNeutralEncampments():Object {
            return neutralEncampments;
        }

        public function getEncampments():Object {
            return encampments
        }

        public function getGroundRobots():Object {
            return groundRobots;
        }

        public function getHQ(team:String):DrawRobot {
            return team == Team.A ? hqA : hqB;
        }

        public function getPoints(team:String):uint {
            return (team == Team.A) ? aPoints : bPoints;
        }

        public function getFlux(team:String):uint {
            return (team == Team.A) ? aFlux : bFlux;
        }

        public function getResearchProgress(team:String):Array {
            return team == Team.A ? progressA : progressB;
        }

        ///////////////////////////////////////////////////////
        /////////////////// CORE FUNCTIONS ////////////////////
        ///////////////////////////////////////////////////////

        private function copyStateFrom(state:DrawState):void {
            var a:*;

            mines = new Array();
            for (var i:int = 0; i < map.getWidth(); i++) {
                mines[i] = new Array();
                for (var j:int = 0; j < map.getHeight(); j++) {
                    mines[i][j] = state.mines[i][j];
                }
            }

            neutralEncampments = new Object();
            for (a in state.neutralEncampments) {
                neutralEncampments[a] = state.neutralEncampments[a].clone();
            }

            encampments = new Object();
            for (a in state.encampments) {
                encampments[a] = state.encampments[a].clone();
            }

            groundRobots = new Object();
            for (a in state.groundRobots) {
                groundRobots[a] = state.groundRobots[a].clone();
            }

            hqA = state.hqA ? state.hqA.clone() as DrawRobot : null;
            hqB = state.hqB ? state.hqB.clone() as DrawRobot : null;

            progressA = state.progressA.concat();
            progressB = state.progressB.concat();

            roundNum = state.roundNum;
        }

        public function clone():DrawState {
            var state:DrawState = new DrawState(map);
            state.copyStateFrom(this);
            return state;
        }

        public function applyDelta(delta:RoundDelta):void {
            updateRound();
            for each (var signal:Signal in delta.getSignals()) {
                applySignal(signal);
            }
            processEndOfRound();
        }

        public function applySignal(signal:Signal):void {
            if (signal != null) signal.accept(this);
        }

        public function applyStats(stats:RoundStats):void {
            aPoints = stats.getPoints(Team.A);
            bPoints = stats.getPoints(Team.B);
            aGatheredPoints = stats.getGatheredPoints(Team.A);
            bGatheredPoints = stats.getGatheredPoints(Team.B);
        }

        public function updateRound():void {
            var a:*, i:uint, j:uint;
            var o:DrawRobot;

            for (a in groundRobots) {
                o = groundRobots[a] as DrawRobot;
                o.updateRound();
                if (!o.isAlive()) {
                    if (o.parent) {
                        o.parent.removeChild(o);
                    }
                    delete groundRobots[a];
                }
            }

            for (a in encampments) {
                o = encampments[a] as DrawRobot;
                o.updateRound();
                if (!o.isAlive()) {
                    if (o.parent) {
                        o.parent.removeChild(o);
                    }
                    delete encampments[a];
                }
            }

            if (hqA) {
                hqA.updateRound();
                if (!hqA.isAlive()) {
                    if (hqA.parent) {
                        hqA.parent.removeChild(hqA);
                    }
                    hqA = null;
                }
            }

            if (hqB) {
                hqB.updateRound();
                if (!hqB.isAlive()) {
                    if (hqB.parent) {
                        hqB.parent.removeChild(hqB);
                    }
                    hqB = null;
                }
            }
        }

        private function processEndOfRound():void {
            roundNum++;
        }

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

        private function getRobot(id:uint):DrawRobot {
            if (groundRobots[id]) return groundRobots[id] as DrawRobot;
            if (encampments[id]) return encampments[id] as DrawRobot;
            return null;
        }

        private function removeRobot(id:uint):void {
            if (groundRobots[id]) delete groundRobots[id];
            if (encampments[id]) delete encampments[id];
        }

        private function translateCoordinates(loc:MapLocation):MapLocation {
            return new MapLocation(loc.getX() - origin.getX(), loc.getY() - origin.getY());
        }

        ///////////////////////////////////////////////////////
        /////////////////// SIGNAL HANDLERS ///////////////////
        ///////////////////////////////////////////////////////

        override public function visitAttackSignal(s:AttackSignal):* {
            getRobot(s.getRobotID()).attack(s.getTargetLoc());
        }

        override public function visitBroadcastSignal(s:BroadcastSignal):* {
            getRobot(s.getRobotID()).broadcast();
        }

        override public function visitCaptureSignal(s:CaptureSignal):* {
            var robot:DrawRobot = getRobot(s.getParentID());
            robot.capture();
        }

        override public function visitDeathSignal(s:DeathSignal):* {
            var robot:DrawRobot = getRobot(s.getRobotID());
            robot.destroyUnit();

            if (robot.getType() == RobotType.HQ) {
                var hq:DrawRobot = robot.getTeam() == Team.A ? hqA : hqB;
                hq.destroyUnit();
            }

            if (RobotType.isEncampment(robot.getType())) {
                var encampment:DrawRobot = new DrawRobot(0, RobotType.ENCAMPMENT, Team.NEUTRAL);
                encampment.setLocation(robot.getLocation());
                neutralEncampments[robot.getLocation()] = encampment;
            }
        }

        override public function visitEnergonChangeSignal(s:EnergonChangeSignal):* {
            var robotIDs:Array = s.getRobotIDs();
            var energon:Array = s.getEnergon();

            for (var i:uint; i < robotIDs.length; i++) {
                var robot:DrawRobot = getRobot(robotIDs[i]);
                robot.setEnergon(energon[i]);

                if (robot.getType() == RobotType.HQ) {
                    var hq:DrawRobot = robot.getTeam() == Team.A ? hqA : hqB;
                    hq.setEnergon(energon[i]);
                }
            }
        }

        override public function visitFluxChangeSignal(s:FluxChangeSignal):* {
            aFlux = s.getFlux(Team.A);
            bFlux = s.getFlux(Team.B);
        }

        override public function visitIndicatorStringSignal(s:IndicatorStringSignal):* {
            getRobot(s.getRobotID()).setIndicatorString(s.getIndicatorString(), s.getIndex());
        }

        override public function visitMineSignal(s:MineSignal):* {
            var loc:MapLocation = translateCoordinates(s.getLocation());
            if (map.isOnMap(loc)) {
                if (s.isBirth()) {
                    if (!mines[loc.getX()][loc.getY()]) {
                        mines[loc.getX()][loc.getY()] = s.getTeam();
                    }
                } else {
                    mines[loc.getX()][loc.getY()] = null;
                }
            }
        }

        override public function visitMineLayerSignal(s:MineLayerSignal):* {
            var robot:DrawRobot = getRobot(s.getRobotID());
            if (s.isLaying()) {
                robot.layMine();
            } else {
                var researchProgress:Array = robot.getTeam() == Team.A ? progressA : progressB;
                var hasUpgrade:Boolean = researchProgress[ResearchType.getField(ResearchType.DEFUSION)] == 1.0;
                robot.diffuseMine(hasUpgrade);
            }
        }

        override public function visitMovementSignal(s:MovementSignal):* {
            getRobot(s.getRobotID()).moveToLocation(s.getTargetLoc());
        }

        override public function visitNodeBirthSignal(s:NodeBirthSignal):* {
            var encampment:DrawRobot = new DrawRobot(0, RobotType.ENCAMPMENT, Team.NEUTRAL);
            encampment.setLocation(s.getLocation());
            neutralEncampments[s.getLocation()] = encampment;
        }

        override public function visitResearchChangeSignal(s:ResearchChangeSignal):* {
            progressA = s.getProgress(Team.A);
            progressB = s.getProgress(Team.B);
        }

        override public function visitSpawnSignal(s:SpawnSignal):* {
            var robot:DrawRobot = new DrawRobot(s.getRobotID(), s.getRobotType(), s.getTeam());
            robot.setLocation(s.getLocation());

            if (s.getRobotType() == RobotType.HQ) {
                if (s.getTeam() == Team.A) hqA = robot.clone() as DrawRobot;
                if (s.getTeam() == Team.B) hqB = robot.clone() as DrawRobot;
            }

            if (RobotType.isEncampment(s.getRobotType())) {
                var o:DrawRobot = neutralEncampments[s.getLocation()];
                if (o) {
                    if (o.parent) {
                        o.parent.removeChild(o);
                    }
                    delete neutralEncampments[s.getLocation()];
                }
                encampments[s.getRobotID()] = robot;
            } else {
                groundRobots[s.getRobotID()] = robot;
            }
        }

    }

}

fix height map for encampments

fix height map for encampments


/*
  PureMVC Utility for AS3 / AIR - Desktop Citizen
  Copyright(c) 2007-10 Cliff Hall <clifford.hall@puremvc.org>
  Your reuse is governed by the Creative Commons Attribution 3.0 License
 */
package org.puremvc.as3.utilities.air.desktopcitizen.view
{
	import flash.display.NativeWindowDisplayState;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.NativeWindowBoundsEvent;
	import flash.events.NativeWindowDisplayStateEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.system.Capabilities;

	import org.puremvc.as3.interfaces.*;
	import org.puremvc.as3.patterns.mediator.Mediator;
	import org.puremvc.as3.utilities.air.desktopcitizen.DesktopCitizenConstants;
	import org.puremvc.as3.utilities.air.desktopcitizen.controller.WindowCloseCommand;
	import org.puremvc.as3.utilities.air.desktopcitizen.model.WindowMetricsProxy;

	/**
	 * A Mediator for interacting with the WindowedApplication Stage
	 */
	public class WindowMediator extends Mediator implements IMediator
	{
		// Cannonical name of the Mediator
		public static const NAME:String = DesktopCitizenConstants.UTIL+"WindowMediator";

		// Notification Constants specific to this mediator
		public static const WINDOW_SHOW:String		= NAME+"/WindowShow";
		public static const SET_DEFAULT:String		= NAME+"/SetDefault";
		public static const SET_BOUNDS:String		= NAME+"/SetBounds";
		public static const SET_FULLSCREEN:String	= NAME+"/SetFullScreen";

		// Constant for Minimum stage width
		public static const MIN_WIDTH:Number = 800;
		// Constant for Minimum stage height
		public static const MIN_HEIGHT:Number = 570;

		/**
		 * Constructor.
		 */
		public function WindowMediator( viewComponent:Object )
		{
			// pass the viewComponent to the superclass where
			// it will be stored in the inherited viewComponent property
			super( NAME, viewComponent );
		}

		override public function onRegister():void
		{
			// cache a reference to frequently used proxies
			windowMetricsProxy = facade.retrieveProxy( WindowMetricsProxy.NAME ) as WindowMetricsProxy;

			// Listen for events from the view component
			stage.nativeWindow.addEventListener( Event.CLOSING, onWindowClosing );
			stage.nativeWindow.addEventListener( NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGE, onFullScreen )
			stage.nativeWindow.addEventListener( NativeWindowBoundsEvent.RESIZE, onResize );
			stage.nativeWindow.addEventListener( NativeWindowBoundsEvent.MOVE, onResize );
		}

		/**
		 * List all notifications this Mediator is interested in.
		 *

		 * Automatically called by the framework when the mediator
		 * is registered with the view.

		 *
		 * @return Array the list of Nofitication names
		 */
		override public function listNotificationInterests():Array
		{
			return [ SET_DEFAULT,
					 SET_BOUNDS,
					 SET_FULLSCREEN,
					 WINDOW_SHOW,
					 DesktopCitizenConstants.ENABLE_CONFIRM,
					 DesktopCitizenConstants.CONFIRM_CLOSING
					];
		}

		/**
		 * Handle all notifications this Mediator is interested in.
		 *

		 * Called by the framework when a notification is sent that
		 * this mediator expressed an interest in when registered
		 * (see listNotificationInterests.

		 *
		 * @param INotification a notification
		 */
		override public function handleNotification( note:INotification ):void
		{
			switch ( note.getName() )
			{
				// The max size of the window was passed in as a Point
				// Set the window size accordingly
				case SET_DEFAULT:
					var rect:Rectangle = new Rectangle();
					var maxSize:Point = new Point(Capabilities.screenResolutionX, Capabilities.screenResolutionY);
					rect.width = MIN_WIDTH;
	                rect.height = MIN_HEIGHT;
	                rect.x = (maxSize.x - rect.width)/2;
	                rect.y = (maxSize.y - rect.height)/2;
	                stage.nativeWindow.bounds = rect;
					break;

				// The previously saved window size has been
				// passed in as a Rectangle. Size the window accordingly
				case SET_BOUNDS:
					stage.nativeWindow.bounds = note.getBody() as Rectangle;
					break;

				// Sent when the app was saved in fullscreen mode
				case SET_FULLSCREEN:
					stage.nativeWindow.bounds = note.getBody() as Rectangle;
					stage.nativeWindow.maximize();
					break;

				// The window has been restored to the saved metrics, now show it
				// and then notify the application that its show time...
				case WINDOW_SHOW:
					stage.nativeWindow.visible = true;
					sendNotification( DesktopCitizenConstants.WINDOW_READY );
					break;

				case DesktopCitizenConstants.CONFIRM_CLOSING:
					needConfirm = false;
					stage.nativeWindow.close();
					break;

				case DesktopCitizenConstants.ENABLE_CONFIRM:
					needConfirm = true;
					break;

			}
		}

		/**
		 * Handle resize event dispatched from the viewComponent.
		 *
		 * @param event the resize event
		 */
		protected function onResize( event:NativeWindowBoundsEvent ):void
		{
			// The StageMediator passed in a Rectangle representing the size and location
			var rect:Rectangle = event.afterBounds;

			// Only save the changes to size and location if not minimized or maximized
			if ( windowMetricsProxy.displayState == NativeWindowDisplayState.NORMAL ) windowMetricsProxy.bounds = rect;

			// If this is the result of the window being resized programatically
			// at startup, then it's time to show the window now.
			if (firstPass) {
				firstPass = false;
				sendNotification( WINDOW_SHOW );
			}
		}

		/**
		 * Handle Display State change events dispatched from the viewComponent.
		 *
		 * @param event the Display State change event
		 */
		protected function onFullScreen( event:NativeWindowDisplayStateEvent ):void
		{
			// Update the WindoMetrics display_state property
			windowMetricsProxy.displayState = String( event.afterDisplayState );

		}

		/**
		 * Handle window closing events dispatched from the viewComponent.
		 *
		 * @param event the Display State change event
		 */
		protected function onWindowClosing( event:Event ):void
		{
			if( needConfirm )
			{
				event.preventDefault();
				sendNotification( DesktopCitizenConstants.WINDOW_CLOSING );
	  		} else {
	  			stage.nativeWindow.visible = false;
				sendNotification( WindowCloseCommand.NAME );
	  		}
		}

		/**
		 * Cast the viewComponent to its actual type.
		 *
		 * @return stage the viewComponent cast to flash.display.Stage
		 */
		protected function get stage():Stage{
			return viewComponent as Stage;
		}

		protected var windowMetricsProxy:WindowMetricsProxy;
		private var firstPass:Boolean 	= true;
		private var needConfirm:Boolean = false ;
	}
}

/*
  PureMVC Utility for AS3 / AIR - Desktop Citizen
  Copyright(c) 2007-10 Cliff Hall <clifford.hall@puremvc.org>
  Your reuse is governed by the Creative Commons Attribution 3.0 License
 */
package org.puremvc.as3.utilities.air.desktopcitizen.view
{
	import flash.display.NativeWindowDisplayState;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.NativeWindowBoundsEvent;
	import flash.events.NativeWindowDisplayStateEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.system.Capabilities;

	import org.puremvc.as3.interfaces.*;
	import org.puremvc.as3.patterns.mediator.Mediator;
	import org.puremvc.as3.utilities.air.desktopcitizen.DesktopCitizenConstants;
	import org.puremvc.as3.utilities.air.desktopcitizen.controller.WindowCloseCommand;
	import org.puremvc.as3.utilities.air.desktopcitizen.model.WindowMetricsProxy;

	/**
	 * A Mediator for interacting with the WindowedApplication Stage
	 */
	public class WindowMediator extends Mediator implements IMediator
	{
		// Cannonical name of the Mediator
		public static const NAME:String = DesktopCitizenConstants.UTIL+"WindowMediator";

		// Notification Constants specific to this mediator
		public static const WINDOW_SHOW:String		= NAME+"/WindowShow";
		public static const SET_DEFAULT:String		= NAME+"/SetDefault";
		public static const SET_BOUNDS:String		= NAME+"/SetBounds";
		public static const SET_FULLSCREEN:String	= NAME+"/SetFullScreen";

		// Constant for Minimum stage width
		public static const MIN_WIDTH:Number = 800;
		// Constant for Minimum stage height
		public static const MIN_HEIGHT:Number = 570;

		private var lastRect:Rectangle;

		/**
		 * Constructor.
		 */
		public function WindowMediator( viewComponent:Object )
		{
			// pass the viewComponent to the superclass where
			// it will be stored in the inherited viewComponent property
			super( NAME, viewComponent );
		}

		override public function onRegister():void
		{
			// cache a reference to frequently used proxies
			windowMetricsProxy = facade.retrieveProxy( WindowMetricsProxy.NAME ) as WindowMetricsProxy;

			// Listen for events from the view component
			stage.nativeWindow.addEventListener( Event.CLOSING, onWindowClosing );
			stage.nativeWindow.addEventListener( NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGE, onFullScreen )
			stage.nativeWindow.addEventListener( NativeWindowBoundsEvent.RESIZE, onResize );
			stage.nativeWindow.addEventListener( NativeWindowBoundsEvent.MOVE, onResize );
		}

		/**
		 * List all notifications this Mediator is interested in.
		 *

		 * Automatically called by the framework when the mediator
		 * is registered with the view.

		 *
		 * @return Array the list of Nofitication names
		 */
		override public function listNotificationInterests():Array
		{
			return [ SET_DEFAULT,
					 SET_BOUNDS,
					 SET_FULLSCREEN,
					 WINDOW_SHOW,
					 DesktopCitizenConstants.ENABLE_CONFIRM,
					 DesktopCitizenConstants.CONFIRM_CLOSING
					];
		}

		/**
		 * Handle all notifications this Mediator is interested in.
		 *

		 * Called by the framework when a notification is sent that
		 * this mediator expressed an interest in when registered
		 * (see listNotificationInterests.

		 *
		 * @param INotification a notification
		 */
		override public function handleNotification( note:INotification ):void
		{
			switch ( note.getName() )
			{
				// The max size of the window was passed in as a Point
				// Set the window size accordingly
				case SET_DEFAULT:
					var rect:Rectangle = new Rectangle();
					var maxSize:Point = new Point(Capabilities.screenResolutionX, Capabilities.screenResolutionY);
					rect.width = MIN_WIDTH;
	                rect.height = MIN_HEIGHT;
	                rect.x = (maxSize.x - rect.width)/2;
	                rect.y = (maxSize.y - rect.height)/2;
	                stage.nativeWindow.bounds = rect;
					break;

				// The previously saved window size has been
				// passed in as a Rectangle. Size the window accordingly
				case SET_BOUNDS:
					stage.nativeWindow.bounds = note.getBody() as Rectangle;
					break;

				// Sent when the app was saved in fullscreen mode
				case SET_FULLSCREEN:
					stage.nativeWindow.bounds = note.getBody() as Rectangle;
					stage.nativeWindow.maximize();
					break;

				// The window has been restored to the saved metrics, now show it
				// and then notify the application that its show time...
				case WINDOW_SHOW:
					stage.nativeWindow.visible = true;
					sendNotification( DesktopCitizenConstants.WINDOW_READY );
					break;

				case DesktopCitizenConstants.CONFIRM_CLOSING:
					needConfirm = false;
					stage.nativeWindow.close();
					break;

				case DesktopCitizenConstants.ENABLE_CONFIRM:
					needConfirm = true;
					break;

			}
		}

		/**
		 * Handle resize event dispatched from the viewComponent.
		 *
		 * @param event the resize event
		 */
		protected function onResize( event:NativeWindowBoundsEvent ):void
		{
			// The StageMediator passed in a Rectangle representing the size and location
			var rect:Rectangle = event.afterBounds;
			lastRect = windowMetricsProxy.bounds;
			// Only save the changes to size and location if not minimized or maximized
			if ( windowMetricsProxy.displayState == NativeWindowDisplayState.NORMAL ) windowMetricsProxy.bounds = rect;

			// If this is the result of the window being resized programatically
			// at startup, then it's time to show the window now.
			if (firstPass) {
				firstPass = false;
				sendNotification( WINDOW_SHOW );
			}
		}

		/**
		 * Handle Display State change events dispatched from the viewComponent.
		 *
		 * @param event the Display State change event
		 */
		protected function onFullScreen( event:NativeWindowDisplayStateEvent ):void
		{

			// Update the WindoMetrics display_state property
			windowMetricsProxy.displayState = String( event.afterDisplayState );
			if ( windowMetricsProxy.displayState == NativeWindowDisplayState.MAXIMIZED ) {
				windowMetricsProxy.bounds = lastRect;
			}
		}

		/**
		 * Handle window closing events dispatched from the viewComponent.
		 *
		 * @param event the Display State change event
		 */
		protected function onWindowClosing( event:Event ):void
		{
			if( needConfirm )
			{
				event.preventDefault();
				sendNotification( DesktopCitizenConstants.WINDOW_CLOSING );
	  		} else {
	  			stage.nativeWindow.visible = false;
				sendNotification( WindowCloseCommand.NAME );
	  		}
		}

		/**
		 * Cast the viewComponent to its actual type.
		 *
		 * @return stage the viewComponent cast to flash.display.Stage
		 */
		protected function get stage():Stage{
			return viewComponent as Stage;
		}

		protected var windowMetricsProxy:WindowMetricsProxy;
		private var firstPass:Boolean 	= true;
		private var needConfirm:Boolean = false ;
	}
}

Update WindowMediator.as

Update WindowMediator.as

when window is maximized, it should be stored the last window position, size instead of full screen size, otherwise, when the window relaunch and user click "restore" button on the top right corner, the window is still stayed as full screen size.


/**
 * @preserve Copyright (c) 2011 http://colorhook.com
 * @author: colorhook
 * @version: 2.4
 * @license: Released under the MIT License.
 *
 * Transplant from Flash AS3 APE Engine
 * http://www.cove.org/ape/
 * Copyright (c) 2006, 2007 Alec Cove
 * Released under the MIT Licenses.
 *
 * Dependency on EaselJS
 * http://easeljs.com/
 * Copyright (c) 2011 Grant Skinner
 * Released under the MIT License.
 */
package com.colorhook.spring{

	public class Spring{

		public static const VERSION:String="2.4";
		public static const COPYRIGHT:String="http://colorhook.com";

	}

}

/**
 * @preserve Copyright (c) 2011 http://colorhook.com
 * @author: colorhook
 * @version: 2.4
 * @license: Released under the MIT License.
 *
 */
package com.colorhook.spring{

	public class Spring{

		public static const VERSION:String="2.4";
		public static const COPYRIGHT:String="http://colorhook.com";

	}

}

update license

update license