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
            if (type != RobotType.SOLDIER) {
                this.imageCanvas.rotation = directionToRotation(direction);
            } else {
                // TODO soldier sprite
            }

            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;
                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 {
            return 0;
        }

        private function calculateDrawY(rounds:uint):Number {
            return 0;
        }

    }

}

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;
                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 {
            return 0;
        }

        private function calculateDrawY(rounds:uint):Number {
            return 0;
        }

    }

}

fix solider rotation

fix solider rotation


package fr.manashield.flex.thex
{
	import fr.manashield.flex.thex.blocks.Block;
	import fr.manashield.flex.thex.blocks.BlockGenerator;
	import fr.manashield.flex.thex.blocks.HexagonalGrid;
	import fr.manashield.flex.thex.events.BlockLandingEvent;
	import fr.manashield.flex.thex.utils.Color;

	import flash.display.Stage;
	import flash.geom.Point;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;

	/**
	 * @author Morgan Peyre (morgan@peyre.info)
	 * @author Paul Bonnet
	 */
	public class Game
	{
		private static const CELL_RADIUS:uint = 25;

		protected var _stage:Stage;
		protected var _origin:Point;
		protected var _score:uint;
		protected var _scoreField:TextField;

		public function Game(stage:Stage):void
		{
			// Game board
			_stage = stage;
			HexagonalGrid.init(_stage, CELL_RADIUS);
			_origin = _origin = HexagonalGrid.instance.hexToCartesian(new Point(0,0));

			// Score field
			_score = 0;

			_scoreField = new TextField();
			_scoreField.name = "_scoreField";
			// TODO : repositionner le texte en fonction du ratio d'aspect
			_scoreField.autoSize = TextFieldAutoSize.LEFT;
			stage.addChild(_scoreField);

			var format:TextFormat = new TextFormat();
			format.size = 100;
			format.font = "score";
			format.color = 0x555555;
			format.letterSpacing = 4;
			_scoreField.defaultTextFormat = format;
			_scoreField.embedFonts = true;
			_scoreField.text = _score.toString();

			// central hexagon
			var centralBlock:Block = new Block(HexagonalGrid.instance.cell(new Point(0,0)), new Color(0x5c5c5c));
			$(centralBlock); // Get rid of the annoying "not used" warning.

			// reate the initial blocks
			this.fillGrid();

			// initialze and launch the animation of the falling blocks
			Animation.initialize(this);
			Animation.instance.addEventListener(BlockLandingEvent.LANDING, this.blockLanded);

			// create first falling block
			BlockGenerator.instance.spawnBlock();
		}

		public function get stage():Stage
		{
			return _stage;
		}

		public function blockLanded(e:BlockLandingEvent):void
		{
			if(e.block.sameColorNeighbors() >= 3)
			{
				_score += e.block.sameColorNeighbors() - 2;
				_scoreField.text = _score.toString();

				e.block.destroyIf(e.block.color);
			}
		}

		protected function fillGrid(radius:uint = 3):void
		{
			for(var i:uint=1; i<=radius; ++i)
			{
				for(var j:uint=0; j<6*i; ++j)
				{
					BlockGenerator.instance.spawnBlock(i, j, false);
				}
			}
		}

		//useless function used to get rid of a warning
		private function $(o:Object):void{}
	}
}

package fr.manashield.flex.thex
{
	import fr.manashield.flex.thex.blocks.Block;
	import fr.manashield.flex.thex.blocks.BlockGenerator;
	import fr.manashield.flex.thex.blocks.HexagonalGrid;
	import fr.manashield.flex.thex.events.BlockLandingEvent;
	import fr.manashield.flex.thex.utils.Color;

	import flash.display.Stage;
	import flash.geom.Point;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;

	/**
	 * @author Morgan Peyre (morgan@peyre.info)
	 * @author Paul Bonnet
	 */
	public class Game
	{
		private static const CELL_RADIUS:uint = 25;

		protected var _stage:Stage;
		protected var _origin:Point;
		protected var _score:uint;
		protected var _scoreField:TextField;

		public function Game(stage:Stage):void
		{
			// Game board
			_stage = stage;
			HexagonalGrid.init(_stage, CELL_RADIUS);
			_origin = _origin = HexagonalGrid.instance.hexToCartesian(new Point(0,0));

			// Score field
			_score = 0;

			_scoreField = new TextField();
			_scoreField.name = "_scoreField";
			// TODO : repositionner le texte en fonction du ratio d'aspect
			_scoreField.autoSize = TextFieldAutoSize.LEFT;
			stage.addChild(_scoreField);

			var format:TextFormat = new TextFormat();
			format.size = 100;
			format.font = "score";
			format.color = 0x555555;
			format.letterSpacing = 4;
			_scoreField.defaultTextFormat = format;
			_scoreField.embedFonts = true;
			_scoreField.text = _score.toString();

			// central hexagon
			var centralBlock:Block = new Block(HexagonalGrid.instance.cell(new Point(0,0)), new Color(0x5c5c5c));
			$(centralBlock); // Get rid of the annoying "not used" warning.

			// create the initial blocks
			this.fillGrid();

			// initialze and launch the animation of the falling blocks
			Animation.initialize(this);
			Animation.instance.addEventListener(BlockLandingEvent.LANDING, this.blockLanded);

			// create first falling block
			BlockGenerator.instance.spawnBlock();
		}

		public function get stage():Stage
		{
			return _stage;
		}

		public function blockLanded(e:BlockLandingEvent):void
		{
			if(e.block.sameColorNeighbors() >= 3)
			{
				_score += e.block.sameColorNeighbors() - 2;
				_scoreField.text = _score.toString();

				e.block.destroyIf(e.block.color);
			}
		}

		protected function fillGrid(radius:uint = 3):void
		{
			for(var i:uint=1; i<=radius; ++i)
			{
				for(var j:uint=0; j<6*i; ++j)
				{
					BlockGenerator.instance.spawnBlock(i, j, false);
				}
			}
		}

		//useless function used to get rid of a warning
		private function $(o:Object):void{}
	}
}

comment fix

comment fix


package com.kaltura.kdpfl
{
	import com.kaltura.kdpfl.controller.InitMacroCommand;
	import com.kaltura.kdpfl.controller.LayoutReadyCommand;
	import com.kaltura.kdpfl.controller.PlaybackCompleteCommand;
	import com.kaltura.kdpfl.controller.SequenceItemPlayEndCommand;
	import com.kaltura.kdpfl.controller.SequenceSkipNextCommand;
	import com.kaltura.kdpfl.controller.StartupCommand;
	import com.kaltura.kdpfl.controller.media.InitChangeMediaMacroCommand;
	import com.kaltura.kdpfl.controller.media.LiveStreamCommand;
	import com.kaltura.kdpfl.controller.media.MediaReadyCommand;
	import com.kaltura.kdpfl.events.DynamicEvent;
	import com.kaltura.kdpfl.model.ExternalInterfaceProxy;
	import com.kaltura.kdpfl.model.type.DebugLevel;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.view.controls.KTrace;
	import com.kaltura.kdpfl.view.media.KMediaPlayerMediator;
	import com.kaltura.puremvc.as3.core.KView;

	import flash.display.DisplayObject;

	import mx.utils.ObjectProxy;

	import org.osmf.media.MediaPlayerState;
	import org.puremvc.as3.interfaces.IFacade;
	import org.puremvc.as3.interfaces.IMediator;
	import org.puremvc.as3.interfaces.IProxy;
	import org.puremvc.as3.patterns.facade.Facade;

	public class ApplicationFacade extends Facade implements IFacade
	{
		/**
		 * The current version of the KDP.
		 */
		public var kdpVersion : String = "v3.9.3";

		/**
		 * save any mediator name that is registered to this array in order to delete at any time
		 * (the pure mvc don't support it as built-in function)
		 */
		private var _mediatorNameArray : Array = new Array();
		/**
		 * save any proxy name that is registered to this array in order to delete at any time
		 * (the pure mvc don't support it as built-in function)
		 */
		private var _proxyNameArray : Array = new Array();
		/**
		 * save any notification that create a command name that is registered to this array in order to delete at any time
		 * (the pure mvc don't support it as built-in function)
		 */
		private var _commandNameArray : Array = new Array();

		/**
		 * The bindObject create dynamicly all attributes that need to be binded on it
		 */
		public var bindObject:ObjectProxy = new ObjectProxy();
		/**
		 * a reference to KDP3 main application
		 */
		private var _app : DisplayObject;

		/**
		 * the url of the kdp
		 */
		public var appFolder : String;
		public var debugMode : Boolean = false;
		/**
		 *
		 * will affect the traces that will be sent. See DebugLevel enum
		 */
		public var debugLevel:int;

		private var externalInterface : ExternalInterfaceProxy;

		/**
		 * return the one and only instance of the ApplicationFacade,
		 * if not exist it will be created.
		 * @return
		 *
		 */
		public static function getInstance() : ApplicationFacade
		{
			if (instance == null) instance = new ApplicationFacade();
			return instance as ApplicationFacade;
		}

		/**
		 * All this simply does is fire a notification which is routed to
		 * "StartupCommand" via the "registerCommand" handlers
		 * @param app
		 *
		 */
		public function start(app:DisplayObject):void
		{
			CONFIG::isSDK46 {
				kdpVersion += ".sdk46";
			}

			_app = app;
			appFolder = app.root.loaderInfo.url;
			appFolder = appFolder.substr(0, appFolder.lastIndexOf('/') + 1);

			sendNotification(NotificationType.STARTUP, app);
			externalInterface = retrieveProxy(ExternalInterfaceProxy.NAME) as ExternalInterfaceProxy;
		}

		/**
		 * Created to trace all notifications for debug
		 * @param notificationName
		 * @param body
		 * @param type
		 *
		 */
		override public function sendNotification(notificationName:String, body:Object = null, type:String = null):void {
			if (debugMode) {
				var s:String;
				if ((notificationName == NotificationType.BYTES_DOWNLOADED_CHANGE && debugLevel == DebugLevel.HIGH) ||
					(notificationName == NotificationType.PLAYER_UPDATE_PLAYHEAD && (debugLevel == DebugLevel.HIGH || debugLevel == DebugLevel.MEDIUM)) ||
					(notificationName != NotificationType.PLAYER_UPDATE_PLAYHEAD && notificationName != NotificationType.BYTES_DOWNLOADED_CHANGE))
				 {
					if (notificationName == NotificationType.PLAYER_STATE_CHANGE)
						s = 'Sent ' + notificationName + ' ==> ' + body.toString();
					else {
						s = 'Sent ' + notificationName;
						if (body) {
							var found:Boolean = false;
							for (var o:* in body) {
								s += ", " + o + ":" + body[o];
								found = true;
							}
							if (!found) {
								s += ", " + body.toString();
							}
						}
					}
				}

				if (s && s != "null") {
					var curTime:Number = 0;
					var kmediaPlayerMediator:KMediaPlayerMediator = retrieveMediator(KMediaPlayerMediator.NAME) as KMediaPlayerMediator;
					if (kmediaPlayerMediator &&
						kmediaPlayerMediator.player &&
						kmediaPlayerMediator.player.state!=MediaPlayerState.LOADING &&
						kmediaPlayerMediator.player.state!=MediaPlayerState.UNINITIALIZED &&
						kmediaPlayerMediator.player.state!=MediaPlayerState.PLAYBACK_ERROR)
					{
						curTime = kmediaPlayerMediator.getCurrentTime();
					}
					var date:Date = new Date();

					KTrace.getInstance().log(date.toTimeString(), ":", s, ", playhead position:", curTime);
				}

			}

			super.sendNotification(notificationName, body, type);
			//For external Flash/Flex application application listening to the KDP events
			_app.dispatchEvent(new DynamicEvent(notificationName, body));

			//For external Javascript application listening to the KDP events
			if (externalInterface)
				externalInterface.notifyJs(notificationName, body);

		}

		/**
		 * controls the routing of notifications to our controllers,
		 * we all know them as commands
		 *
		 */
		override protected function initializeController():void
		{
			super.initializeController();
			registerCommand(NotificationType.STARTUP, StartupCommand);

			// we do both init start KDP life cycle, and start to load the entry simultaneously
			registerCommand(NotificationType.INITIATE_APP, InitMacroCommand);

			//There are several things that need to be done as soon as the layout of the kdp is ready.
			registerCommand(NotificationType.LAYOUT_READY, LayoutReadyCommand );
			//when one call change media we fire the load media command again
			registerCommand(NotificationType.CHANGE_MEDIA, InitChangeMediaMacroCommand);

			registerCommand (NotificationType.LIVE_ENTRY, LiveStreamCommand );

			registerCommand (NotificationType.MEDIA_LOADED, MediaReadyCommand );

			registerCommand (NotificationType.PLAYBACK_COMPLETE, PlaybackCompleteCommand);

			registerCommand(NotificationType.SEQUENCE_ITEM_PLAY_END, SequenceItemPlayEndCommand);

			registerCommand(NotificationType.SEQUENCE_SKIP_NEXT, SequenceSkipNextCommand);
		}

		override protected function initializeView():void
		{
			if ( view != null ) return;
			view = KView.getInstance();
		}

		override protected function initializeFacade():void
		{
			initializeView();
			initializeModel();
			initializeController();
		}

		/**
		 * Help to remove all commands, mediator, proxies from the facade
		 *
		 */
		public function dispose() : void
		{
			var i:int =0;

			for(i=0; i<_commandNameArray.length; i++)
				this.removeCommand( _commandNameArray[i] );

			for(i=0; i<_mediatorNameArray.length; i++)
				this.removeMediator( _mediatorNameArray[i] );

			for(i=0; i<_proxyNameArray.length; i++)
				this.removeProxy( _proxyNameArray[i] );

			_commandNameArray = new Array();
			_mediatorNameArray = new Array();
			_proxyNameArray = new Array();
		}

		/**
		 * after registartion add the notification name to a
		 * @param notificationName
		 * @param commandClassRef
		 *
		 */
		override public function registerCommand(notificationName:String, commandClassRef:Class):void
		{
			super.registerCommand(notificationName, commandClassRef);

			//save the notification name so we can delete it later
			_commandNameArray.push( notificationName );
		}

		override public function registerMediator(mediator:IMediator):void
		{
			super.registerMediator(mediator);

			//save the mediator name so we can delete it later
			_mediatorNameArray.push( mediator.getMediatorName() );
		}

		override public function registerProxy(proxy:IProxy):void
		{
			bindObject[proxy.getProxyName()] = proxy.getData();

			super.registerProxy(proxy);

			//save the proxy name so we can delete it later
			_proxyNameArray.push( proxy.getProxyName() );
		}

		/**
		 * add notification observer for a given mediator
		 * @param notification the notification to listen for
		 * @param notificationHandler handler to be called
		 * @param mediator
		 *
		 */
		public function addNotificationInterest(notification:String, notificationHandler:Function, mediator:IMediator):void {
			(view as KView).addNotificationInterest(notification, notificationHandler, mediator);
		}

		public function get app () : DisplayObject
		{
			return _app;
		}

		public function set app (disObj : DisplayObject) : void
		{
			_app = disObj;
		}

	}
}

package com.kaltura.kdpfl
{
	import com.kaltura.kdpfl.controller.InitMacroCommand;
	import com.kaltura.kdpfl.controller.LayoutReadyCommand;
	import com.kaltura.kdpfl.controller.PlaybackCompleteCommand;
	import com.kaltura.kdpfl.controller.SequenceItemPlayEndCommand;
	import com.kaltura.kdpfl.controller.SequenceSkipNextCommand;
	import com.kaltura.kdpfl.controller.StartupCommand;
	import com.kaltura.kdpfl.controller.media.InitChangeMediaMacroCommand;
	import com.kaltura.kdpfl.controller.media.LiveStreamCommand;
	import com.kaltura.kdpfl.controller.media.MediaReadyCommand;
	import com.kaltura.kdpfl.events.DynamicEvent;
	import com.kaltura.kdpfl.model.ExternalInterfaceProxy;
	import com.kaltura.kdpfl.model.type.DebugLevel;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.view.controls.KTrace;
	import com.kaltura.kdpfl.view.media.KMediaPlayerMediator;
	import com.kaltura.puremvc.as3.core.KView;

	import flash.display.DisplayObject;

	import mx.utils.ObjectProxy;

	import org.osmf.media.MediaPlayerState;
	import org.puremvc.as3.interfaces.IFacade;
	import org.puremvc.as3.interfaces.IMediator;
	import org.puremvc.as3.interfaces.IProxy;
	import org.puremvc.as3.patterns.facade.Facade;

	public class ApplicationFacade extends Facade implements IFacade
	{
		/**
		 * The current version of the KDP.
		 */
		public var kdpVersion : String = "v3.9.4";

		/**
		 * save any mediator name that is registered to this array in order to delete at any time
		 * (the pure mvc don't support it as built-in function)
		 */
		private var _mediatorNameArray : Array = new Array();
		/**
		 * save any proxy name that is registered to this array in order to delete at any time
		 * (the pure mvc don't support it as built-in function)
		 */
		private var _proxyNameArray : Array = new Array();
		/**
		 * save any notification that create a command name that is registered to this array in order to delete at any time
		 * (the pure mvc don't support it as built-in function)
		 */
		private var _commandNameArray : Array = new Array();

		/**
		 * The bindObject create dynamicly all attributes that need to be binded on it
		 */
		public var bindObject:ObjectProxy = new ObjectProxy();
		/**
		 * a reference to KDP3 main application
		 */
		private var _app : DisplayObject;

		/**
		 * the url of the kdp
		 */
		public var appFolder : String;
		public var debugMode : Boolean = false;
		/**
		 *
		 * will affect the traces that will be sent. See DebugLevel enum
		 */
		public var debugLevel:int;

		private var externalInterface : ExternalInterfaceProxy;

		/**
		 * return the one and only instance of the ApplicationFacade,
		 * if not exist it will be created.
		 * @return
		 *
		 */
		public static function getInstance() : ApplicationFacade
		{
			if (instance == null) instance = new ApplicationFacade();
			return instance as ApplicationFacade;
		}

		/**
		 * All this simply does is fire a notification which is routed to
		 * "StartupCommand" via the "registerCommand" handlers
		 * @param app
		 *
		 */
		public function start(app:DisplayObject):void
		{
			CONFIG::isSDK46 {
				kdpVersion += ".sdk46";
			}

			_app = app;
			appFolder = app.root.loaderInfo.url;
			appFolder = appFolder.substr(0, appFolder.lastIndexOf('/') + 1);

			sendNotification(NotificationType.STARTUP, app);
			externalInterface = retrieveProxy(ExternalInterfaceProxy.NAME) as ExternalInterfaceProxy;
		}

		/**
		 * Created to trace all notifications for debug
		 * @param notificationName
		 * @param body
		 * @param type
		 *
		 */
		override public function sendNotification(notificationName:String, body:Object = null, type:String = null):void {
			if (debugMode) {
				var s:String;
				if ((notificationName == NotificationType.BYTES_DOWNLOADED_CHANGE && debugLevel == DebugLevel.HIGH) ||
					(notificationName == NotificationType.PLAYER_UPDATE_PLAYHEAD && (debugLevel == DebugLevel.HIGH || debugLevel == DebugLevel.MEDIUM)) ||
					(notificationName != NotificationType.PLAYER_UPDATE_PLAYHEAD && notificationName != NotificationType.BYTES_DOWNLOADED_CHANGE))
				 {
					if (notificationName == NotificationType.PLAYER_STATE_CHANGE)
						s = 'Sent ' + notificationName + ' ==> ' + body.toString();
					else {
						s = 'Sent ' + notificationName;
						if (body) {
							var found:Boolean = false;
							for (var o:* in body) {
								s += ", " + o + ":" + body[o];
								found = true;
							}
							if (!found) {
								s += ", " + body.toString();
							}
						}
					}
				}

				if (s && s != "null") {
					var curTime:Number = 0;
					var kmediaPlayerMediator:KMediaPlayerMediator = retrieveMediator(KMediaPlayerMediator.NAME) as KMediaPlayerMediator;
					if (kmediaPlayerMediator &&
						kmediaPlayerMediator.player &&
						kmediaPlayerMediator.player.state!=MediaPlayerState.LOADING &&
						kmediaPlayerMediator.player.state!=MediaPlayerState.UNINITIALIZED &&
						kmediaPlayerMediator.player.state!=MediaPlayerState.PLAYBACK_ERROR)
					{
						curTime = kmediaPlayerMediator.getCurrentTime();
					}
					var date:Date = new Date();

					KTrace.getInstance().log(date.toTimeString(), ":", s, ", playhead position:", curTime);
				}

			}

			super.sendNotification(notificationName, body, type);
			//For external Flash/Flex application application listening to the KDP events
			_app.dispatchEvent(new DynamicEvent(notificationName, body));

			//For external Javascript application listening to the KDP events
			if (externalInterface)
				externalInterface.notifyJs(notificationName, body);

		}

		/**
		 * controls the routing of notifications to our controllers,
		 * we all know them as commands
		 *
		 */
		override protected function initializeController():void
		{
			super.initializeController();
			registerCommand(NotificationType.STARTUP, StartupCommand);

			// we do both init start KDP life cycle, and start to load the entry simultaneously
			registerCommand(NotificationType.INITIATE_APP, InitMacroCommand);

			//There are several things that need to be done as soon as the layout of the kdp is ready.
			registerCommand(NotificationType.LAYOUT_READY, LayoutReadyCommand );
			//when one call change media we fire the load media command again
			registerCommand(NotificationType.CHANGE_MEDIA, InitChangeMediaMacroCommand);

			registerCommand (NotificationType.LIVE_ENTRY, LiveStreamCommand );

			registerCommand (NotificationType.MEDIA_LOADED, MediaReadyCommand );

			registerCommand (NotificationType.PLAYBACK_COMPLETE, PlaybackCompleteCommand);

			registerCommand(NotificationType.SEQUENCE_ITEM_PLAY_END, SequenceItemPlayEndCommand);

			registerCommand(NotificationType.SEQUENCE_SKIP_NEXT, SequenceSkipNextCommand);
		}

		override protected function initializeView():void
		{
			if ( view != null ) return;
			view = KView.getInstance();
		}

		override protected function initializeFacade():void
		{
			initializeView();
			initializeModel();
			initializeController();
		}

		/**
		 * Help to remove all commands, mediator, proxies from the facade
		 *
		 */
		public function dispose() : void
		{
			var i:int =0;

			for(i=0; i<_commandNameArray.length; i++)
				this.removeCommand( _commandNameArray[i] );

			for(i=0; i<_mediatorNameArray.length; i++)
				this.removeMediator( _mediatorNameArray[i] );

			for(i=0; i<_proxyNameArray.length; i++)
				this.removeProxy( _proxyNameArray[i] );

			_commandNameArray = new Array();
			_mediatorNameArray = new Array();
			_proxyNameArray = new Array();
		}

		/**
		 * after registartion add the notification name to a
		 * @param notificationName
		 * @param commandClassRef
		 *
		 */
		override public function registerCommand(notificationName:String, commandClassRef:Class):void
		{
			super.registerCommand(notificationName, commandClassRef);

			//save the notification name so we can delete it later
			_commandNameArray.push( notificationName );
		}

		override public function registerMediator(mediator:IMediator):void
		{
			super.registerMediator(mediator);

			//save the mediator name so we can delete it later
			_mediatorNameArray.push( mediator.getMediatorName() );
		}

		override public function registerProxy(proxy:IProxy):void
		{
			bindObject[proxy.getProxyName()] = proxy.getData();

			super.registerProxy(proxy);

			//save the proxy name so we can delete it later
			_proxyNameArray.push( proxy.getProxyName() );
		}

		/**
		 * add notification observer for a given mediator
		 * @param notification the notification to listen for
		 * @param notificationHandler handler to be called
		 * @param mediator
		 *
		 */
		public function addNotificationInterest(notification:String, notificationHandler:Function, mediator:IMediator):void {
			(view as KView).addNotificationInterest(notification, notificationHandler, mediator);
		}

		public function get app () : DisplayObject
		{
			return _app;
		}

		public function set app (disObj : DisplayObject) : void
		{
			_app = disObj;
		}

	}
}

update version to 3.9.4

update version to 3.9.4


////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You 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
{

import flash.display.Sprite;

import classes.B;

import interfaces.IA;
import interfaces.IB;
import interfaces.IC;
import interfaces.ID;
import interfaces.IE;
import interfaces.IF;

public class LanguageTests extends Sprite implements IA, IE
{
	public function LanguageTests()
	{
		var testResult:Boolean;
		var testObject:Object;

		var b:B = new B();

		testResult = this instanceof Sprite;
		trace('this instanceof Sprite - true: ' + testResult.toString());
		testResult = this instanceof B;
		trace('this instanceof classes.B - false: ' + testResult.toString());
		testResult = b instanceof classes.B;
		trace('b instanceof classes.B - true: ' + testResult.toString());
		testResult = b instanceof classes.C;
		trace('b instanceof classes.C - true: ' + testResult.toString());
		testResult = b instanceof interfaces.IC;
		trace('b instanceof interfaces.IC - false: ' + testResult.toString());
		testResult = b instanceof interfaces.IF;
		trace('b instanceof interfaces.IF - false: ' + testResult.toString());
		testResult = this instanceof IA;
		trace('this instanceof interfaces.IA - false: ' + testResult.toString());
		testResult = this instanceof IB;
		trace('this instanceof interfaces.IB - false: ' + testResult.toString());
		testResult = this instanceof IC;
		trace('this instanceof interfaces.IC - false: ' + testResult.toString());
		testResult = this instanceof ID;
		trace('this instanceof interfaces.ID - false: ' + testResult.toString());
		testResult = this instanceof IE;
		trace('this instanceof interfaces.IE - false: ' + testResult.toString());
		trace();
		testResult = this is Sprite;
		trace('this is Sprite - true: ' + testResult.toString());
		testResult = this is B;
		trace('this is classes.B - false: ' + testResult.toString());
		testResult = b is classes.B;
		trace('b is classes.B - true: ' + testResult.toString());
		testResult = b is classes.C;
		trace('b is classes.C - true: ' + testResult.toString());
		testResult = b is interfaces.IC;
		trace('b is interfaces.IC - false: ' + testResult.toString());
		testResult = b is interfaces.IF;
		trace('b is interfaces.IF - true: ' + testResult.toString());
		testResult = this is IA;
		trace('this is interfaces.IA - true: ' + testResult.toString());
		testResult = this is IB;
		trace('this is interfaces.IB - false: ' + testResult.toString());
		testResult = this is IC;
		trace('this is interfaces.IC - true: ' + testResult.toString());
		testResult = this is ID;
		trace('this is interfaces.ID - true: ' + testResult.toString());
		testResult = this is IE;
		trace('this is interfaces.IE - true: ' + testResult.toString());
		trace();
		testObject = (this as Sprite) ? this as Sprite : 'null';
		trace('this as Sprite - [object ...]: ' + testObject.toString());
		testObject = (this as B) ? this as B : 'null';
		trace('this as classes.B - null: ' + testObject.toString());
		testObject = (b as classes.B) ? b as classes.B : 'null';
		trace('b as classes.B - [object ...]: ' + testObject.toString());
		testObject = (b as classes.C) ? b as classes.C : 'null';
		trace('b as classes.C - [object ...]: ' + testObject.toString());
		testObject = (b as interfaces.IC) ? b as interfaces.IC : 'null';
		trace('b as interfaces.IC - null: ' + testObject.toString());
		testObject = (b as interfaces.IF) ? b as interfaces.IF : 'null';
		trace('b as interfaces.IF - [object ...]: ' + testObject.toString());
		testObject = (this as IA) ? this as IA : 'null';
		trace('this as interfaces.IA - [object ...]: ' + testObject.toString());
		testObject = (this as IB) ? this as IB : 'null';
		trace('this as interfaces.IB - null: ' + testObject.toString());
		testObject = (this as IC) ? this as IC : 'null';
		trace('this as interfaces.IC - [object ...]: ' + testObject.toString());
		testObject = (this as ID) ? this as ID : 'null';
		trace('this as interfaces.ID - [object ...]: ' + testObject.toString());
		testObject = (this as IE) ? this as IE : 'null';
		trace('this as interfaces.IE - [object ...]: ' + testObject.toString());
		trace();
		try {
			testObject = Sprite(this);
			trace('Sprite(this) - [object ...]: ' + testObject.toString());
		} catch (e:Error)
		{
			trace("This shouldn't show!");
		}
		try {
			testObject = B(this);
			trace("This shouldn't show!");
		} catch (e:Error)
		{
			trace('B(this) - exception expected: ' + e.message);
		}
		try {
			testObject = interfaces.IC(b);
			trace("This shouldn't show!");
		} catch (e:Error)
		{
			trace('IC(b) - exception expected: ' + e.message);
		}
		try {
			testObject = interfaces.IF(b);
			trace('IF(b) - [object ...]: ' + testObject.toString());
		} catch (e:Error)
		{
			trace("This shouldn't show!");
		}
	}
}
}

////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You 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
{

import org.apache.flex.core.SimpleApplication;
import org.apache.flex.events.Event;

import classes.B;

import interfaces.IA;
import interfaces.IB;
import interfaces.IC;
import interfaces.ID;
import interfaces.IE;
import interfaces.IF;

public class LanguageTests extends SimpleApplication implements IA, IE
{
	public function LanguageTests()
	{
		var testResult:Boolean;
		var testObject:Object;

		var b:B = new B();

		testResult = this instanceof SimpleApplication;
		trace('this instanceof SimpleApplication - true: ' + testResult.toString());
		testResult = this instanceof B;
		trace('this instanceof classes.B - false: ' + testResult.toString());
		testResult = b instanceof classes.B;
		trace('b instanceof classes.B - true: ' + testResult.toString());
		testResult = b instanceof classes.C;
		trace('b instanceof classes.C - true: ' + testResult.toString());
		testResult = b instanceof interfaces.IC;
		trace('b instanceof interfaces.IC - false: ' + testResult.toString());
		testResult = b instanceof interfaces.IF;
		trace('b instanceof interfaces.IF - false: ' + testResult.toString());
		testResult = this instanceof IA;
		trace('this instanceof interfaces.IA - false: ' + testResult.toString());
		testResult = this instanceof IB;
		trace('this instanceof interfaces.IB - false: ' + testResult.toString());
		testResult = this instanceof IC;
		trace('this instanceof interfaces.IC - false: ' + testResult.toString());
		testResult = this instanceof ID;
		trace('this instanceof interfaces.ID - false: ' + testResult.toString());
		testResult = this instanceof IE;
		trace('this instanceof interfaces.IE - false: ' + testResult.toString());
		trace();
		testResult = this is SimpleApplication;
		trace('this is SimpleApplication - true: ' + testResult.toString());
		testResult = this is B;
		trace('this is classes.B - false: ' + testResult.toString());
		testResult = b is classes.B;
		trace('b is classes.B - true: ' + testResult.toString());
		testResult = b is classes.C;
		trace('b is classes.C - true: ' + testResult.toString());
		testResult = b is interfaces.IC;
		trace('b is interfaces.IC - false: ' + testResult.toString());
		testResult = b is interfaces.IF;
		trace('b is interfaces.IF - true: ' + testResult.toString());
		testResult = this is IA;
		trace('this is interfaces.IA - true: ' + testResult.toString());
		testResult = this is IB;
		trace('this is interfaces.IB - false: ' + testResult.toString());
		testResult = this is IC;
		trace('this is interfaces.IC - true: ' + testResult.toString());
		testResult = this is ID;
		trace('this is interfaces.ID - true: ' + testResult.toString());
		testResult = this is IE;
		trace('this is interfaces.IE - true: ' + testResult.toString());
		trace();
		testObject = (this as SimpleApplication) ? this as SimpleApplication : 'null';
		trace('this as SimpleApplication - [object ...]: ' + testObject.toString());
		testObject = (this as B) ? this as B : 'null';
		trace('this as classes.B - null: ' + testObject.toString());
		testObject = (b as classes.B) ? b as classes.B : 'null';
		trace('b as classes.B - [object ...]: ' + testObject.toString());
		testObject = (b as classes.C) ? b as classes.C : 'null';
		trace('b as classes.C - [object ...]: ' + testObject.toString());
		testObject = (b as interfaces.IC) ? b as interfaces.IC : 'null';
		trace('b as interfaces.IC - null: ' + testObject.toString());
		testObject = (b as interfaces.IF) ? b as interfaces.IF : 'null';
		trace('b as interfaces.IF - [object ...]: ' + testObject.toString());
		testObject = (this as IA) ? this as IA : 'null';
		trace('this as interfaces.IA - [object ...]: ' + testObject.toString());
		testObject = (this as IB) ? this as IB : 'null';
		trace('this as interfaces.IB - null: ' + testObject.toString());
		testObject = (this as IC) ? this as IC : 'null';
		trace('this as interfaces.IC - [object ...]: ' + testObject.toString());
		testObject = (this as ID) ? this as ID : 'null';
		trace('this as interfaces.ID - [object ...]: ' + testObject.toString());
		testObject = (this as IE) ? this as IE : 'null';
		trace('this as interfaces.IE - [object ...]: ' + testObject.toString());
		trace();
		try {
			testObject = SimpleApplication(this);
			trace('SimpleApplication(this) - [object ...]: ' + testObject.toString());
		} catch (e:Error)
		{
			trace("This shouldn't show!");
		}
		try {
			testObject = B(this);
			trace("This shouldn't show!");
		} catch (e:Error)
		{
			trace('B(this) - exception expected: ' + e.message);
		}
		try {
			testObject = interfaces.IC(b);
			trace("This shouldn't show!");
		} catch (e:Error)
		{
			trace('IC(b) - exception expected: ' + e.message);
		}
		try {
			testObject = interfaces.IF(b);
			trace('IF(b) - [object ...]: ' + testObject.toString());
		} catch (e:Error)
		{
			trace("This shouldn't show!");
		}

        addEventListener("foo", eventHandler);
        if (hasEventListener("foo"))
            trace("addEventListener worked");
        else
            trace("This shouldn't show!");

        removeEventListener("foo", eventHandler);
        if (!hasEventListener("foo"))
            trace("removeEventListener worked");
        else
            trace("This shouldn't show!");
	}

    public function eventHandler(e:Event):void
    {

    }
}
}

switch language tests to use SimpleApp and add event listener tests


const int PAINT_STEP_UPDATE = 16;
const int PAINT_SELECTION_KEY = KEY_C;

bool EditorPaintSelectionShow = false;
int EditorPaintSelectionUITimeToUpdate = 0;

UIElement@ EditorPaintSelectionUIContainer = null;
BorderImage@ paintSelectionImage = null;

IntVector2 paintSelectionBrushDefaultSize(96,96);
IntVector2 paintSelectionBrushCurrentSize = paintSelectionBrushDefaultSize;
IntVector2 paintSelectionBrushMinSize(64,64);
IntVector2 paintSelectionBrushMaxSize(512,512);
IntVector2 paintSelectionBrushStepSizeChange(16,16);

void CreatePaintSelectionContainer()
{
    if (editorScene is null) return;
    EditorPaintSelectionUIContainer = UIElement();
    EditorPaintSelectionUIContainer.position = IntVector2(0,0);
    EditorPaintSelectionUIContainer.size = IntVector2(graphics.width,graphics.height);
    EditorPaintSelectionUIContainer.priority = -5;
    EditorPaintSelectionUIContainer.focusMode = FM_NOTFOCUSABLE;
    EditorPaintSelectionUIContainer.bringToBack = true;
    EditorPaintSelectionUIContainer.name ="DebugPaintSelectionContainer";
    EditorPaintSelectionUIContainer.temporary = true;
    ui.root.AddChild(EditorPaintSelectionUIContainer);
}

void CreatePaintSelectionTool()
{
    paintSelectionImage = BorderImage("Icon");
    paintSelectionImage.temporary = true;
    paintSelectionImage.SetFixedSize(paintSelectionBrushDefaultSize.x,paintSelectionBrushDefaultSize.y);
    paintSelectionImage.texture = cache.GetResource("Texture2D", "Textures/Editor/SelectionCircle.png");
    paintSelectionImage.imageRect = IntRect(0,0,512,512);
    paintSelectionImage.priority = -5;
    paintSelectionImage.color = Color(1,1,1);
    paintSelectionImage.bringToBack = true;
    paintSelectionImage.enabled = false;
    paintSelectionImage.selected = false;
    paintSelectionImage.visible = true;
    EditorPaintSelectionUIContainer.AddChild(paintSelectionImage);
}

void UpdatePaintSelection()
{
    PaintSelectionCheckKeyboard();

    // Early out if disabled
    if (!EditorPaintSelectionShow) return;

    if (editorScene is null || EditorPaintSelectionUITimeToUpdate > time.systemTime) return;

    EditorPaintSelectionUIContainer = ui.root.GetChild("DebugPaintSelectionContainer");

    if (EditorPaintSelectionUIContainer is null)
    {
        CreatePaintSelectionContainer();
        CreatePaintSelectionTool();
    }

    if (EditorPaintSelectionUIContainer !is null)
    {
        // Set visibility for all origins
        EditorPaintSelectionUIContainer.visible = EditorPaintSelectionShow;

        if (viewportMode!=VIEWPORT_SINGLE)
            EditorPaintSelectionUIContainer.visible = false;

        if (EditorPaintSelectionShow)
        {
            IntVector2 mp = input.mousePosition;
            paintSelectionImage.position = IntVector2(mp.x - (paintSelectionBrushCurrentSize.x * 0.5f), mp.y - (paintSelectionBrushCurrentSize.y * 0.5f));
        }
    }

    EditorPaintSelectionUITimeToUpdate = time.systemTime + PAINT_STEP_UPDATE;
}

void PaintSelectionCheckKeyboard()
{
    bool key = input.keyPress[PAINT_SELECTION_KEY];

    if (key && ui.focusElement is null)
    {
        EditorPaintSelectionShow = !EditorPaintSelectionShow;
        if (EditorPaintSelectionUIContainer !is null)
            EditorPaintSelectionUIContainer.visible = EditorPaintSelectionShow;

        if (EditorPaintSelectionShow)
        {
            // When we start paint selection we change editmode to select
            editMode = EDIT_SELECT;
            //selectedNodes.Clear();
            // and also we show origins for proper origins update
            ShowOrigins(true);
            toolBarDirty = true;
        }
    }
    else if (EditorPaintSelectionShow && ui.focusElement is null)
    {
        if (editMode != EDIT_SELECT)
        {
            EditorPaintSelectionShow = false;
            EditorPaintSelectionUIContainer.visible = false;
        }
    }

    if (input.mouseButtonDown[MOUSEB_RIGHT])
    {
        EditorPaintSelectionShow = false;
        if (EditorPaintSelectionUIContainer !is null)
            EditorPaintSelectionUIContainer.visible = false;
    }
}

void SelectOriginsByPaintSelection(IntVector2 curPos, float brushRadius, bool isOperationAddToSelection = true)
{
    if (!EditorPaintSelectionShow || EditorPaintSelectionUIContainer is null) return;

    for (int i=0; i < originsNodes.length; i++)
    {
        Vector3 v1(originsIcons[i].position.x, originsIcons[i].position.y, 0);
        Vector3 v2(curPos.x - ORIGINOFFSETICON.x, curPos.y - ORIGINOFFSETICON.y, 0);

        float distance = (v1 - v2).length;
        bool isThisOriginInCircle = distance < brushRadius ? true : false;
        int nodeID = originsIcons[i].vars[ORIGIN_NODEID_VAR].GetInt();

        if (isThisOriginInCircle)
        {
            WeakHandle handle = editorScene.GetNode(nodeID);
            if (handle.Get() !is null)
            {
                Node@ node = handle.Get();
                if (isOperationAddToSelection)
                {
                    if (node !is null && isThisNodeOneOfSelected(node) == false)
                        SelectNode(node, true);
                }
                else // Deselect origins operation
                {
                    if (node !is null && isThisNodeOneOfSelected(node) == true)
                        DeselectNode(node);
                }
            }
        }
    }
}

void HandlePaintSelectionMouseMove(StringHash eventType, VariantMap& eventData)
{
    if (!EditorPaintSelectionShow || EditorPaintSelectionUIContainer is null) return;

    int x = eventData["X"].GetInt();
    int y = eventData["Y"].GetInt();
    float r = (paintSelectionBrushCurrentSize.x * 0.5);

    IntVector2 mousePos(x,y);
    // Select by mouse
    if (input.mouseButtonDown[MOUSEB_LEFT] && input.qualifierDown[QUAL_ALT] != true)
    {
        SelectOriginsByPaintSelection(mousePos, r, true);
    }
    // Deselect by mouse
    else if (input.mouseButtonDown[MOUSEB_LEFT] && input.qualifierDown[QUAL_ALT] == true)
    {
        SelectOriginsByPaintSelection(mousePos, r, false);
    }
}

void HandlePaintSelectionWheel(StringHash eventType, VariantMap& eventData)
{
    if (!EditorPaintSelectionShow || EditorPaintSelectionUIContainer is null) return;

    int wheelValue = eventData["Wheel"].GetInt();

    if (wheelValue != 0)
    {
        if (wheelValue > 0)
        {
            paintSelectionBrushCurrentSize = paintSelectionBrushCurrentSize - paintSelectionBrushStepSizeChange;
            paintSelectionBrushCurrentSize = IntVector2(Max(paintSelectionBrushCurrentSize.x, paintSelectionBrushMinSize.x), Max(paintSelectionBrushCurrentSize.y, paintSelectionBrushMinSize.y));
        }
        else if (wheelValue < 0)
        {
            paintSelectionBrushCurrentSize = paintSelectionBrushCurrentSize + paintSelectionBrushStepSizeChange;
            paintSelectionBrushCurrentSize = IntVector2(Min(paintSelectionBrushCurrentSize.x, paintSelectionBrushMaxSize.x), Min(paintSelectionBrushCurrentSize.y, paintSelectionBrushMaxSize.y));
        }
        paintSelectionImage.SetFixedSize(paintSelectionBrushCurrentSize.x, paintSelectionBrushCurrentSize.y);
    }
}

const int PAINT_STEP_UPDATE = 16;
const int PAINT_SELECTION_KEY = KEY_C;

bool EditorPaintSelectionShow = false;
int EditorPaintSelectionUITimeToUpdate = 0;

UIElement@ EditorPaintSelectionUIContainer = null;
BorderImage@ paintSelectionImage = null;

IntVector2 paintSelectionBrushDefaultSize(96,96);
IntVector2 paintSelectionBrushCurrentSize = paintSelectionBrushDefaultSize;
IntVector2 paintSelectionBrushMinSize(64,64);
IntVector2 paintSelectionBrushMaxSize(512,512);
IntVector2 paintSelectionBrushStepSizeChange(16,16);

void CreatePaintSelectionContainer()
{
    if (editorScene is null) return;
    EditorPaintSelectionUIContainer = UIElement();
    EditorPaintSelectionUIContainer.position = IntVector2(0,0);
    EditorPaintSelectionUIContainer.size = IntVector2(graphics.width,graphics.height);
    EditorPaintSelectionUIContainer.priority = -5;
    EditorPaintSelectionUIContainer.focusMode = FM_NOTFOCUSABLE;
    EditorPaintSelectionUIContainer.bringToBack = true;
    EditorPaintSelectionUIContainer.name ="DebugPaintSelectionContainer";
    EditorPaintSelectionUIContainer.temporary = true;
    ui.root.AddChild(EditorPaintSelectionUIContainer);
}

void CreatePaintSelectionTool()
{
    paintSelectionImage = BorderImage("Icon");
    paintSelectionImage.temporary = true;
    paintSelectionImage.SetFixedSize(paintSelectionBrushDefaultSize.x,paintSelectionBrushDefaultSize.y);
    paintSelectionImage.texture = cache.GetResource("Texture2D", "Textures/Editor/SelectionCircle.png");
    paintSelectionImage.imageRect = IntRect(0,0,512,512);
    paintSelectionImage.priority = -5;
    paintSelectionImage.color = Color(1,1,1);
    paintSelectionImage.bringToBack = true;
    paintSelectionImage.enabled = false;
    paintSelectionImage.selected = false;
    paintSelectionImage.visible = true;
    EditorPaintSelectionUIContainer.AddChild(paintSelectionImage);
}

void UpdatePaintSelection()
{
    PaintSelectionCheckKeyboard();

    // Early out if disabled
    if (!EditorPaintSelectionShow) return;

    if (editorScene is null || EditorPaintSelectionUITimeToUpdate > time.systemTime) return;

    EditorPaintSelectionUIContainer = ui.root.GetChild("DebugPaintSelectionContainer");

    if (EditorPaintSelectionUIContainer is null)
    {
        CreatePaintSelectionContainer();
        CreatePaintSelectionTool();
    }

    if (EditorPaintSelectionUIContainer !is null)
    {
        // Set visibility for all origins
        EditorPaintSelectionUIContainer.visible = EditorPaintSelectionShow;

        if (viewportMode!=VIEWPORT_SINGLE)
            EditorPaintSelectionUIContainer.visible = false;

        if (EditorPaintSelectionShow)
        {
            IntVector2 mp = input.mousePosition;
            paintSelectionImage.position = IntVector2(mp.x - (paintSelectionBrushCurrentSize.x * 0.5f), mp.y - (paintSelectionBrushCurrentSize.y * 0.5f));
        }
    }

    EditorPaintSelectionUITimeToUpdate = time.systemTime + PAINT_STEP_UPDATE;
}

void PaintSelectionCheckKeyboard()
{
    bool key = input.keyPress[PAINT_SELECTION_KEY];

    if (key && ui.focusElement is null)
    {
        EditorPaintSelectionShow = !EditorPaintSelectionShow;
        if (EditorPaintSelectionUIContainer !is null)
            EditorPaintSelectionUIContainer.visible = EditorPaintSelectionShow;

        if (EditorPaintSelectionShow)
        {
            // When we start paint selection we change editmode to select
            editMode = EDIT_SELECT;
            //selectedNodes.Clear();
            // and also we show origins for proper origins update
            ShowOrigins(true);
            toolBarDirty = true;
        }
    }
    else if (EditorPaintSelectionShow && ui.focusElement is null)
    {
        if (editMode != EDIT_SELECT)
        {
            EditorPaintSelectionShow = false;
            EditorPaintSelectionUIContainer.visible = false;
        }
    }

    if (input.mouseButtonDown[MOUSEB_RIGHT])
    {
        EditorPaintSelectionShow = false;
        if (EditorPaintSelectionUIContainer !is null)
            EditorPaintSelectionUIContainer.visible = false;
    }
}

void SelectOriginsByPaintSelection(IntVector2 curPos, float brushRadius, bool isOperationAddToSelection = true)
{
    if (!EditorPaintSelectionShow || EditorPaintSelectionUIContainer is null) return;

    for (int i=0; i < originsNodes.length; i++)
    {
        Vector3 v1(originsIcons[i].position.x, originsIcons[i].position.y, 0);
        Vector3 v2(curPos.x - ORIGINOFFSETICON.x, curPos.y - ORIGINOFFSETICON.y, 0);

        float distance = (v1 - v2).length;
        bool isThisOriginInCircle = distance < brushRadius ? true : false;
        int nodeID = originsIcons[i].vars[ORIGIN_NODEID_VAR].GetInt();

        if (isThisOriginInCircle)
        {
            WeakHandle handle = editorScene.GetNode(nodeID);
            if (handle.Get() !is null)
            {
                Node@ node = handle.Get();
                if (isOperationAddToSelection)
                {
                    if (node !is null && isThisNodeOneOfSelected(node) == false)
                        SelectNode(node, true);
                }
                else // Deselect origins operation
                {
                    if (node !is null && isThisNodeOneOfSelected(node) == true)
                        DeselectNode(node);
                }
            }
        }
    }
}

void HandlePaintSelectionMouseMove(StringHash eventType, VariantMap& eventData)
{
    if (!EditorPaintSelectionShow || EditorPaintSelectionUIContainer is null) return;

    int x = eventData["X"].GetInt();
    int y = eventData["Y"].GetInt();
    float r = (paintSelectionBrushCurrentSize.x * 0.5);

    IntVector2 mousePos(x,y);

    // Select by mouse
    if (input.mouseButtonDown[MOUSEB_LEFT] && input.qualifierDown[QUAL_CTRL] != true)
    {
        SelectOriginsByPaintSelection(mousePos, r, true);
    }
    // Deselect by mouse
    else if (input.mouseButtonDown[MOUSEB_LEFT] && input.qualifierDown[QUAL_CTRL] == true)
    {
        SelectOriginsByPaintSelection(mousePos, r, false);
    }
}

void HandlePaintSelectionWheel(StringHash eventType, VariantMap& eventData)
{
    if (!EditorPaintSelectionShow || EditorPaintSelectionUIContainer is null) return;

    int wheelValue = eventData["Wheel"].GetInt();

    if (wheelValue != 0)
    {
        if (wheelValue > 0)
        {
            paintSelectionBrushCurrentSize = paintSelectionBrushCurrentSize - paintSelectionBrushStepSizeChange;
            paintSelectionBrushCurrentSize = IntVector2(Max(paintSelectionBrushCurrentSize.x, paintSelectionBrushMinSize.x), Max(paintSelectionBrushCurrentSize.y, paintSelectionBrushMinSize.y));
        }
        else if (wheelValue < 0)
        {
            paintSelectionBrushCurrentSize = paintSelectionBrushCurrentSize + paintSelectionBrushStepSizeChange;
            paintSelectionBrushCurrentSize = IntVector2(Min(paintSelectionBrushCurrentSize.x, paintSelectionBrushMaxSize.x), Min(paintSelectionBrushCurrentSize.y, paintSelectionBrushMaxSize.y));
        }
        paintSelectionImage.SetFixedSize(paintSelectionBrushCurrentSize.x, paintSelectionBrushCurrentSize.y);
    }
}

Use Ctrl instead Alt for deselecting with PaintSelection


//
// Flump - Copyright 2012 Three Rings Design

package flump.xfl {

import flump.mold.LayerMold;

import com.threerings.util.XmlUtil;

public class XflLayer
{
    use namespace xflns;

    public static function parse (lib :XflLibrary, baseLocation :String, xml :XML, flipbook :Boolean) :LayerMold {
        const layer :LayerMold = new LayerMold();
        layer.name = XmlUtil.getStringAttr(xml, "name");
        layer.flipbook = flipbook;
        layer.location = baseLocation + ":" + layer.name
        for each (var frameEl :XML in xml.frames.DOMFrame) {
            layer.keyframes.push(XflKeyframe.parse(lib, layer.location, frameEl, flipbook));
        }
        if (layer.keyframes.length == 0) lib.addError(layer, ParseError.INFO, "No keyframes on layer");
        return layer;
    }

}
}

//
// Flump - Copyright 2012 Three Rings Design

package flump.xfl {

import flump.mold.KeyframeMold;
import flump.mold.LayerMold;

import com.threerings.util.XmlUtil;

public class XflLayer
{
    use namespace xflns;

    public static function parse (lib :XflLibrary, baseLocation :String, xml :XML, flipbook :Boolean) :LayerMold {
        const layer :LayerMold = new LayerMold();
        layer.name = XmlUtil.getStringAttr(xml, "name");
        layer.flipbook = flipbook;
        layer.location = baseLocation + ":" + layer.name
        for each (var frameEl :XML in xml.frames.DOMFrame) {
            layer.keyframes.push(XflKeyframe.parse(lib, layer.location, frameEl, flipbook));
        }
        if (layer.keyframes.length == 0) lib.addError(layer, ParseError.INFO, "No keyframes on layer");

        // normalize rotations, so that we always rotate the shortest distance between
        // two angles (we don't want to rotate more than Math.PI)
        // TODO: support keyframes with rotations >= TWO_PI
        for (var ii :int = 0; ii < layer.keyframes.length - 1; ++ii) {
            var kf :KeyframeMold = layer.keyframes[ii];
            var nextKf :KeyframeMold = layer.keyframes[ii+1];
            if (kf.rotation + Math.PI < nextKf.rotation) {
                nextKf.rotation -= Math.PI * 2;
            } else if (kf.rotation - Math.PI > nextKf.rotation) {
                nextKf.rotation += Math.PI * 2;
            }
        }
        return layer;
    }

}
}

normalize keyframe rotations

normalize keyframe rotations

We want to rotate the shortest distance between two angles (i.e.
rotation <= PI)


/**
 * Generated by Gas3 v2.2.0 (Granite Data Services).
 *
 * NOTE: this file is only generated if it does not exist. You may safely put
 * your custom code here.
 */

package org.openforis.collect.model.proxy {
	import mx.collections.ArrayCollection;
	import mx.collections.IList;

	import org.granite.collections.IMap;
	import org.openforis.collect.remoting.service.UpdateResponse;

    [Bindable]
    [RemoteClass(alias="org.openforis.collect.model.proxy.RecordProxy")]
    public class RecordProxy extends RecordProxyBase {

		private var _updated:Boolean = false;
		private var _saved:Boolean = false;

		private var validationResults:ValidationResultsProxy;

		public function getNode(id:int):NodeProxy {
			if(id == rootEntity.id) {
				return rootEntity;
			} else {
				return rootEntity.getNode(id);
			}
		}

		public function update(responses:IList):void {
			for each (var response:UpdateResponse in responses)	{
				processResponse(response);
			}
			_updated = true;
		}

		private function processResponse(response:UpdateResponse):void {
			var node:NodeProxy, oldNode:NodeProxy, parent:EntityProxy;
			if(response.createdNode != null) {
				node = response.createdNode;
				parent = getNode(node.parentId) as EntityProxy;
				parent.addChild(node);
			}
			if(response.deletedNodeId > 0) {
				node = getNode(response.deletedNodeId);
				if(node != null) {
					parent = getNode(node.parentId) as EntityProxy;
					parent.removeChild(node);
				}
			} else {
				node = getNode(response.nodeId);
				if(node is AttributeProxy) {
					var a:AttributeProxy = AttributeProxy(node);
					if(response.validationResults != null) {
						a.validationResults = response.validationResults;
					}
					if(response.updatedFieldValues != null) {
						var fieldIdxs:ArrayCollection = response.updatedFieldValues.keySet;
						for each (var i:int in fieldIdxs) {
							var f:FieldProxy = a.getField(i);
							f.value = response.updatedFieldValues.get(i);
						}
					}
				} else if(node is EntityProxy) {
					var e:EntityProxy = EntityProxy(node);
					if(response.maxCountValidation != null && response.maxCountValidation.length > 0) {
						e.updateChildrenMaxCountValiditationMap(response.maxCountValidation);
					}
					if(response.minCountValidation != null && response.minCountValidation.length > 0) {
						e.updateChildrenMinCountValiditationMap(response.minCountValidation);
					}
					if(response.relevant != null && response.relevant.length > 0) {
						e.updateChildrenRelevanceMap(response.relevant);
					}
					if(response.required != null && response.required.length > 0) {
						e.updateChildrenRequiredMap(response.required);
					}
				}
			}
		}

		public function get updated():Boolean {
			return _updated;
		}

		public function get saved():Boolean {
			return _saved;
		}

		public function set saved(value:Boolean):void {
			_saved = value;
		}

    }
}

/**
 * Generated by Gas3 v2.2.0 (Granite Data Services).
 *
 * NOTE: this file is only generated if it does not exist. You may safely put
 * your custom code here.
 */

package org.openforis.collect.model.proxy {
	import mx.collections.ArrayCollection;
	import mx.collections.IList;

	import org.granite.collections.IMap;
	import org.openforis.collect.remoting.service.UpdateResponse;

    [Bindable]
    [RemoteClass(alias="org.openforis.collect.model.proxy.RecordProxy")]
    public class RecordProxy extends RecordProxyBase {

		private var _updated:Boolean = false;
		private var _saved:Boolean = false;

		private var validationResults:ValidationResultsProxy;

		public function getNode(id:int):NodeProxy {
			if(id == rootEntity.id) {
				return rootEntity;
			} else {
				return rootEntity.getNode(id);
			}
		}

		public function update(responses:IList):void {
			for each (var response:UpdateResponse in responses)	{
				processResponse(response);
			}
			_updated = true;
		}

		private function processResponse(response:UpdateResponse):void {
			var node:NodeProxy, oldNode:NodeProxy, parent:EntityProxy;
			if(response.createdNode != null) {
				node = response.createdNode;
				parent = getNode(node.parentId) as EntityProxy;
				parent.addChild(node);
			}
			if(response.deletedNodeId > 0) {
				node = getNode(response.deletedNodeId);
				if(node != null) {
					parent = getNode(node.parentId) as EntityProxy;
					parent.removeChild(node);
				}
			} else {
				node = getNode(response.nodeId);
				if(node is AttributeProxy) {
					var a:AttributeProxy = AttributeProxy(node);
					if(response.validationResults != null) {
						a.validationResults = response.validationResults;
					}
					if(response.updatedFieldValues != null) {
						var fieldIdxs:ArrayCollection = response.updatedFieldValues.keySet;
						for each (var i:int in fieldIdxs) {
							var f:FieldProxy = a.getField(i);
							f.value = response.updatedFieldValues.get(i);
						}
						parent = getNode(node.parentId) as EntityProxy;
						parent.updateKeyText();
					}
				} else if(node is EntityProxy) {
					var e:EntityProxy = EntityProxy(node);
					if(response.maxCountValidation != null && response.maxCountValidation.length > 0) {
						e.updateChildrenMaxCountValiditationMap(response.maxCountValidation);
					}
					if(response.minCountValidation != null && response.minCountValidation.length > 0) {
						e.updateChildrenMinCountValiditationMap(response.minCountValidation);
					}
					if(response.relevant != null && response.relevant.length > 0) {
						e.updateChildrenRelevanceMap(response.relevant);
					}
					if(response.required != null && response.required.length > 0) {
						e.updateChildrenRequiredMap(response.required);
					}
				}
			}
		}

		public function get updated():Boolean {
			return _updated;
		}

		public function get saved():Boolean {
			return _saved;
		}

		public function set saved(value:Boolean):void {
			_saved = value;
		}

    }
}

Update rootEntityKey label when updating a key attribute