In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

forked from: forked from: MediaRSS City

/**
 * Copyright Chvatal_raco ( http://wonderfl.net/user/Chvatal_raco )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/YWYx
 */

// forked from Jenya's forked from: MediaRSS City
// forked from tencho's MediaRSS City
// forked from Event's Simple MediaRSS Viewer
/**
 * flickrで検索した画像が広告になります。
 * 広告をクリックするとちょっと大きい画像が見れます。
 */
package  {
    import com.bit101.components.*;
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.system.*;
    
    public class AdCity extends Sprite {
        private var _flickr:Flickr = new Flickr();
        private var _map:MapView = new MapView();
        private var _bg:Sprite;
        private var _fade:Sprite;
        private var _sky:Sprite;
        private var _mouseOverSprite:Sprite = new Sprite();
        private var _canvas:BitmapData = new BitmapData(Display.WIDTH, Display.HEIGHT, true, 0);
        private var _loader:ImageLoader = new ImageLoader();
        private var _viewer:Viewer = new Viewer();
        private var _loading:LoadingEffect;
        private var _drag:MouseDrag;
        private var _search:SearchDialog = new SearchDialog();
        private var _menu:TopMenu;
        private var _mousePos:Point = new Point();
        private var _cameraMng:CameraManager = new CameraManager();
        
        private const SCENE_SEARCH:int = 0;
        private const SCENE_NORMAL:int = 1;
        private const SCENE_ZOOM:int = 2;
        private var _scene:int = SCENE_SEARCH;
        
        public function AdCity() {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            
            Display.stage = stage;
            stage.frameRate = 30;
            stage.quality = StageQuality.LOW;
            Style.fontSize *= 2;
            Style.LABEL_TEXT = 0xFFFFFF;
            //画面構成
            _bg = Painter.createColorRect(2000, 2000, 0);
            _bg.x = (Display.WIDTH - _bg.width) / 2;
            _bg.y = (Display.HEIGHT - _bg.height) / 2;
            _sky = Painter.createColorRect(Display.WIDTH, Display.HEIGHT, 0x78B2F8);
            _sky.mouseEnabled = false;
            _loading = new LoadingEffect();
            _loading.x = (Display.WIDTH - _loading.width) / 2;
            _loading.y = (Display.HEIGHT - _loading.height) / 2;
            _menu = new TopMenu();
            _menu.hide();
            _fade = Painter.createColorRect(Display.WIDTH, Display.HEIGHT, 0);
            _fade.mouseEnabled = false;
            
            addChild(_bg);
            addChild(_sky);
            addChild(new Bitmap(_canvas));
            addChild(_viewer);
            addChild(_mouseOverSprite);
            addChild(_search);
            addChild(_menu);
            addChild(_fade);
            addChild(_loading);
            
            _menu.search.addEventListener(LabelButton.EVENT_CLICK, onClickSeach);
            _menu.camera.addEventListener(Event.CHANGE, onChangeCamera);
            _search.hide();
            //hitTestを有効にするためstageに配置しておく
            addChild(_map.container);
            _map.container.visible = false;
            //指マークに変えるためのボタン
            _mouseOverSprite.addChild(Painter.createColorRect(Display.WIDTH, Display.HEIGHT, 0, 0));
            _mouseOverSprite.buttonMode = true;
            _mouseOverSprite.mouseChildren = false;
            _mouseOverSprite.visible = false;
            
            _drag = new MouseDrag(_bg, 400, 400);
            _drag.speed.x = -0.5;
            _drag.speed.y = -0.5;
            
            //マップチップとかを読み込む
            _loader.addImage("http://assets.wonderfl.net/images/related_images/2/2f/2fca/2fca9c6a48ea9c729059dc8f7d4717283933b92d", "mapchip");
            _loader.addImage("http://assets.wonderfl.net/images/related_images/7/78/781f/781f056d44583c10d7b214cf2891e7258ad20520", "bldg");
            _loader.addImage("http://assets.wonderfl.net/images/related_images/9/91/9180/918077e79672d25963f77a8ff3f37a700574f2d4", "car");
            _loader.addImage("http://assets.wonderfl.net/images/related_images/6/67/67c9/67c9fe2e421ee0f5776ca969a38a0d3a9e3fdc4f", "ui");
            _loader.load(onLoadMaterial, onErrorMaterial, onProgressMaterial);
        }
        
        private function onProgressMaterial(per:Number):void {
            _loading.setProgress(per);
        }
        //メニューのSEARCHボタンを押した
        private function onClickSeach(e:Event):void {
            _scene = SCENE_SEARCH;
            _search.setSearch();
            setCamera(CameraManager.MODE_CAR);
            _menu.hide();
        }
        //カメラボタンで切り替えた
        private function onChangeCamera(e:Event):void {
            nextCamera();
        }
        //マップチップロード失敗
        private function onErrorMaterial():void {
            _loading.transform.colorTransform = new ColorTransform(0, 0, 0, 1, 0xFF, 0, 0, 0);
        }
        //全ての素材を読み込めた
        private function onLoadMaterial():void {
            EaseMotion.ease(_fade, "alpha", 0, 0.15, function():void { _fade.parent.removeChild(_fade); } );
            _loading.hide();
            _loading.parent.removeChild(_loading);
            _viewer.init(_loader.image.ui);
            _map.init(_loader);
            initCamera();
            onSimulateTick(null);
            _search.init("poster logo", onSearch);
            _flickr.loadLimit = 16;
            _flickr.isSkipFlash = true;
            _flickr.addEventListener(Flickr.EVENT_LOADIMAGE, onLoadImage);
            _flickr.addEventListener(Flickr.EVENT_NOIMAGE, onNoImage);
            _flickr.addEventListener(ErrorEvent.ERROR, onErrorRss);
            _flickr.addEventListener(Event.COMPLETE, onCompleteImage);
        }
        //検索実行時
        private function onSearch(tag:String):void{
            //flickrのRSSを取得
            _search.setMessage("LOADING... ");
            _flickr.load(tag);
        }
        private function onNoImage(e:Event):void {
            _search.errorMessage("NO IMAGE!");
        }
        private function onErrorRss(e:ErrorEvent):void {
            _search.errorMessage("ERROR!");
        }
        private function onLoadImage(e:Event):void {
            var per:Number = _flickr.loaded.length / _flickr.items.length * 100;
            _search.setMessage("LOADING... " + int(per) + "%");
        }
        //全てのサムネイルが読み込まれた
        private function onCompleteImage(e:Event):void {
            _scene = SCENE_NORMAL;
            _search.hide();
            _bg.addEventListener(MouseEvent.MOUSE_DOWN, onClickStage);
            _mouseOverSprite.addEventListener(MouseEvent.MOUSE_DOWN, onClickStage);
            _menu.show();
            var count:int = -1;
            if (_flickr.items.length > 0) {
                for each(var obj:Piece in _map.piece.objects) {
                    for each(var ad:Ad in obj.ads) {
                        count = (count + 1) % _flickr.items.length;
                        ad.setItem(_flickr.items[count]);
                    }
                }
            }
            startSim();
        }
        //背景クリック時
        private function onClickStage(e:MouseEvent):void {
            switch(_scene) {
                case SCENE_ZOOM:
                    startSim();
                    _viewer.close();
                    _menu.show();
                    _scene = SCENE_NORMAL;
                    break;
                case SCENE_NORMAL:
                    var ad:Ad = _map.piece.hitTest(_mousePos.x, _mousePos.y);
                    if (ad) {
                        stopSim();
                        _scene = SCENE_ZOOM;
                        _menu.hide();
                        _mouseOverSprite.visible = false;
                        _viewer.setItem(ad.item);
                    }
                    break;
            }
        }
        //カメラ初期化
        private function initCamera():void {
            var cam_car:CameraData = new CameraData();
            var cam_drag:CameraData = new CameraData();
            var cam_sky:CameraData = new CameraData();
            cam_car.scale = 3;
            cam_car.speed = 1;
            cam_car.update = function(me:CameraData):void {
                me.position.x = _map.myCar.x;
                me.position.y = _map.myCar.y  - 30;
            }
            cam_drag.scale = 2;
            cam_drag.speed = 0.7;
            cam_drag.update = function(me:CameraData):void {
                me.position.x = _drag.position.x;
                me.position.y = _drag.position.y;
            }
            cam_sky.scale = 0.5;
            cam_sky.speed = 1;
            var pp:Point = Quarter.toScreen(_map.data.W / 2, _map.data.H / 2, 10);
            cam_sky.position.x = pp.x;
            cam_sky.position.y = pp.y;
            
            _cameraMng.cameras[CameraManager.MODE_CAR] = cam_car;
            _cameraMng.cameras[CameraManager.MODE_DRAG] = cam_drag;
            _cameraMng.cameras[CameraManager.MODE_SKY] = cam_sky;
            setCamera(CameraManager.MODE_CAR);
            _cameraMng.goto(_cameraMng.activeCamera);
        }
        //カメラ切り替え
        private function nextCamera():void {
            setCamera(_cameraMng.getNextMode());
        }
        private function setCamera(mode:int):void {
            var prev:CameraData = _cameraMng.activeCamera;
            _cameraMng.setMode(mode);
            _menu.camera.setLabel(mode);
            if (_cameraMng.mode == CameraManager.MODE_DRAG) {
                _drag.position = prev.position.clone();
            }
        }
        public function startSim():void {
            addEventListener(Event.ENTER_FRAME, onSimulateTick);
        }
        public function stopSim():void {
            removeEventListener(Event.ENTER_FRAME, onSimulateTick);
        }
        private function onSimulateTick(e:Event):void {
            _map.piece.objects.sort(sortFunc);
            var l:int = _map.piece.objects.length;
            for (var i:int = 0; i < l; i++) {
                _map.objLayer.setChildIndex(_map.piece.objects[i], i);
            }
            _map.myCar.simulateTick();
            var ad:Ad = _map.piece.hitTest(_mousePos.x, _mousePos.y)
            _mouseOverSprite.visible = !!ad;
            for each(var c:Car in _map.cars) c.simulateTick();
            _cameraMng.ease(_cameraMng.activeCamera);
            var cp:Point = _cameraMng.getPosition();
            var scl:Number = _cameraMng.scale;
            _mousePos = new Point((cp.x + stage.mouseX) / scl, (cp.y + stage.mouseY) / scl);
            _canvas.fillRect(_canvas.rect, 0);
            _canvas.draw(_map.container, new Matrix(scl, 0, 0, scl, int( -cp.x), int( -cp.y)));
        }
        private function sortFunc(a:Piece, b:Piece):int{
            return (a.y + a.basePos.y) - (b.y + b.basePos.y);
        }
    }
}

import com.bit101.components.*;
import flash.display.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import flash.net.*;
import flash.system.*;
import flash.text.*;
import flash.ui.*;
import flash.utils.*;

/**
 * ローディング用ノイズバー
 */
class LoadingEffect extends Sprite {
    private var _img:BitmapData;
    private var _slide:int = 0;
    private var _zoomX:int = 8;
    private var _zoomY:int = 3;
    private var _loop:int = 100;
    private var _count:int = 0;
    private var _label:Label;
    public function LoadingEffect() {
        _img = new BitmapData(20, _loop, false, 0xFF00000000);
        _img.noise(1234, 0, 255, 7);
        _img.applyFilter(_img, _img.rect, new Point(), new BlurFilter(0, 2, 2));
        _label = new Label(this, 0, -15, "");
        _label.transform.colorTransform = new ColorTransform(0, 0, 0, 1, 0xA0, 0xA0, 0xA0, 0);
        _label.scaleX = _label.scaleY = 0.5;
        setProgress(0);
        show();
    }
    private function onEnterFrame(e:Event):void {
        if (++_count % 2) update();
    }
    public function setProgress(per:Number):void {
        _label.text = int(per * 100) + "%";
    }
    private function update():void {
        _slide = ++_slide % _loop;
        graphics.clear();
        graphics.beginBitmapFill(_img, new Matrix(_zoomX, 0, 0, _zoomY, 0, _slide * _zoomY), true, false);
        graphics.drawRect(0, 0, _img.width * _zoomX, _zoomY);
    }
    public function show():void {
        update();
        visible = true;
        addEventListener(Event.ENTER_FRAME, onEnterFrame);
    }
    public function hide():void {
        visible = false;
        removeEventListener(Event.ENTER_FRAME, onEnterFrame);
    }
}

/**
 * カメラの動き管理
 */
class CameraManager {
    public var activeCamera:CameraData;
    public var cameras:Vector.<CameraData> = new Vector.<CameraData>();
    public var position:Point = new Point();
    public var scale:Number = 1;
    public var mode:int = 0;
    private var _speed:Number = 0;
    private var _targetPos:Point = new Point();
    private var _targetScale:Number = 1;
    private var _targetSpeed:Number = 0.2;
    static public const MODE_CAR:int = 0;
    static public const MODE_DRAG:int = 1;
    static public const MODE_SKY:int = 2;
    public function CameraManager() {
    }
    public function getNextMode():int {
        return (mode + 1) % cameras.length;
    }
    public function setMode(mode:int):void {
        this.mode = mode;
        resetTrackingSpeed();
        activeCamera = cameras[mode];
    }
    public function resetTrackingSpeed():void {
        _speed = 0;
    }
    public function goto(cam:CameraData):void {
        ease(cam);
        position = _targetPos.clone();
        scale = _targetScale;
        _speed = _targetSpeed;
    }
    public function ease(cam:CameraData):void {
        cam.update(cam);
        _targetScale = cam.scale;
        _targetPos = cam.position.clone();
        _targetSpeed = cam.speed;
        update();
    }
    public function getPosition():Point {
        var p:Point = new Point();
        p.x = position.x * scale - Display.CENTER.x;
        p.y = position.y * scale - Display.CENTER.y;
        return p;
    }
    public function update():void {
        _speed += (_targetSpeed - _speed) * 0.03;
        position.x += (_targetPos.x - position.x) * _speed;
        position.y += (_targetPos.y - position.y) * _speed;
        scale += (_targetScale - scale) * _speed;
        if (Math.abs(_targetScale - scale) < 0.01) scale = _targetScale;
    }
}

/**
 * カメラデータ
 */
class CameraData {
    //画面位置
    public var position:Point = new Point();
    //画面スケール
    public var scale:Number = 1;
    //最終的な追尾速度
    public var speed:Number = 0.2;
    //毎フレーム実行する処理
    public var update:Function = function(me:CameraData):void { };
    public function CameraData() {
    }
}

/**
 * 上部メニュー
 */
class TopMenu extends Sprite {
    private var _base:Sprite;
    public var search:LabelButton;
    public var camera:CameraSwitch;
    private var _height:Number = 35;
    public function TopMenu() {
        _base = addChild(Painter.createColorRect(Display.WIDTH, _height, 0, 0.7)) as Sprite;
        search = new LabelButton(this, 8, 0, "SEARCH", 100);
        camera = new CameraSwitch(this, Display.WIDTH - 145, 0);
        vanish();
    }
    public function show():void {
        mouseEnabled = true;
        mouseChildren = true;
        EaseMotion.ease(this, "y", 0, 0.4);
        EaseMotion.ease(search, "alpha", 1, 0.2);
        EaseMotion.ease(camera, "alpha", 1, 0.2);
    }
    public function vanish():void {
        mouseEnabled = false;
        y = -_height;
        search.alpha = 0;
        camera.alpha = 0;
    }
    public function hide():void {
        mouseEnabled = false;
        mouseChildren = false;
        EaseMotion.ease(this, "y", -_height, 0.4);
        EaseMotion.ease(search, "alpha", 0, 0.2);
        EaseMotion.ease(camera, "alpha", 0, 0.2);
    }
}

/**
 * 検索窓
 */
class SearchDialog extends Sprite {
    private var _base:Sprite = new Sprite();
    private var _message:Label;
    private var _input:InputText;
    private var _searchLayer:Sprite = new Sprite();
    private var _searchFunc:Function;
    private var _timer:Timer = new Timer(2000, 1);
    public function SearchDialog() {
        _timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimesUp);
        addChild(_base);
        var imgBase:BitmapData = new BitmapData(170, 34, true, 0);
        var imgTri:BitmapData = new BitmapData(11, 11, true, 0);
        var i:int, px:int, py:int;
        var round:int = 3;
        for (i = 0; i < imgTri.width; i += 2) {
            imgTri.fillRect(new Rectangle(i, 0, 2, imgTri.height-i), 0xFFffffff);
        }
        for (i = 0; i < round; i++) {
            px = i * 2;
            py = (round - i -1) * 2;
            imgBase.fillRect(new Rectangle(px, py, imgBase.width - px * 2, imgBase.height - py * 2), 0xFFffffff);
        }
        var tri:Bitmap = _base.addChild(new Bitmap(imgTri)) as Bitmap;
        var base:Bitmap = _base.addChild(new Bitmap(imgBase)) as Bitmap;
        tri.y = -tri.height;
        base.y = -imgTri.height + 1 - imgBase.height;
        base.x = int( -imgBase.width / 2);
        addChild(_searchLayer);
        _input = new InputText(_searchLayer, -78, -35, "");
        new PushButton(_searchLayer, 28, -35, "SEARCH", onClickSearch).setSize(50, 16);
        _message = new Label(this, -34, -37, "");
        _message.visible = false;
        filters = [new DropShadowFilter(2, 45, 0, 0.5, 6, 6, 100, 1)];
        x = 230;
        y = 270;
    }
    public function init(text:String, onSearch:Function):void {
        _searchFunc = onSearch;
        setSearch();
        _input.text = text;
    }
    public function getKeyword():String {
        return _input.text.replace(/\s+|\/|\./g, ",");
    }
    public function setSearch():void {
        visible = true;
        EaseMotion.ease(this, "scaleX", 2, 0.6);
        EaseMotion.ease(this, "scaleY", 2, 0.6);
        _searchLayer.visible = true;
        _message.visible = false;
        stage.focus = _input.textField;
        stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
    }
    private function onKeyDown(e:KeyboardEvent):void {
        if (e.keyCode == Keyboard.ENTER) onClickSearch(null);
    }
    public function hide():void {
        visible = false;
        EaseMotion.goto(this, "scaleX", 0);
        EaseMotion.goto(this, "scaleY", 0);
    }
    public function errorMessage(text:String):void {
        setMessage(text);
        _timer.reset();
        _timer.start();
    }
    private function onTimesUp(e:TimerEvent):void {
        setSearch();
    }
    public function setMessage(text:String):void {
        _searchLayer.visible = false;
        _message.text = text;
        _message.visible = true;
    }
    private function onClickSearch(e:MouseEvent):void {
        stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
        if (_input.text.replace(/\s*/g, "") == "") return;
        if (_searchFunc != null)_searchFunc.apply(null, [getKeyword()]);
    }
}

/**
 * メニューのラベルボタン
 */
class LabelButton extends Label {
    static public const EVENT_CLICK:String = "onClickMe";
    private var _base:Sprite = new Sprite();
    private var _mainColor:ColorTransform = new ColorTransform();
    private var _overColor:ColorTransform = new ColorTransform();
    public function LabelButton(parent:DisplayObjectContainer, xpos:Number, ypos:Number, text:String = "", width:Number = 150) {
        super(parent, xpos, ypos, text);
        _mainColor.color = 0xFFFFFF;
        _overColor.color = 0xFFFF63;
        _base.addChild(Painter.createColorRect(width, 30, 0, 0));
        _base.addEventListener(MouseEvent.ROLL_OVER, onOver);
        _base.addEventListener(MouseEvent.ROLL_OUT, onOut);
        _base.addEventListener(MouseEvent.CLICK, onClick);
        _base.buttonMode = true;
        _base.x = -4;
        _base.y = 3;
        mouseEnabled = true;
        mouseChildren = true;
        addChildAt(_base, 0);
    }
    private function onOut(e:MouseEvent):void {
        textField.transform.colorTransform = _mainColor;
    }
    private function onOver(e:MouseEvent):void {
        textField.transform.colorTransform = _overColor;
    }
    protected function onClick(e:MouseEvent):void {
        dispatchEvent(new Event(EVENT_CLICK));
    }
}

/**
 * カメラ切り替えボタン
 */
class CameraSwitch extends LabelButton {
    public function CameraSwitch(parent:DisplayObjectContainer, xpos:Number, ypos:Number) {
        super(parent, xpos, ypos);
    }
    override protected function onClick(e:MouseEvent):void {
        dispatchEvent(new Event(Event.CHANGE));
        super.onClick(e);
    }
    public function setLabel(mode:int):void {
        text = "CAMERA: " + ["  CAR", "DRAG", "  SKY"][mode];
    }
}

/**
 * マウスドラッグ管理
 */
class MouseDrag extends EventDispatcher {
    public var position:Point = new Point();
    public var speed:Point = new Point(1, 1);
    public var clickRange:Number = 2;
    private var _sprite:DisplayObjectContainer;
    private var _isDragged:Boolean = false;
    private var _isMouseDown:Boolean = false;
    private var _savePosition:Point = new Point();
    private var _saveMousePos:Point;
    static public const EVENT_STARTDRAG:String = "onStartDrag";
    public function get isMouseDown():Boolean { return _isMouseDown; }
    public function get isDragged():Boolean { return _isDragged; }
    public function MouseDrag(target:DisplayObjectContainer, x:Number = 0, y:Number = 0) {
        position.x = x;
        position.y = y;
        _sprite = target;
        setEnabled(true);
    }
    public function setEnabled(enabled:Boolean):void {
        if (enabled) {
            _sprite.addEventListener(MouseEvent.MOUSE_DOWN, onMsDown);
        } else {
            _sprite.removeEventListener(MouseEvent.MOUSE_DOWN, onMsDown);
        }
    }
    private function onMsDown(e:MouseEvent):void {
        _isMouseDown = true;
        _isDragged = false;
        _sprite.stage.addEventListener(MouseEvent.MOUSE_UP, onMsUp);
        _sprite.stage.addEventListener(Event.MOUSE_LEAVE, onMsUp);
        _sprite.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMsMove);
        _savePosition = position.clone();
        _saveMousePos = new Point(_sprite.mouseX, _sprite.mouseY);
        dispatchEvent(new MouseEvent(MouseEvent.MOUSE_DOWN));
    }
    private function onMsUp(...rest):void {
        _isMouseDown = false;
        _sprite.stage.removeEventListener(MouseEvent.MOUSE_UP, onMsUp);
        _sprite.stage.removeEventListener(Event.MOUSE_LEAVE, onMsUp);
        _sprite.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMsMove);
        checkDrag();
        dispatchEvent(new MouseEvent(MouseEvent.MOUSE_UP));
    }
    private function onMsMove(e:MouseEvent):void {
        checkDrag();
    }
    private function checkDrag():void {
        var drag:Point = new Point(_sprite.mouseX, _sprite.mouseY).subtract(_saveMousePos);
        if (!_isDragged && drag.length > clickRange) {
            dispatchEvent(new Event(EVENT_STARTDRAG));
            _isDragged = true;
        }
        if (_isDragged) {
            position.x = _savePosition.x + drag.x * speed.x;
            position.y = _savePosition.y + drag.y * speed.y;
            dispatchEvent(new MouseEvent(MouseEvent.MOUSE_MOVE));
        }
    }
}

/**
 * 拡大画像ビューワ
 */
class Viewer extends Sprite {
    private var _viewRect:Rectangle = new Rectangle(27, 45, 411, 330);
    private var _bg:Sprite;
    private var _bmp:Bitmap;
    private var _slideLayer:Sprite = new Sprite();
    private var _imageContainer:Sprite = new Sprite();
    private var _loader:ImageLoader = new ImageLoader();
    private var _title:Label;
    private var _loading:LoadingEffect;
    private var _errorLabel:Label;
    public function Viewer() {
        mouseEnabled = false;
        mouseChildren = false;
    }
    /**
     * フレーム画像を渡して初期化
     */
    public function init(bmp:BitmapData):void {
        _bg = Painter.createColorRect(Display.WIDTH, Display.HEIGHT, 0xFFFFFF);
        _bg.alpha = 0;
        _bg.visible = false;
        _bg.blendMode = BlendMode.ADD;
        addChild(_bg);
        addChild(_slideLayer);
        _loading = new LoadingEffect();
        _loading.x = (Display.WIDTH - _loading.width) / 2;
        _loading.y = 215;
        _slideLayer.addChild(new Bitmap(bmp));
        _slideLayer.addChild(_loading);
        _slideLayer.addChild(_imageContainer);
        _title = new Label(_slideLayer, 30, 420);
        _title.x = 30;
        _title.width = 200;
        _title.height = 50;
        _title.scrollRect = new Rectangle(0, 0, 400, 100);
        _errorLabel = new Label(_slideLayer, 130, 180, "");
        _errorLabel.visible = false;
        _imageContainer.mask = addChild(Painter.createColorRect(_viewRect.width, _viewRect.height, 0, 1, _viewRect.x, _viewRect.y));
        mask = addChild(Painter.createColorRect(Display.WIDTH, Display.HEIGHT, 0));
        _slideLayer.y = Display.HEIGHT;
    }
    public function dispose():void {
        if(_bmp) _bmp.bitmapData.dispose();
        while (_imageContainer.numChildren) _imageContainer.removeChildAt(0);
        _loader.stop();
        _loader.disposeAll();
    }
    public function setItem(item:MediaItem):void {
        dispose();
        EaseMotion.goto(_loading, "alpha", 0);
        _imageContainer.alpha = 0;
        EaseMotion.ease(_slideLayer, "y", 0, 0.4, loadImage, [item]);
        EaseMotion.ease(_bg, "alpha", 0.5, 0.25);
        _bg.visible = true;
        _title.text = item.authorName +" - "+ item.title;
    }
    private function loadImage(item:MediaItem):void {
        _loading.show();
        _loading.setProgress(0);
        EaseMotion.ease(_loading, "alpha", 1);
        _loader.resetItem();
        _loader.addImage(item.content, "image");
        _loader.load(onLoadImage, null, onLoading);
    }
    private function onLoading(per:Number):void {
        _loading.setProgress(per);
    }
    private function onLoadImage():void {
        if (!(_loader.image.image is BitmapData)) return;
        var img:BitmapData = _loader.image.image;
        EaseMotion.ease(_loading, "alpha", 0, 0.2, function():void { _loading.hide(); } );
        //画像が大きすぎたらエラー
        if (img.width > 8192 || img.height > 8192 || img.width * img.height > 16777216) {
            _errorLabel.text = "ERROR: Image too large\n(" + img.width + " X " + img.height + " px)";
            _errorLabel.visible = true;
            return;
        }
        var bmpTemp:Bitmap = new Bitmap(img, "auto", true);
        var rect:Rectangle = RectUtil.adjustRect(img.rect, _viewRect, RectUtil.FULL, false);
        var scaleW:Number = rect.width / img.width;
        var scaleH:Number = rect.height / img.height;
        var img2:BitmapData = new BitmapData(rect.width, rect.height, false, 0xFF000000);
        
        //一時的に最高画質でキャプチャ
        stage.quality = StageQuality.BEST;
        img2.draw(bmpTemp, new Matrix(scaleW, 0, 0, scaleH), null, null, null, true);
        stage.quality = StageQuality.LOW;
        
        _loader.disposeAll();
        _bmp = _imageContainer.addChild(new Bitmap(img2)) as Bitmap;
        _bmp.x = _viewRect.x + (_viewRect.width - _bmp.width) * 0.5;
        _bmp.y = _viewRect.y + (_viewRect.height - _bmp.height) * 0.5;
        stage.addEventListener(MouseEvent.MOUSE_MOVE, onMsMove);
        onMsMove(null);
        EaseMotion.ease(_imageContainer, "alpha", 1);
    }
    private function onMsMove(e:MouseEvent):void {
        if (!_bmp) return;
        var centerX:Number = _viewRect.left + _viewRect.right / 2;
        var centerY:Number = _viewRect.top + _viewRect.bottom / 2;
        var perX:Number = (_bmp.width < _viewRect.width)? 0.5 : (stage.mouseX - centerX) / Display.WIDTH * 2 + 0.5;
        var perY:Number = (_bmp.height < _viewRect.height)? 0.5 : (stage.mouseY - centerY) / Display.HEIGHT * 2 + 0.5;
        perX = Math.max(0, Math.min(1, perX));
        perY = Math.max(0, Math.min(1, perY));
        EaseMotion.ease(_bmp, "x", _viewRect.x + (_viewRect.width - _bmp.width) * perX, 0.3);
        EaseMotion.ease(_bmp, "y", _viewRect.y + (_viewRect.height - _bmp.height) * perY, 0.3);
    }
    public function close():void {
        stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMsMove);
        dispose();
        _errorLabel.visible = false;
        EaseMotion.ease(_slideLayer, "y", 465, 0.4, function():void { } );
        EaseMotion.ease(_bg, "alpha", 0, 0.25, function():void { _bg.visible = false; } );
    }
}

class RectUtil {
    static public const FULL:String = "full";
    static public const AUTO:String = "auto";
    static public const FIT:String = "fit";
    static public const NONE:String = "none";
    static public function adjustRect(target:Rectangle, frame:Rectangle, mode:String = AUTO, isExpand:Boolean = true):Rectangle {
        var asp1:Number = frame.width / frame.height;
        var asp2:Number = target.width / target.height;
        var resize:Rectangle = new Rectangle();
        if (mode == FIT) {
            resize = frame.clone();
        } else if (mode == NONE) {
            resize = target.clone();
        } else if ((mode == AUTO && asp1 < asp2) || (mode == FULL && asp1 > asp2)) {
            resize.width = (!isExpand && target.width < frame.width)? target.width : frame.width;
            resize.height = resize.width / asp2;
        } else {
            resize.height = (!isExpand && target.height < frame.height)? target.height : frame.height;
            resize.width = resize.height * asp2;
        }
        return resize;
    }
}

/**
 * マップ上に配置するオブジェクト
 */
class Piece extends Sprite {
    public var bmp:Bitmap;
    public var image:BitmapData;
    public var w:int;
    public var h:int;
    public var materialID:int = -1;
    public var offsetX:int = 0;
    public var offsetY:int = 0;
    public var ads:Vector.<Ad> = new Vector.<Ad>();
    public var basePos:Point = new Point();
    protected var _px:Number;
    protected var _py:Number;
    protected var _pz:Number;
    public function Piece(img:BitmapData, sizeW:int = 2, sizeH:int = 2) {
        image = img;
        bmp = addChild(new Bitmap(image)) as Bitmap;
        w = sizeW;
        h = sizeH;
        var min:Number = Math.min(w, h) / 2
        basePos = (w >= h)? Quarter.toLocalScreen(w - min, h / 2, 0) : Quarter.toLocalScreen(w / 2, h - min, 0);
        bmp.x = h * -22;
        bmp.y = -bmp.height + (w + h) * 11;
    }
    public function setPosition(px:Number, py:Number, pz:Number):void {
        _px = px;
        _py = py;
        _pz = pz;
        update();
    }
    public function update():void {
        var p:Point = Quarter.toScreen(_px, _py, _pz);
        x = p.x + offsetX;
        y = p.y + offsetY;
    }
    public function addAd(px:Number, py:Number, width:Number, height:Number, skew:Number = -0.5):void {
        var ad:Ad = new Ad(width, height, skew);
        ad.x = bmp.x + px;
        ad.y = bmp.y + py;
        addChild(ad);
        ads.push(ad);
    }
}

/**
 * 車
 */
class Car extends Piece {
    private var _size:Rectangle = new Rectangle(0, 0, 21, 18);
    private var _carImage:BitmapData;
    private var _snapPos:Point = new Point();
    private var _map:MapView;
    //速度
    public var speed:Number = 0.05;
    //現在の方向
    private var _direction:int = 0;
    //次に曲がる方向
    private var _nextDirection:int = -1;
    public var route:Array = [];
    private var _routCount:int = -1;
    public function Car(img:BitmapData, xx:int, yy:int, map:MapView) {
        _map = map;
        _carImage = img;
        super(new BitmapData(_size.width, _size.height, true, 0), 1, 1);
        _snapPos.x = xx;
        _snapPos.y = yy;
        setPosition(xx, yy, 2);
        bmp.x += 10;
        bmp.y += -15;
        basePos.x = 0;
        basePos.y = 0;
        setDirection(_direction);
    }
    //道路に沿うように調整
    private function adjustPos():void {
        if (_direction == 0) _px = _snapPos.x + 0.25;
        else if (_direction == 2) _px = _snapPos.x + 0.75;
        else if (_direction == 1) _py = _snapPos.y + 0.25;
        else if (_direction == 3) _py = _snapPos.y + 0.75;
    }
    //前方のベクトルを取得
    private function getVector(dir:int):Point {
        var px:int = [0, 1, 0, -1][dir];
        var py:int = [ -1, 0, 1, 0][dir];
        return new Point(px, py);
    }
    //前方のタイルの座標を調べる
    private function getNextPos():Point {
        return _snapPos.add(getVector(_direction));
    }
    //次にどっちに曲がるか調べる
    private function checkNextPath():void {
        var p:Point = getNextPos();
        var path:Array = [];
        //前方のタイルからマップ外に続く進路を削る
        for each(var dir:int in _map.data.roadPath[_map.toIndex(p.x, p.y)]) {
            var p2:Point = getVector(dir).add(p);
            var num:int = _map.toIndex(p2.x, p2.y);
            if (num >= 0 && num < _map.data.W * _map.data.H) path.push(dir); 
        }
        //自分と逆方向の進路を削る
        if (path.length >= 2) {
            var ngPath:int = (_direction + 2) % 4;
            var index:int = path.indexOf(ngPath);
            if (index != -1) path.splice(index, 1);
        }
        var routeDir:int = -1;
        if (route.length && path.length >= 2) {
            _routCount = (_routCount + 1) % route.length;
            routeDir = route[_routCount];
        }
        _nextDirection = (!path.length)? -1 : (routeDir != -1)? routeDir : path[Math.floor(Math.random() * path.length)];
        if (_direction == _nextDirection) _nextDirection = -1;
    }
    /**
     * 方向設定
     */
    public function setDirection(dir:int):void {
        _direction = dir;
        image.copyPixels(_carImage, new Rectangle(_size.width * dir, 0, _size.width, _size.height), new Point());
    }
    /**
     * 動かす
     */
    public function simulateTick():void {
        var vec:Point = getVector(_direction);
        var nextTile:Point = getNextPos();
        if (_nextDirection != -1) {
            if (_nextDirection == 0) {
                if (_direction == 1 && _px > nextTile.x + 0.25) nextPath();
                else if (_direction == 3 && _px < nextTile.x + 0.25) nextPath();
                else if (_direction == 2 && _py > nextTile.y + 0.25) nextPath();
            } else if (_nextDirection == 2) {
                if (_direction == 1 && _px > nextTile.x + 0.75) nextPath();
                else if (_direction == 3 && _px < nextTile.x + 0.75) nextPath();
                else if (_direction == 0 && _py < nextTile.y + 0.75) nextPath();
            } else if (_nextDirection == 1) {
                if (_direction == 0 && _py < nextTile.y + 0.25) nextPath();
                else if (_direction == 2 && _py > nextTile.y + 0.25) nextPath();
                else if (_direction == 3 && _px < nextTile.x + 0.25) nextPath();
            } else if (_nextDirection == 3) {
                if (_direction == 0 && _py < nextTile.y + 0.75) nextPath();
                else if (_direction == 2 && _py > nextTile.y + 0.75) nextPath();
                else if (_direction == 1 && _px > nextTile.x + 0.75) nextPath();
            }
        } else {
            nextPath();
        }
        _px += vec.x * speed;
        _py += vec.y * speed;
        _pz = _map.getAltitude(_px, _py);
        rotation = _slopeRotation[_map.getSlope(_px, _py)];
        adjustPos();
        update();
    }
    private var _slopeRotation:Array = [0, -20, -20, 20, 20];
    private function nextPath():void {
        _snapPos = getNextPos();
        if(_nextDirection != -1) setDirection(_nextDirection);
        adjustPos();
        checkNextPath();
    }
}

/**
 * 色変換
 */
class ColorMatrix {
    static private const _LUM_R:Number = 0.212671;
    static private const _LUM_G:Number = 0.715160;
    static private const _LUM_B:Number = 0.072169;
    /**
     * 色相変化フィルター用配列を取得
     */
    static public function getHueArray(num:Number):Array {
        var rad:Number = Math.PI * num;
        var cos:Number = Math.cos(rad);
        var sin:Number = Math.sin(rad);
        var rNum:Number = 1 - cos - sin;
        var gNum:Number = 1 - cos;
        var bNum:Number = 1 - cos + sin;
        var r1:Number = _LUM_R * rNum + cos;
        var r2:Number = _LUM_G * rNum;
        var r3:Number = _LUM_B * rNum + sin;
        var g1:Number = _LUM_R * gNum + sin * 0.143;
        var g2:Number = _LUM_G * gNum + sin * 0.140 + cos;
        var g3:Number = _LUM_B * gNum - sin * 0.283;
        var b1:Number = _LUM_R * bNum - sin;
        var b2:Number = _LUM_G * bNum;
        var b3:Number = _LUM_B * bNum + cos;
        return [r1, r2, r3, 0, 0, g1, g2, g3, 0, 0, b1, b2, b3, 0, 0, 0, 0, 0, 1, 0];
    }
}

/**
 * 広告
 */
class Ad extends Sprite{
    public var item:MediaItem;
    private var _mask:Sprite;
    private var _container:Sprite = new Sprite();
    private var _size:Rectangle = new Rectangle();
    public function Ad(width:Number, height:Number, skew:Number = -0.5) {
        _mask = Painter.createColorRect(width, height);
        addChild(_container);
        addChild(_mask);
        _size.width = width;
        _size.height = height;
        _container.mask = _mask;
        _container.blendMode = BlendMode.ADD;
        transform.matrix = new Matrix(1, skew, 0, 1);
    }
    public function setItem(item:MediaItem):void {
        this.item = item;
        while (_container.numChildren) _container.removeChildAt(0);
        var bmp:Bitmap = _container.addChild(new Bitmap(item.image, "auto")) as Bitmap;
        var rect:Rectangle = RectUtil.adjustRect(bmp.bitmapData.rect, _size, RectUtil.FULL);
        bmp.width = rect.width;
        bmp.height = rect.height;
        bmp.x = (_size.width - rect.width) / 2;
        bmp.y = (_size.height - rect.height) / 2;
    }
}

/**
 * マップデータ
 */
class MapData {
    public const W:int = 25;
    public const H:int = 25;
    //水面の高さ
    public const WATER_LEVEL:int = 2;
    //道路の最低高度
    public const ROAD_LEVEL:int = 3;
    public var roadPath:Vector.<Array> = new Vector.<Array>();
    //地形の高さ
    public var level:Array = [
        [8,7,6,6,5,5,5,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,2,1],
        [5,6,5,5,4,5,4,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,2,1],
        [4,5,5,4,4,4,4,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,2,1],
        [4,4,4,4,4,4,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,2,1],
        [3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
        [3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
        [3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
        [3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
        [3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
        [3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
        [3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
        [3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1],
        [4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1],
        [3,4,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1],
        [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
        [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
        [1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2],
        [1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
        [1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
        [1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
        [1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
        [1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
        [1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
        [1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
        [1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3]
    ];
    //地形の傾き
    public var slope:Array = [
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0],
        [0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,4,0],
        [0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
        [0,0,2,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],
        [0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
    ];
    /**
     * 地面の種類
     * 0:地面
     * 1:道路
     * 2:草
     * 3:アスファルト
     * 4:砂
     * 5:木
     * 6:電話ボックス
     * 7:丸い植物
     * 8:排水管(斜面専用)
     */
    public var material:Array = [
        [2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,6,7,5,5,5,0,0,0],
        [2,2,2,2,2,2,2,0,0,1,1,1,1,1,1,1,1,1,1,1,1,5,0,0,0],
        [2,2,2,2,2,2,4,0,0,1,0,0,0,0,1,0,0,0,0,5,1,0,0,0,0],
        [2,2,2,2,2,4,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,5,8,0,0],
        [2,2,2,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0],
        [0,0,0,0,0,0,0,0,5,1,0,0,0,0,1,0,0,0,0,0,1,5,0,0,0],
        [0,0,7,1,1,1,1,1,1,1,0,0,0,0,1,5,5,5,5,7,1,5,0,0,0],
        [0,0,5,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1],
        [0,0,7,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
        [0,0,0,1,0,0,0,0,0,1,5,0,0,5,1,0,0,0,0,1,0,0,8,0,0],
        [0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,0,0,0,0],
        [0,0,7,0,0,6,5,5,5,1,5,0,6,5,1,5,0,0,5,1,5,0,0,0,0],
        [1,1,5,0,8,0,0,0,0,1,0,8,0,0,1,0,8,0,0,1,0,0,0,0,0],
        [0,1,5,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
        [0,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
        [0,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
        [1,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
        [0,0,0,0,5,0,0,0,0,1,0,0,0,0,1,5,6,7,7,1,0,0,0,0,5],
        [0,0,0,0,5,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,5],
        [0,0,0,0,5,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,5],
        [0,0,0,0,5,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,3,3,3,3,5],
        [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,5,0,0,6,5],
        [0,0,0,0,5,5,5,5,0,0,0,0,0,2,1,1,1,1,1,1,1,1,1,1,1],
        [0,0,0,0,0,0,5,5,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2]
    ];
    public function MapData() {
        //二重配列の前後にダミーのデータを入れる
        var a:Array, i:int;
        level.unshift(level[0].concat());
        level.push(level[level.length - 1].concat());
        for each(a in level) {
            a.unshift(a[0]);
            a.push(a[a.length - 1]);
        }
        a.length = 0;
        for (i = 0; i < slope[0].length; i++) a.push(0);
        slope.unshift(a);
        slope.push(a.concat());
        for each(a in slope) {
            a.unshift(0);
            a.push(0);
        }
        material.unshift(material[0].concat());
        material.push(material[material.length - 1].concat());
        for each(a in material) {
            a.unshift(a[0]);
            a.push(a[a.length - 1]);
        }
        //
        var l:int = W * H;
        for (i = 0; i < l; i++) roadPath[i] = [];
    }
}

/**
 * オブジェクト管理
 */
class PieceMaker {
    private var _rawImage:BitmapData;
    public var image:Object = { };
    public var objects:Vector.<Piece> = new Vector.<Piece>();
    public function PieceMaker(img:BitmapData) {
        _rawImage = img;
    }
    public function hitTest(x:Number, y:Number):Ad {
        for each(var obj:Piece in objects) {
            for each(var ad:Ad in obj.ads) {
                if (ad.hitTestPoint(x, y, true)) return ad;
            }
        }
        return null;
    }
    public function newImage(x:int, y:int, width:int, height:int):BitmapData {
        var img:BitmapData = new BitmapData(width, height, true, 0);
        img.copyPixels(_rawImage, new Rectangle(x, y, width, height), new Point(), null, null, true);
        return img;
    }
    public function createPiece(name:String, x:int, y:int, width:int, height:int, sizeW:int = 2, sizeH:int = 2, matID:int = -1):Piece {
        return newPiece(name, newImage(x, y, width, height), sizeW, sizeH, matID);
    }
    public function newPiece(name:String, image:BitmapData, sizeW:int = 2, sizeH:int = 2, matID:int = -1):Piece {
        var obj:Piece = new Piece(image, sizeW, sizeH);
        obj.name = name;
        obj.materialID = matID;
        objects.push(obj);
        return obj;
    }
    public function addPiece(p:Piece):Piece {
        objects.push(p);
        return p;
    }
    public function getPiece(name:String):Piece {
        for each(var p:Piece in objects) {
            if (name == p.name) return p;
        }
        return null;
    }
    public function addChildAll(target:DisplayObjectContainer):void {
        for each(var p:Piece in objects) target.addChild(p);
    }
}

/**
 * マップ管理
 */
class MapView {
    public var container:Sprite = new Sprite();
    public var objLayer:Sprite = new Sprite();
    public var canvas:BitmapData;
    public var waterSection:BitmapData;
    public var chip:MapChip = new MapChip();
    public var data:MapData = new MapData();
    public var piece:PieceMaker;
    public var myCar:Car;
    public var cars:Vector.<Car> = new Vector.<Car>();
    public function MapView() {
        canvas = new BitmapData(1100, 865, true, 0);
        waterSection = new BitmapData(canvas.width, canvas.height, false, 0xFF000000);
        container.addChild(new Bitmap(canvas));
        container.addChild(objLayer);
    }
    private function transparent(img:BitmapData):BitmapData {
        var temp:BitmapData = new BitmapData(img.width, img.height, true, 0);
        temp.copyPixels(img, img.rect, new Point());
        temp.threshold(temp, temp.rect, new Point(), "==", 0xFF << 24 | chip.TRANSPARENT1, 0x00FFFFFF);
        temp.threshold(temp, temp.rect, new Point(), "==", 0xFF << 24 | chip.TRANSPARENT2, 0x00FFFFFF);
        return temp;
    }
    public function init(loader:ImageLoader):void {
        //マップチップ切り出し開始
        var ix:int, iy:int, i:int, k:String, img:BitmapData;
        var bmp:BitmapData = transparent(loader.image.mapchip);
        var bmp2:BitmapData = transparent(loader.image.bldg);
        var bmp3:BitmapData = transparent(loader.image.car);
        loader.image.bldg = bmp2;
        var images:Vector.<BitmapData> = new Vector.<BitmapData>();
        for (i = 0; i < 60 ; i++) {
            ix = i % 10;
            iy = int(i / 10);
            img = chip.newBitmap();
            img.copyPixels(bmp, new Rectangle(ix * chip.W, iy * chip.H, chip.W, chip.H), new Point());
            images.push(img);
        }
        //地形
        chip.terrain = images.splice(0, 15);
        //断面
        for each(k in ["L", "R"]) chip["section" + k] = images.shift();
        chip.sections = images.splice(0, 4);
        //道路
        chip.road = images.splice(0, 5);
        //歩道
        for each(k in ["TLI", "TRI", "BRI", "BLI", "TLO", "TRO", "BRO", "BLO", "T", "R", "B", "L"]) chip.sidewalk[k] = images.shift();
        //橋のフェンスと柱
        for each(k in ["PN", "PW1", "PW2", "base", "T", "R", "B", "L"]) chip.bridge[k] = images.shift();
        //白線
        for each(k in ["T", "R", "B", "L"]) chip.centerline[k] = images.shift();
        //横断歩道
        for each(k in ["X", "Y"]) chip["crosswalk" + k] = images.shift();
        for each(k in ["Grass", "Asphalt", "Sand"]) chip["ground" + k] = images.shift();
        
        chip.phoneBooth = images.shift();
        chip.grassBall = images.shift();
        images.shift();
        chip.drainpipe.B = images.shift();
        chip.drainpipe.R = images.shift();
        
        
        //オブジェクト
        piece = new PieceMaker(loader.image.bldg);
        piece.createPiece("bldg1", 0, 183, 108, 150, 2, 3, -1);
        piece.createPiece("bldg2", 230, 0, 86, 163, 2, 2, -1);
        piece.createPiece("bldg3", 0, 0, 121, 178, 3, 3, -1);
        piece.createPiece("bldg4", 121, 0, 109, 168, 2, 3, -1);
        piece.createPiece("bldg5", 316, 0, 132, 220, 4, 2, -1);
        piece.createPiece("house1", 174, 356, 174, 94, 4, 4, 2);
        piece.createPiece("house2", 0, 358, 174, 92, 4, 4, 2);
        piece.createPiece("conveni", 112, 189, 135, 84, 4, 3, 3);
        piece.createPiece("park", 110, 274, 112, 76, 4, 2, 2);
        piece.createPiece("busstopV", 255, 227, 56, 49, 1, 2, -1);
        piece.createPiece("busstopH", 319, 227, 60, 49, 2, 1, -1);
        piece.createPiece("adsignV", 383, 229, 61, 69, 1, 2, -1);
        piece.createPiece("adsignH", 444, 229, 53, 69, 2, 1, -1);
        piece.createPiece("parking", 348, 313, 152, 112, 4, 3, 3);
        piece.image.tree = piece.newImage(232, 313, 42, 37);
        
        piece.getPiece("bldg1").addAd(19, 40, 40, 34, 0.5);
        piece.getPiece("bldg1").addAd(88, 280 - 183, 20, 30, -0.5);
        piece.getPiece("bldg2").addAd(15, 16, 27, 28, 0.5);
        piece.getPiece("bldg2").addAd(44, 29, 27, 28, -0.5);
        piece.getPiece("bldg3").addAd(56, 51, 29, 61, -0.5);
        piece.getPiece("bldg3").addAd(4, 9, 31, 21, 0.5);
        piece.getPiece("bldg4").addAd(59, 66, 44, 46, -0.5);
        piece.getPiece("bldg5").addAd(11, 81, 22, 45, 0.5);
        piece.getPiece("bldg5").addAd(11, 129, 22, 45, 0.5);
        piece.getPiece("bldg5").addAd(69, 110, 40, 94, 0);
        piece.getPiece("conveni").addAd(44, 11, 22, 13, 0.5);
        piece.getPiece("adsignV").addAd(15, 19, 36, 36, -0.5);
        piece.getPiece("adsignH").addAd(10, 1, 36, 36, 0.5);
        piece.getPiece("parking").addAd(56, 2, 36, 35, 0.5);
        piece.getPiece("parking").addAd(100, 24, 36, 35, 0.5);
        
        putObject(piece.getPiece("bldg1"), 16, 7);
        putObject(piece.getPiece("bldg2"), 11, 7);
        putObject(piece.getPiece("bldg3"), 17, 3);
        putObject(piece.getPiece("bldg4"), 0, 8);
        putObject(piece.getPiece("bldg5"), 4, 7);
        putObject(piece.getPiece("house2"), 10, 18);
        putObject(piece.getPiece("house1"), 5, 18);
        putObject(piece.getPiece("park"), 9, 23);
        putObject(piece.getPiece("conveni"), 15, 20);
        putObject(piece.getPiece("busstopV"), 2, 9);
        putObject(piece.getPiece("busstopH"), 21, 22);
        putObject(piece.getPiece("adsignV"), 13, 23);
        putObject(piece.getPiece("adsignH"), 11, 9);
        putObject(piece.getPiece("parking"), 20, 18);
        
        drawTerrain();
        
        myCar = piece.addPiece(new Car(bmp3, 7, 22, this)) as Car;
        //myCar.route = [1,2,0,3,0,1,0,0,2,3,0,3,2];
        myCar.route = [1,2,0,0,0,0,2,3,0,3,2];
        myCar.setDirection(1);
        for (i = 0; i < 10; i++) {
            var c:Car = piece.addPiece(new Car(bmp3, i+3, 12, this)) as Car;
            c.filters = [new ColorMatrixFilter(ColorMatrix.getHueArray(Math.random() * 2 - 1))];
            cars.push(c);
        }
        piece.addChildAll(objLayer);
        
        bmp.dispose();
        bmp2.dispose();
    }
    /**
     * 建造物を指定の位置に置く
     */
    public function putObject(obj:Piece, x:int, y:int, z:int = 0):void {
        var h:int, maxH:int = int.MIN_VALUE, ix:int, iy:int;
        for (ix = x; ix < x + obj.w; ix++) {
            for (iy = y; iy < y + obj.h; iy++) {
                h = getLevel(ix, iy);
                if (h > maxH) maxH = h;
            }
        }
        obj.setPosition(x, y, Math.max(z, maxH, data.WATER_LEVEL));
        if (obj.materialID == -1) return;
        for (ix = x; ix < x + obj.w; ix++) {
            for (iy = y; iy < y + obj.h; iy++) {
                setMaterial(ix, iy, obj.materialID);
            }
        }
    }
    /**
     * 地形マップを描画する
     */
    public function drawTerrain():void {
        canvas.fillRect(canvas.rect, 0);
        var ct_white1:ColorTransform = new ColorTransform();
        var ct_white2:ColorTransform = new ColorTransform();
        var ct_black:ColorTransform = new ColorTransform();
        var ct_water1:ColorTransform = new ColorTransform();
        var ct_water2:ColorTransform = new ColorTransform();
        ct_white1.color = 0xFFFFFF;
        ct_white2.color = 0x888888;
        ct_black.color = 0x000000;
        ct_water1.color = 0x1C3C82;
        ct_water2.color = 0x1F459B;//0x617DBE;
        
        var dirSign1:Array = ["t", "l", "b", "r"];
        var dirSign2:Array = ["tl", "tr", "bl", "br"];
        var dirSign3:Array = ["t", "l", "b", "r", "tl", "tr", "bl", "br"];
        var ofx:Object = { t:0, r:1, b:0, l: -1, tl:-1, tr:1, bl:-1, br:1 };
        var ofy:Object = { t: -1, r:0, b:1, l:0, tl:-1, tr:-1, bl:1, br:1 };
        var index:Object = { t: 0, r:1, b:2, l:3 };
        
        var k:String, i:int, slope:int, h:int, h2:Number, ix:int, iy:int, iz:int, pz:int, img:BitmapData;
        var isSubmerged:Boolean, id:int, isHorizon:Boolean;
        var level:int = Math.max(data.ROAD_LEVEL, data.WATER_LEVEL);
        for (ix = 0; ix < data.W; ix++) {
            for (iy = 0; iy < data.H; iy++) {
                h = getLevel(ix, iy);
                //背面の水の断面を描画
                if (!ix || !iy || ix == (data.W - 1) || iy == (data.H - 1)) {
                    for (iz = 0; iz <= data.WATER_LEVEL; iz++) {
                        var txy:Point = Quarter.toScreen(ix, iy, iz).add(new Point(0, -chip.H));
                        if (!ix) paintBlock(canvas, chip.sectionR, ix, iy, iz, -20, -11, ct_water1);
                        if (!iy) paintBlock(canvas, chip.sectionL, ix, iy, iz, 20, -11, ct_water2);
                        if (ix == (data.W - 1)) paintBlock(waterSection, chip.sectionR, ix, iy, iz, 0, 0, ct_white1);
                        if (iy == (data.H - 1)) paintBlock(waterSection, chip.sectionL, ix, iy, iz, 0, 0, ct_white2);
                    }
                }
                slope = 0
                //地形を描画
                for (iz = 0; iz <= h; iz++) {
                    var deep:int = 5 * Math.min(2, Math.max(0, (data.WATER_LEVEL - iz + 1)));
                    slope = (iz == h)? getSlope(ix, iy) : 0;
                    img = chip.terrain[slope + deep];
                    drawBlock(img, ix, iy, iz);
                    //地形の断面を描画
                    if (ix == (data.W - 1) && slope != 4) {
                        //右端
                        img = (slope == 0 || slope == 2)? chip.sectionR : chip.sections[slope-1];
                        drawBlock(img, ix, iy, iz);
                        paintBlock(waterSection, img, ix, iy, iz, 0, 0, ct_black);
                    }
                    if (iy == (data.H - 1) && slope != 1) {
                        //左端
                        img = (slope == 0 || slope == 3)? chip.sectionL : chip.sections[slope-1];
                        drawBlock(img, ix, iy, iz);
                        paintBlock(waterSection, img, ix, iy, iz, 0, 0, ct_black);
                    }
                }
                iz = h;
                h2 = getHeight(ix, iy);
                isSubmerged = h2 < data.WATER_LEVEL;
                id = getMaterial(ix, iy);
                isHorizon = (slope == 0);
                //道路
                if (id == 1) {
                    if(h2 >= level){
                        //普通に描画
                        img = chip.road[slope];
                        iz = h;
                    } else {
                        //最低レベルより下にいかないようにする
                        img = chip.road[0];
                        iz = level;
                        setLevel(ix, iy, iz);
                        setSlope(ix, iy, 0);
                        //橋の支柱
                        if (isHorizon) {
                            for (pz = h; pz < level; pz++) {
                                var pict:BitmapData = [chip.bridge.PN, chip.bridge.PW1, chip.bridge.PW2][Math.min(2, Math.max(0, data.WATER_LEVEL - pz))];
                                drawPlane(pict, ix, iy, pz);
                            }
                        }
                        drawPlane(chip.bridge.base, ix, iy, iz, 0, 3);
                    }
                    drawBlock(img, ix, iy, iz);
                    //橋のフェンス
                    if (h2 < level) {
                        for each(k in dirSign1) {
                            if (getMaterial(ix + ofx[k], iy + ofy[k]) != 1) drawPlane(chip.bridge[k.toUpperCase()], ix, iy, iz);
                        }
                    }
                }
                if (!isSubmerged && isHorizon) {
                    if (id == 2) drawBlock(chip.groundGrass, ix, iy, iz);
                    if (id == 3) drawBlock(chip.groundAsphalt, ix, iy, iz);
                    if (id == 4) drawBlock(chip.groundSand, ix, iy, iz);
                    if (id == 5) putObject(piece.newPiece("tree", piece.image.tree, 1, 1), ix, iy, iz);
                }
            }
        }
        var noLine:Array = [];
        var slopeH:Object = {
            t:[0, -0.5, 0, 0.5, 0],
            b:[0, 0.5, 0, -0.5, 0],
            l:[0, 0, 0.5, 0, -0.5],
            r:[0, 0, -0.5, 0, 0.5]
        }
        //道路の歩道と白線
        for (ix = 0; ix < data.W; ix++) {
            for (iy = 0; iy < data.H; iy++) {
                h2 = getHeight(ix, iy);
                id = getMaterial(ix, iy);
                slope = getSlope(ix, iy);
                iz = getLevel(ix, iy);
                isHorizon = slope == 0;
                isSubmerged = (h2 < data.WATER_LEVEL);
                var cn:Object = { };
                for each(k in dirSign3) {
                    var plusH:Number = (k.length == 2)? 0 : slopeH[k][getSlope(ix + ofx[k], iy +ofy[k])];
                    cn[k] = getMaterial(ix + ofx[k], iy + ofy[k]) == 1 && h2 == getHeight(ix + ofx[k], iy + ofy[k]) + plusH;
                }
                //道路
                if (id == 1) {
                    //白線描画
                    if (isHorizon) {
                        if (cn.t + cn.b + cn.r + cn.l >= 3 && !cn.tl && !cn.tr && !cn.br && !cn.bl) {
                            for each(k in dirSign1) {
                                if (cn[k]) {
                                    img = (k == "t" || k == "b")? chip.crosswalkX : chip.crosswalkY;
                                    drawBlock(chip.road[0], ix + ofx[k], iy + ofy[k], iz);
                                    drawBlock(img, ix + ofx[k], iy + ofy[k], iz);
                                    if (k == "r" || k == "b") noLine[toIndex(ix + ofx[k], iy + ofy[k])] = true;
                                }
                            }
                        } else if (!noLine[toIndex(ix, iy)]) {
                            for each(k in dirSign1) if (cn[k]) drawBlock(chip.centerline[k.toUpperCase()], ix, iy, iz);
                        }
                    }
                    //進行可能な方向を記録
                    for each(k in dirSign1) {
                        if (cn[k]) data.roadPath[toIndex(ix, iy)].push(index[k]);
                    }
                } else if (isHorizon && iz >= level) {
                    //歩道描画
                    if(cn.t) drawBlock(chip.sidewalk.T, ix, iy, iz);
                    if(cn.b) drawBlock(chip.sidewalk.B, ix, iy, iz);
                    if(cn.l) drawBlock(chip.sidewalk.L, ix, iy, iz);
                    if(cn.r) drawBlock(chip.sidewalk.R, ix, iy, iz);
                    if(cn.t && cn.r) drawBlock(chip.sidewalk.TRI, ix, iy, iz);
                    if(cn.t && cn.l) drawBlock(chip.sidewalk.TLI, ix, iy, iz);
                    if(cn.b && cn.r) drawBlock(chip.sidewalk.BRI, ix, iy, iz);
                    if(cn.b && cn.l) drawBlock(chip.sidewalk.BLI, ix, iy, iz);
                    if(!cn.t && !cn.r && cn.tr) drawBlock(chip.sidewalk.TRO, ix, iy, iz);
                    if(!cn.t && !cn.l && cn.tl) drawBlock(chip.sidewalk.TLO, ix, iy, iz);
                    if(!cn.b && !cn.r && cn.br) drawBlock(chip.sidewalk.BRO, ix, iy, iz);
                    if(!cn.b && !cn.l && cn.bl) drawBlock(chip.sidewalk.BLO, ix, iy, iz);
                }
                if (!isSubmerged) {
                    if (id == 6) drawPlane(chip.phoneBooth, ix, iy, iz);
                    if (id == 7) drawPlane(chip.grassBall, ix, iy, iz);
                    if (id == 8) {
                        if(slope == 4) drawBlock(chip.drainpipe.R, ix, iy, iz);
                        if(slope == 1) drawBlock(chip.drainpipe.B, ix, iy, iz);
                    }
                }
            }
        }
        //水の断面を描画
        var bmp:BitmapData = new BitmapData(canvas.width, canvas.height, true, 0xFF324856);
        bmp.copyChannel(waterSection, waterSection.rect, new Point(), BitmapDataChannel.BLUE, BitmapDataChannel.ALPHA);
        canvas.draw(bmp, new Matrix(), new ColorTransform(1, 1, 1, 1), BlendMode.MULTIPLY);
        bmp.dispose();
    }
    public function toIndex(x:int, y:int):int {
        return x * data.W + y;
    }
    public function getSlope(x:int, y:int):int {
        return data.slope[y + 1][x + 1];
    }
    public function getMaterial(x:int, y:int):int {
        return data.material[y + 1][x + 1];
    }
    /**
     * 正確な高度を取得
     */
    public function getAltitude(x:Number, y:Number):Number {
        var lv:Number = getLevel(int(x), int(y));
        var intX:Number = x - int(x);
        var intY:Number = y - int(y);
        var slope:int = getSlope(int(x), int(y));
        if (slope == 1) lv += -intY;
        if (slope == 2) lv += - 1 + intX;
        if (slope == 3) lv += - 1 + intY;
        if (slope == 4) lv += -intX;
        return lv;
    }
    /**
     * 斜面も考慮した高さを取得
     */
    public function getHeight(x:int, y:int):Number {
        return data.level[y + 1][x + 1] - int(!!getSlope(x, y)) * 0.5;
    }
    /**
     * 斜面を無視した高さを取得
     */
    public function getLevel(x:int, y:int):int {
        return data.level[y + 1][x + 1];
    }
    public function setMaterial(x:int, y:int, id:int):void {
        data.material[y + 1][x + 1] = id;
    }
    public function setSlope(x:int, y:int, slope:int):void {
        data.slope[y + 1][x + 1] = slope;
    }
    public function setLevel(x:int, y:int, lv:int):void {
        data.level[y + 1][x + 1] = lv;
    }
    public function paintBlock(target:BitmapData, img:BitmapData, x:Number, y:Number, z:Number, offsetX:int = 0, offsetY:int = 0, ct:ColorTransform = null):void {
        paintPlane(target, img, x, y, z - 1, offsetX, offsetY - 1, ct);
    }
    public function paintPlane(target:BitmapData, img:BitmapData, x:Number, y:Number, z:Number, offsetX:int = 0, offsetY:int = 0, ct:ColorTransform = null):void {
        var p:Point = Quarter.toScreen(x, y, z);
        p.x -= 22;
        p.y += 22;
        target.draw(img, new Matrix(1, 0, 0, 1, p.x + offsetX, p.y - img.height + offsetY), ct);
    }
    public function drawBlock(img:BitmapData, x:Number, y:Number, z:Number, offsetX:int = 0, offsetY:int = 0):void {
        drawPlane(img, x, y, z - 1, offsetX, offsetY - 1);
    }
    public function drawPlane(img:BitmapData, x:Number, y:Number, z:Number, offsetX:int = 0, offsetY:int = 0):void {
        var p:Point = Quarter.toScreen(x, y, z);
        p.x -= 22;
        p.y += 22;
        canvas.copyPixels(img, img.rect, new Point(p.x + offsetX, p.y - img.height + offsetY), null, null, true); 
    }
}
class Quarter {
    /**
     * クオータービュー座標をスクリーン座標に変換する
     */
    static public function toScreen(x:Number, y:Number, z:Number):Point {
        return toLocalScreen(x, y, z).add(new Point(550, 150));
    }
    static public function toLocalScreen(x:Number, y:Number, z:Number):Point {
        return new Point(x * 22 - y * 22, x * 11 + y * 11 - z * 16);
    }
}
/**
 * マップチップ
 */
class MapChip {
    public const W:int = 42;
    public const H:int = 37;
    public const SIZE:Rectangle = new Rectangle(0, 0, W, H);
    //透過色
    public const TRANSPARENT1:uint = 0x324856;
    public const TRANSPARENT2:uint = 0x182329;
    //道路マップリスト
    public var road:Vector.<BitmapData>;
    //T,B,L,R,TL,TR,BL,BR,TLI,TRI,BLI,BRI,TLO,TRO,BLO,BRO
    public var sidewalk:Object = { };
    //RB(TL)
    public var drainpipe:Object = { };
    //TRBL
    public var centerline:Object = { };
    //TBLR
    public var bridge:Object = { };
    //地形マップリスト
    public var terrain:Vector.<BitmapData>;
    public var crosswalkX:BitmapData;
    public var crosswalkY:BitmapData;
    public var groundGrass:BitmapData;
    public var groundAsphalt:BitmapData;
    public var groundSand:BitmapData;
    public var phoneBooth:BitmapData;
    public var grassBall:BitmapData;
    //断面
    public var sectionL:BitmapData;
    public var sectionR:BitmapData;
    public var sections:Vector.<BitmapData>;
    public function MapChip() {
    }
    public function newBitmap(transparent:Boolean = true, color:uint = 0x000000):BitmapData {
        var bg:uint = (transparent)? 0x00 << 24 | color : 0xFF << 24 | color;
        return new BitmapData(W, H, transparent, bg);
    }
}
/**
 * 画面サイズ
 */
class Display {
    static public var stage:Stage;
    static public const WIDTH:int = 465;
    static public const HEIGHT:int = 465;
    static public const SIZE:Rectangle = new Rectangle(0, 0, WIDTH, HEIGHT);
    static public const CENTER:Point = new Point(SIZE.width / 2, SIZE.height / 2);
}
/**
 * Flickrから画像読み込み
 */
class Flickr extends EventDispatcher {
    static public const API:String = "http://api.flickr.com/services/feeds/photos_public.gne";
    static public const NS_MEDIA:String = "http://search.yahoo.com/mrss/";
    static public const NS_DC:String = "http://purl.org/dc/elements/1.1/";
    static public const NS_CC:String = "http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html";
    static public const NS_FLICKR:String = "urn:flickr:";
    /**サムネイルが読み込み完了する度に呼び出されるイベント*/
    static public const EVENT_LOADIMAGE:String = "onLoadImage";
    /**条件にマッチする画像が1つも見つからなかった時のイベント*/
    static public const EVENT_NOIMAGE:String = "onNoImage";
    /**画像を読み込む枚数*/
    public var loadLimit:int = 20;
    /**Flash以外を読み込む*/
    public var isSkipFlash:Boolean = true;
    /**全MediaItemデータ*/
    public var items:Vector.<MediaItem> = new Vector.<MediaItem>();
    /**サムネイルの読み込みが完了したMediaItemデータ*/
    public var loaded:Vector.<MediaItem> = new Vector.<MediaItem>();
    private var _stock:Vector.<MediaItem>;
    private var _rssLoader:URLLoader;
    private var _activeItem:MediaItem;
    private var _imageLoader:Loader;
    public function Flickr() {
        _rssLoader = new URLLoader();
        _imageLoader = new Loader();
    }
    /**
     * Flickrの画像を検索する
     */
    public function load(tag:String):void {
        items.length = 0;
        loaded.length = 0;
        removeEventImage();
        _rssLoader.addEventListener(Event.COMPLETE, onLoadRss);
        _rssLoader.addEventListener(IOErrorEvent.IO_ERROR, onErrorRss);
        _rssLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onErrorRss);
        _rssLoader.load(new URLRequest(getSearchPath(tag)));
    }
    private function onErrorRss(e:ErrorEvent):void {
        removeEventRss();
        dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, e.text));
    }
    private function onLoadRss(e:Event):void {
        for each(var itm:MediaItem in items) itm.dispose();
        _stock = new Vector.<MediaItem>();
        removeEventRss();
        var xml:XML = new XML(_rssLoader.data);
        var media:Namespace = new Namespace(NS_MEDIA);
        var dc:Namespace = new Namespace(NS_DC);
        var cc:Namespace = new Namespace(NS_CC);
        var flickr:Namespace = new Namespace(NS_FLICKR);
        var tf:TextField = new TextField();
        for each(var node:XML in xml..item) {
            var item:MediaItem = new MediaItem();
            item.title = node.media::title;
            tf.htmlText = node.media::description;
            item.authorName = String(node.author).match(/.*?\((.*)\)/)[1];
            item.authorURL = node.author.@flickr::profile;
            item.descriptionHTML = node.media::description;
            item.descriptionText = tf.text;
            item.date.setDate(node.dc::date.Taken);
            item.thumbnail = node.media::thumbnail.@url;
            item.type = node.media::content.@type;
            item.content = node.media::content.@url;
            item.content_t = item.content.replace(/_[mstbo]\./, "_t.");
            item.isFlash = item.type == "application/x-shockwave-flash";
            if (!isSkipFlash || !item.isFlash) _stock.push(item);
        }
        _stock.sort(function():int { return 1 - Math.floor(Math.random() * 3) } );
        if (_stock.length > loadLimit) _stock.length = loadLimit;
        items = _stock.concat();
        if (!items.length) {
            dispatchEvent(new Event(EVENT_NOIMAGE));
        } else {
            loadNext();
        }
    }
    private function loadNext():void {
        if (!_stock.length || loaded.length >= loadLimit) {
            completeLoad();
            return;
        }
        _activeItem = _stock.shift();
        if (!_activeItem.thumbnail) {
            loadNext();
            return;
        }
        _imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadImage);
        _imageLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onErrorImage);
        _imageLoader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onErrorImage);
        _imageLoader.load(new URLRequest(_activeItem.thumbnail), new LoaderContext(true));
    }
    private function onErrorImage(e:ErrorEvent):void {
        removeEventImage();
        _activeItem.isLoading = false;
        _activeItem.isError = true;
        loadNext();
    }
    private function onLoadImage(e:Event):void {
        removeEventImage();
        _activeItem.isLoading = false;
        _activeItem.image = Bitmap(_imageLoader.content).bitmapData;
        loaded.push(_activeItem);
        dispatchEvent(new Event(EVENT_LOADIMAGE));
        loadNext();
    }
    private function completeLoad():void {
        _activeItem = null;
        dispatchEvent(new Event(Event.COMPLETE));
    }
    private function removeEventImage():void {
        _imageLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onLoadImage);
        _imageLoader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, onErrorImage);
        _imageLoader.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onErrorImage);
    }
    private function removeEventRss():void {
        _rssLoader.removeEventListener(Event.COMPLETE, onLoadRss);
        _rssLoader.removeEventListener(IOErrorEvent.IO_ERROR, onErrorRss);
        _rssLoader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onErrorRss);
    }
    public function getSearchPath(tag:String):String {
        return API + "?format=rss_200&tags=" + tag;
    }
}
/**
 * MediaRSSから取りだした画像データ
 */
class MediaItem {
    /**サムネイル読み込み中か*/
    public var isLoading:Boolean = true;
    /**サムネイルの読み込みに失敗したか*/
    public var isError:Boolean = false;
    /**タイトル*/
    public var title:String;
    /**作者名*/
    public var authorName:String;
    /**作者URL*/
    public var authorURL:String;
    /**投稿日時*/
    public var date:Date = new Date();
    /**説明文(HTMLテキスト)*/
    public var descriptionHTML:String;
    /**説明文(テキスト)*/
    public var descriptionText:String;
    /**サムネイルURL*/
    public var thumbnail:String;
    /**原寸画像URL*/
    public var content:String;
    public var type:String;
    public var isFlash:Boolean;
    public var content_t:String;
    /**サムネイルBitmapData*/
    public var image:BitmapData;
    public function MediaItem() {
    }
    /**
     * データを破棄
     */
    public function dispose():void {
        image.dispose();
    }
}
/**
 * 複数画像読み込み
 */
class ImageLoader {
    public var image:Object = new Object();
    private var _images:Array = new Array();
    private var _count:int;
    private var _completeFunc:Function;
    private var _errorFunc:Function;
    private var _progressFunc:Function;
    private var _activeLoader:Loader;
    public function ImageLoader() {
    }
    public function addImage(src:String, name:String):void {
        _images.push( { src:src, name:name } );
    }
    public function stop():void {
        if (_activeLoader) {
            _activeLoader.unloadAndStop();
            removeEvent(_activeLoader.contentLoaderInfo);
            _activeLoader = null;
        }
    }
    public function load(complete:Function, error:Function = null, progress:Function = null):void {
        _count = -1;
        _completeFunc = complete;
        _errorFunc = error;
        _progressFunc = progress;
        next();
    }
    public function resetItem():void {
        _images.length = 0;
    }
    public function disposeAll():void {
        for (var k:String in image) {
            dispose(k);
        }
    }
    public function dispose(id:String):void {
        if (image[id] is BitmapData) {
            var bmp:BitmapData = image[id];
            if (bmp) bmp.dispose();
        }
    }
    private function next():void {
        if (++_count >= _images.length) {
            if (_completeFunc != null) _completeFunc();
            return;
        }
        _activeLoader = new Loader();
        _activeLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
        _activeLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
        _activeLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onError);
        _activeLoader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
        _activeLoader.load(new URLRequest(_images[_count].src), new LoaderContext(true));
    }
    private function onProgress(e:ProgressEvent):void {
        var info:LoaderInfo = e.currentTarget as LoaderInfo;
        var per:Number = (_count + info.bytesLoaded / info.bytesTotal) / _images.length;
        if (_progressFunc != null) {
            var args:Array = [per, LoaderInfo(e.currentTarget)];
            if (_progressFunc.length < args.length) args.length = _progressFunc.length;
            _progressFunc.apply(null, args);
        }
    }
    private function onError(e:ErrorEvent):void {
        _activeLoader = null;
        removeEvent(e.currentTarget as LoaderInfo);
        if (_errorFunc != null) _errorFunc.apply(null, []);
    }
    private function onComplete(e:Event):void {
        _activeLoader = null;
        var info:LoaderInfo = e.currentTarget as LoaderInfo;
        removeEvent(info);
        if (info.content is Bitmap) {
            image[_images[_count].name] = Bitmap(info.content).bitmapData;
        } else {
            image[_images[_count].name] = info.content;
        }
        next();
    }
    private function removeEvent(target:LoaderInfo):void {
        target.removeEventListener(ProgressEvent.PROGRESS, onProgress);
        target.removeEventListener(Event.COMPLETE, onComplete);
        target.removeEventListener(IOErrorEvent.IO_ERROR, onError);
        target.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
    }
}
/**
 * 簡易イージング
 */
class EaseMotion {
    private static var _sprite:Sprite = new Sprite();
    private static var _targets:Dictionary = new Dictionary();
    private static var _isInit:Boolean = false;
    public function EaseMotion() {
    }
    private static function init():void {
        if (_isInit) return;
        _isInit = true;
        _sprite.addEventListener(Event.ENTER_FRAME, onEnterFrame);
    }
    static private function onEnterFrame(e:Event):void {
        var p:ParamData, o:Object, k:String;
        for each(o in _targets) {
            for (k in o) {
                p = ParamData(o[k]);
                p.position += (p.to - p.position) * p.speed;
                var n:Number = p.position < p.to ? p.to - p.position : p.position - p.to;
                p.target[k] = p.position;
                if (n <= p.snap) {
                    p.target[k] = p.to;
                    if (p.completeFunc != null) {
                        p.completeFunc.apply(null, p.args);
                        p.completeFunc = null;
                    }
                    delete o[k];
                }
            }
        }
    }
    static public function goto(target:Object, param:String, to:Number):void {
        var tgt:Object = _targets[target];
        if (tgt) delete tgt[param];
        target[param] = to;
    }
    /**
     * オブジェクトのプロパティを指定の数値に近付ける
     * @param    target    対象オブジェクト
     * @param    param    プロパティ名
     * @param    to    近づける値
     * @param    speed    速度(0超過~1以下)
     * @param    complete    移動しきった時に呼び出す関数
     * @param    args    関数に渡す引数
     * @param    snap    
     */
    static public function ease(target:Object, param:String, to:Number, speed:Number = NaN, complete:Function = null, args:Array = null, snap:Number = NaN):void {
        init();
        var p:ParamData;
        var tgt:Object = _targets[target];
        if (tgt) {
            p = tgt[param];
            if (p) {
                p.to = to;
                p.snap = (isNaN(snap))? (p.to - p.position) * 0.01 : snap;
                if (p.snap < 0) p.snap = -p.snap;
                if (!isNaN(speed)) p.speed = speed;
                p.completeFunc = complete;
                p.args = (!args)? [] : args;
                return;
            }
        } else {
            _targets[target] = { };
        }
        p = new ParamData();
        p.target = target;
        p.position = target[param];
        p.to = to;
        p.snap = (isNaN(snap))? (p.to - p.position) * 0.01 : snap;
        p.completeFunc = complete;
        p.args = (!args)? [] : args;
        if (p.snap < 0) p.snap = -p.snap;
        if (!isNaN(speed)) p.speed = speed;
        _targets[target][param] = p;
    }
    static public function setSpeed(target:Object, param:String, speed:Number):void {
        var pd:ParamData = getEase(target, param);
        if (pd) pd.speed = speed;
    }
    static public function getEase(target:Object, param:String):ParamData {
        if (!_targets[target]) return null;
        return _targets[target][param];
    }
}
/**
 * イージング用データ
 */
class ParamData {
    public var target:Object;
    public var position:Number = 0;
    public var to:Number = 0;
    public var speed:Number = 0.2;
    public var snap:Number = 0;
    public var args:Array = [];
    public var completeFunc:Function;
    public function ParamData() {
    }
}
class Painter {
    /**
     * べた塗りスプライト生成
     */
    static public function createColorRect(width:Number, height:Number, color:uint = 0x000000, alpha:Number = 1, x:Number = 0, y:Number = 0):Sprite {
        var sp:Sprite = new Sprite();
        sp.graphics.beginFill(color, alpha);
        sp.graphics.drawRect(0, 0, width, height);
        sp.graphics.endFill();
        sp.x = x;
        sp.y = y;
        return sp;
    }
}