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

炎々

炎を絶やさないように延々とブロックを継ぎ続けるパズル

操作方法
- 十字キー
-- 移動
- SPACE
-- 回転
Get Adobe Flash player
by o_healer 24 Oct 2010
/**
 * Copyright o_healer ( http://wonderfl.net/user/o_healer )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/9ygD
 */

/*
 「炎々」
 ・for wonderfl

 概要
 ・炎を絶やさないようにブロックを延々と繋げていくゲーム
  ・以前に作ったやつの移植シリーズ
  ・ついでに、延焼の「アルゴリズム」と「描画」を改善した

 操作方法
 ・←→:移動
 ・↓:高速落下
 ・SPACE:回転

 アルゴリズム
 ・BitmapDataでパネルデータを管理し、ビットマップ操作だけで延焼を実現している
 ・RGBの役割
  ・R:発火の開始方向を表す(PANEL_FIRE_UなどをORでつなげたもの)
  ・G:不使用
  ・B:ブロックの有無を表す(PANEL_NONEかPANEL_BLOCKか)
 ・延焼アルゴリズム
  ・Rが存在すればそれをConvolutionFilterで上下左右に拡大して、ブロックがあるところだけ採用する

 炎と木目の描画は以下のやつを利用
 ・http://wonderfl.net/c/dzI0

 未対応
 ・ハイスコア管理
 ・炎が全て消えた場合のゲームオーバー処理
 ・ブロック回転時に画像が再生成されてしまっている問題の修正
*/


package
{
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.net.*;
    import flash.text.*;
    import flash.filters.*;
    import flash.ui.*;
    import flash.system.*;

    [SWF(width="465", height="465", frameRate="30", backgroundColor="0x000000")]
    public class GameMain extends Sprite
    {
        //==Const==
//*
        static public const URL_BLOCKS:String = "http://assets.wonderfl.net/images/related_images/e/e4/e466/e4667aa771860a2002c45728da23c52285814e86";
/*/
        static public const URL_BLOCKS:String = "Blocks.png";
//*/

/*
            ■:I
            ■
            ■
            ■

            ■■:O
            ■■

            ■
            ■
            ■■:L

             ■
             ■
            ■■:R

            ■■■:T
             ■

             ■■:S
            ■■

            ■■
             ■■:Z
*/

        static public var BlockTypeIter:int = 0;
        static public const BLOCK_TYPE_I:int = BlockTypeIter++;
        static public const BLOCK_TYPE_O:int = BlockTypeIter++;
        static public const BLOCK_TYPE_L:int = BlockTypeIter++;
        static public const BLOCK_TYPE_R:int = BlockTypeIter++;
        static public const BLOCK_TYPE_T:int = BlockTypeIter++;
        static public const BLOCK_TYPE_S:int = BlockTypeIter++;
        static public const BLOCK_TYPE_Z:int = BlockTypeIter++;
        static public const BLOCK_TYPE_NUM:int = BlockTypeIter;

        //回転量(時計回り方向)
        static public var RotIter:int = 0;
        static public const ROT_0:uint   = RotIter++;
        static public const ROT_90:uint  = RotIter++;
        static public const ROT_180:uint = RotIter++;
        static public const ROT_270:uint = RotIter++;
        static public const ROT_NUM:uint = RotIter;


        static public const BLOCK_FORM:Array = [
            //I
            [
                //ROT_0
                [
                    [0, 1, 0, 0],
                    [0, 1, 0, 0],
                    [0, 1, 0, 0],
                    [0, 1, 0, 0],
                ],
                //ROT_90
                [
                    [0, 0, 0, 0],
                    [0, 0, 0, 0],
                    [1, 1, 1, 1],
                    [0, 0, 0, 0],
                ],
                //ROT_180
                [
                    [0, 1, 0, 0],
                    [0, 1, 0, 0],
                    [0, 1, 0, 0],
                    [0, 1, 0, 0],
                ],
                //ROT_270
                [
                    [0, 0, 0, 0],
                    [0, 0, 0, 0],
                    [1, 1, 1, 1],
                    [0, 0, 0, 0],
                ],
            ],
            //O
            [
                //ROT_0
                [
                    [0, 0, 0, 0],
                    [0, 1, 1, 0],
                    [0, 1, 1, 0],
                    [0, 0, 0, 0],
                ],
                //ROT_90
                [
                    [0, 0, 0, 0],
                    [0, 1, 1, 0],
                    [0, 1, 1, 0],
                    [0, 0, 0, 0],
                ],
                //ROT_180
                [
                    [0, 0, 0, 0],
                    [0, 1, 1, 0],
                    [0, 1, 1, 0],
                    [0, 0, 0, 0],
                ],
                //ROT_270
                [
                    [0, 0, 0, 0],
                    [0, 1, 1, 0],
                    [0, 1, 1, 0],
                    [0, 0, 0, 0],
                ],
            ],
            //L
            [
                //ROT_0
                [
                    [0, 1, 0, 0],
                    [0, 1, 0, 0],
                    [0, 1, 1, 0],
                    [0, 0, 0, 0],
                ],
                //ROT_90
                [
                    [0, 0, 0, 0],
                    [0, 1, 1, 1],
                    [0, 1, 0, 0],
                    [0, 0, 0, 0],
                ],
                //ROT_180
                [
                    [0, 0, 0, 0],
                    [0, 1, 1, 0],
                    [0, 0, 1, 0],
                    [0, 0, 1, 0],
                ],
                //ROT_270
                [
                    [0, 0, 0, 0],
                    [0, 0, 1, 0],
                    [1, 1, 1, 0],
                    [0, 0, 0, 0],
                ],
            ],
            //R
            [
                //ROT_0
                [
                    [0, 0, 1, 0],
                    [0, 0, 1, 0],
                    [0, 1, 1, 0],
                    [0, 0, 0, 0],
                ],
                //ROT_90
                [
                    [0, 0, 0, 0],
                    [0, 1, 0, 0],
                    [0, 1, 1, 1],
                    [0, 0, 0, 0],
                ],
                //ROT_180
                [
                    [0, 0, 0, 0],
                    [0, 1, 1, 0],
                    [0, 1, 0, 0],
                    [0, 1, 0, 0],
                ],
                //ROT_270
                [
                    [0, 0, 0, 0],
                    [1, 1, 1, 0],
                    [0, 0, 1, 0],
                    [0, 0, 0, 0],
                ],
            ],
            //T
            [
                //ROT_0
                [
                    [0, 0, 0, 0],
                    [1, 1, 1, 0],
                    [0, 1, 0, 0],
                    [0, 0, 0, 0],
                ],
                //ROT_90
                [
                    [0, 1, 0, 0],
                    [1, 1, 0, 0],
                    [0, 1, 0, 0],
                    [0, 0, 0, 0],
                ],
                //ROT_180
                [
                    [0, 1, 0, 0],
                    [1, 1, 1, 0],
                    [0, 0, 0, 0],
                    [0, 0, 0, 0],
                ],
                //ROT_270
                [
                    [0, 1, 0, 0],
                    [0, 1, 1, 0],
                    [0, 1, 0, 0],
                    [0, 0, 0, 0],
                ],
            ],
            //S
            [
                //ROT_0
                [
                    [0, 0, 0, 0],
                    [0, 1, 1, 0],
                    [1, 1, 0, 0],
                    [0, 0, 0, 0],
                ],
                //ROT_90
                [
                    [1, 0, 0, 0],
                    [1, 1, 0, 0],
                    [0, 1, 0, 0],
                    [0, 0, 0, 0],
                ],
                //ROT_180
                [
                    [0, 0, 0, 0],
                    [0, 1, 1, 0],
                    [1, 1, 0, 0],
                    [0, 0, 0, 0],
                ],
                //ROT_270
                [
                    [1, 0, 0, 0],
                    [1, 1, 0, 0],
                    [0, 1, 0, 0],
                    [0, 0, 0, 0],
                ],
            ],
            //Z
            [
                //ROT_0
                [
                    [0, 0, 0, 0],
                    [1, 1, 0, 0],
                    [0, 1, 1, 0],
                    [0, 0, 0, 0],
                ],
                //ROT_90
                [
                    [0, 1, 0, 0],
                    [1, 1, 0, 0],
                    [1, 0, 0, 0],
                    [0, 0, 0, 0],
                ],
                //ROT_180
                [
                    [0, 0, 0, 0],
                    [1, 1, 0, 0],
                    [0, 1, 1, 0],
                    [0, 0, 0, 0],
                ],
                //ROT_270
                [
                    [0, 1, 0, 0],
                    [1, 1, 0, 0],
                    [1, 0, 0, 0],
                    [0, 0, 0, 0],
                ],
            ],
        ];


        //擬似マップ用
        static public var m_MapIter:int = 0;
        static public const O:int = m_MapIter++;//空白
        static public const W:int = m_MapIter++;//壁


        //Stage
        static public const VIEW_W:int = 465;
        static public const VIEW_H:int = 465;

        //Panel
        static public const PANEL_LEN:int = 32;

        //Map
        static public const MAP_NUM_X:int = 12;
        static public const MAP_NUM_Y:int = 14;

        //Block Param
        static public const FALL_VEL:Number = 15.0;
        static public const FALL_ACCEL_RATIO:Number = 20.0;

        //Panel : B : ブロック
        static public const PANEL_NONE:int        = 0x00;
        static public const PANEL_BLOCK:int        = 0x01;
        //Panel : R : 炎
        static public const PANEL_FIRE_U:int    = 0x01;
        static public const PANEL_FIRE_D:int    = 0x02;
        static public const PANEL_FIRE_L:int    = 0x04;
        static public const PANEL_FIRE_R:int    = 0x08;

        //Input
        static public var InputIter:int = 0;
        static public const INPUT_U:int = InputIter++;
        static public const INPUT_D:int = InputIter++;
        static public const INPUT_L:int = InputIter++;
        static public const INPUT_R:int = InputIter++;
        static public const INPUT_ROT:int = InputIter++;
        static public const INPUT_NUM:int = InputIter;

        //Util
        static public const VIEW_RECT:Rectangle = new Rectangle(0,0, VIEW_W,VIEW_H);
        static public const DATA_RECT:Rectangle = new Rectangle(0,0, MAP_NUM_X,MAP_NUM_Y);
        static public const PANEL_RECT:Rectangle = new Rectangle(0,0, PANEL_LEN,PANEL_LEN);
        static public const POS_ZERO:Point = new Point(0,0);

        //燃え広がる速さ
        public var INTERVAL_MIN:Number = 0.05;//最速
        public var INTERVAL_MAX:Number = 2.0;//最遅

        //Debug
        static public const IS_DEBUG:Boolean = false;


        //==Var==

        //--基本

        //Root
        public var m_Root:Sprite = new Sprite();

        //BMP * BG
        public var m_Bitmap_BG:Bitmap;

        //BMP : View
        public var m_BitmapData_View:BitmapData = new BitmapData(VIEW_W, VIEW_H, true, 0x00000000);

        //テキスト
        public var m_Text:TextField = new TextField();

        //ゲームオーバーフラグ
        public var m_IsGameOver:Boolean = false;

        //--移動させるブロックまわり

        //ブロックの形状(ブロックがある部分を1にする)
        public var m_Data_MoveBlock:BitmapData = new BitmapData(4,4, false, 0);
        //ブロックの画像
        public var m_Bitmap_MoveBlock:Bitmap = new Bitmap(new BitmapData(4*PANEL_LEN, 4*PANEL_LEN, true, 0x00000000));

        //現在のブロックの情報
        public var m_BlockIndex:int = 0;
        public var m_RotIter:int = 0;

        //入力
        public var m_Input:Array = new Array(INPUT_NUM);
        public var m_Input_Old:Array = new Array(INPUT_NUM);//エッジ検出用

        //--データまわり

        //BMP : Panel(ブロックの有無や炎の管理用)
        public var m_BitmapData_Data:BitmapData = new BitmapData(MAP_NUM_X, MAP_NUM_Y, false, 0x000000);
        //BMP : Util(作業用)
        public var m_BitmapData_Util:BitmapData = new BitmapData(VIEW_W, VIEW_H, false, 0x000000);

        //Filter : 延焼処理
        public var m_Filter_FireSpread:ConvolutionFilter;
        //Filter : 着火処理
        public var m_Filter_FireMask:ColorMatrixFilter;

        //--炎描画まわり

        //実際の炎の描画
        public var m_BitmapData_Fire_View:BitmapData = new BitmapData(VIEW_W, VIEW_H, false, 0x00);
        //炎の状態を0x00~0xFFで保持するデータ
        public var m_BitmapData_Fire_Data:BitmapData = new BitmapData(VIEW_W, VIEW_H, false, 0x00);
        //Fire => Viewの変換のためのパレット(0x00~0xFFの値を、実際の炎の色に置き換える)
        public var m_Palette_Data_to_View:Array = new Array(256);
        //減衰に使うためのパーリンノイズ
        public var m_BitmapData_PerlinNoise:BitmapData = new BitmapData(VIEW_W * 2, VIEW_H * 2, false, 0x000000);
        //スクロールに使うマトリクス
        public var m_Mtx_PerlinNoise:Matrix = new Matrix();
        //広げるためのブラーフィルター
        public var m_Filter_FireBlur:BlurFilter = new BlurFilter(4,4);

        //--燃え広がる処理

        //発火点描画の事前計算用
        public var m_BitmapData_Emit:Array = new Array(16);
        //燃えた部分を消すマスクの事前計算用
        public var m_BitmapData_Mask:Array = new Array(16);

        //Timer
        public var m_TotalTimer:Number = 0.0;
        public var m_FireTimer:Number = 0.0;//Intervalごとにリセット
        //Interval : 燃え広がる速度
        public var m_Interval:Number     = 5.0;//現在の値
        public var m_Interval_HalfTime:Number = 60.0;//時間がこれだけかかったらMinとMaxの中間になる


        //--Debug
        public var m_DebugPalette_R:Array;
        public var m_DebugPalette_B:Array;
        public var m_DebugPalette_Zero:Array;


        //==Function==

        public function GameMain(){
            //画像ロード開始
            {
                const LoadFunc:Function = function(in_URL:String, in_OnLoad:Function):void{
                    var loader:Loader = new Loader();
                    loader.load(new URLRequest(in_URL), new LoaderContext(true));//画像のロードを開始して
                    loader.contentLoaderInfo.addEventListener(
                        Event.COMPLETE,//ロードが完了したら
                        function(e:Event):void{
                            in_OnLoad(loader.content);//初期化に入る
                        }
                    );
                }

                LoadFunc(URL_BLOCKS, OnLoadEnd_Blocks);
            }
        }
        public function OnLoadEnd_Blocks(in_Graphic:DisplayObject):void{
            //Init ImageManager
            {
                ImageManager.Init(in_Graphic);//それを保持した後
            }

            //初期化
            {
                Init();
            }
        }

        public function Init():void{
            var x:int;
            var y:int;

            //=延焼アルゴリズム=

            //m_Filter_FireSpread
            {
                //#延焼処理
                //炎が1、それ以外が0の状態で、上下左右にPANEL_FIRE_Uなどを伝搬させる
                m_Filter_FireSpread = new ConvolutionFilter(
                    3,3,
                    [
                        0,                PANEL_FIRE_U,    0,
                        PANEL_FIRE_L,    -0xFF,            PANEL_FIRE_R,
                        0,                PANEL_FIRE_D,    0
                    ]
                );
            }

            //m_Filter_FireMask
            {
                //#着火処理
                //ブロックのある部分(B=1)だけ炎(Rの値)を残す
                m_Filter_FireMask = new ColorMatrixFilter([
                    1, 0, 0xFF, 0, -0xFF,//R = R + 0xFF*(B-1)
                    0, 1, 0, 0, 0,
                    0, 0, 1, 0, 0,
                    0, 0, 0, 1, 0
                ]);
            }

            //=Param=

            //m_Interval
            {
                m_Interval = INTERVAL_MAX;
            }


            //=BG=
            {
                //マップのサイズ
                var MapW:int = MAP_NUM_X * PANEL_LEN;
                var MapH:int = MAP_NUM_Y * PANEL_LEN;

                //マップ以外の部分のサイズ
                var RestW:int = VIEW_W - MapW;
                var RestH:int = VIEW_H - MapH;

                //マップ以外のところに必要なパネルの数
                var SideNumX:int = Math.ceil(RestW/2 / PANEL_LEN);
                var SideNumY:int = Math.ceil(RestH/2 / PANEL_LEN);

                //背景に必要なパネルの数
                var TotalNumX:int = MAP_NUM_X + 2 * SideNumX;
                var TotalNumY:int = MAP_NUM_Y + 2 * SideNumY;

                //背景用ビットマップのサイズ
                var BgBitmapW:int = TotalNumX * PANEL_LEN;
                var BgBitmapH:int = TotalNumY * PANEL_LEN;

                //背景用ビットマップ
                var BitmapData_BG:BitmapData = new BitmapData(BgBitmapW, BgBitmapH, false, 0x000000);

                //背景のパネル配置
                var Map:Array = new Array(TotalNumY);
                for(y = 0; y < TotalNumY; y++){
                    Map[y] = new Array(TotalNumX);
                    for(x = 0; x < TotalNumX; x++){
                        var index:int = O;
                        if(x < SideNumX){index = W;}
                        if(x >= TotalNumX - SideNumX){index = W;}
                        if(y >= TotalNumY - SideNumY){index = W;}

                        Map[y][x] = index;
                    }
                }

                //背景の描画
                ImageManager.DrawBlocks(Map, BitmapData_BG);

                //背景の配置
                m_Bitmap_BG = new Bitmap(BitmapData_BG);
                m_Bitmap_BG.x = MapW/2 - BgBitmapW/2;
                m_Bitmap_BG.y = MapH/2 - BgBitmapH/2;
            }


            //=炎描画=

            //m_BitmapData_PerlinNoise
            {
                //普通にパーリンノイズを生成して
                const PanelLen:int = 24;//火種のおおまかな大きさ(ドット絵に使うので、1マス=32ドットあたりを想定)
                const Octave:int = 2;//変化は雑でいい
                m_BitmapData_PerlinNoise.perlinNoise(PanelLen,PanelLen, Octave, 1000*Math.random(), true, true);
                //それを縦に2枚並べる形にして(スクロールするため。つなぎ目は気にしない)
                m_BitmapData_PerlinNoise.copyPixels(m_BitmapData_PerlinNoise, new Rectangle(0,0,VIEW_W*2,VIEW_H), new Point(0,VIEW_H));
                //減衰に使うため値を抑制
                const ratio:Number = 0.09;//小さくすると炎の伸びが大きくなる
                m_BitmapData_PerlinNoise.colorTransform(
                    m_BitmapData_PerlinNoise.rect,//VIEW_RECTとは範囲が違うので、直のrectを使う
                    new ColorTransform(ratio,ratio,ratio)//値を減衰させる
                );
            }

            //m_Palette_Data_to_View
            {
                for(i = 0; i < 256; i++){
                    //Cosによって発火部分と消える部分の境界を薄める
                    //さらにPowによって減衰の速さを調整する(白→黄色→橙になるように)
                    var r:uint = 0xFF * (0.5 - 0.5*Math.cos(Math.PI * Math.pow(i/255, 1.0)));
                    var g:uint = 0xFF * (0.5 - 0.5*Math.cos(Math.PI * Math.pow(i/255, 1.5)));
                    var b:uint = 0xFF * (0.5 - 0.5*Math.cos(Math.PI * Math.pow(i/255, 3.0)));

                    m_Palette_Data_to_View[i] = (0xFF<<24)|(r<<16)|(g<<8)|(b<<0);
                }
            }


            //=発火点まわり=

            //m_BitmapData_Emit
            {
                for(i = 0; i < 16; i++){
                    m_BitmapData_Emit[i] = new BitmapData(PANEL_LEN, PANEL_LEN, false, 0x00);
                }

                //[0]だけは特殊なのでここで用意
                {
                    //thresholdで境界線が燃え広がる感じに移動するように値を設定
                    for(y = 0; y < 32; y++){
                        var Ratio:Number = y/(32-1);
                        for(x = 0; x < 32; x++){
/*
                            var GapX:int = 32/2 - Math.abs(32/2 - x);
                            const HalfGap:Number = 12.0;
                            var p:Number = HalfGap / (GapX+HalfGap);
                            m_BitmapData_Emit[0].setPixel(x, y, 0xFF*Math.pow(Ratio, p));
/*/
                            var GapX:int = Math.abs(32/2 - x);
                            var GapY:int = 32 - y;

                            var theta:Number = Math.atan2(GapY, GapX);

                            var color:uint;
                            if(Math.atan2(2,1) < theta){
                                color = 0xFF * Ratio;
                            }else{
                                theta = Math.atan2(GapY, 32/2-GapX);
                                var tan:Number = Math.tan(theta);
                                var rate:Number;
                                if(tan > 0.01){
                                    var souji_x:Number = (32 / tan)
                                    rate = souji_x / (32/2 + souji_x);
                                }else{
                                    rate = 1.0;
                                }
                                color = 0xFF * rate;
                            }

                            m_BitmapData_Emit[0].setPixel(x, 32-1 - y, color);
//*/
                        }
                    }

                    //test
                    m_BitmapData_Emit[0].applyFilter(m_BitmapData_Emit[0], PANEL_RECT, POS_ZERO, m_Filter_FireBlur);
                }
            }

            //m_BitmapData_Mask
            {
                for(i = 0; i < 16; i++){
                    m_BitmapData_Mask[i] = new BitmapData(PANEL_LEN, PANEL_LEN, false, 0xFF);
                }
            }


            //==

            //View
            {
                m_Root.x = VIEW_W/2 - (PANEL_LEN * MAP_NUM_X)/2;
                m_Root.y = VIEW_H/2 - (PANEL_LEN * MAP_NUM_Y)/2;
                addChild(m_Root);
                {
                    //背景
                    m_Root.addChild(m_Bitmap_BG);

                    //ブロック部分
                    m_Root.addChild(new Bitmap(m_BitmapData_View));

                    //炎部分
                    var bmp:Bitmap = new Bitmap(m_BitmapData_Fire_View);
                    bmp.blendMode = BlendMode.ADD;//加算で表示
                    m_Root.addChild(bmp);
                }
            }

            //Data
            {
                //#最初の状態を設定
                //一番下の列にブロックを並べる
                m_BitmapData_Data.fillRect(new Rectangle(0,MAP_NUM_Y-1,MAP_NUM_X,1), (PANEL_BLOCK<<0));
                //m_BitmapData_Data.fillRect(new Rectangle(0,0,1,MAP_NUM_Y-1), (PANEL_BLOCK<<0));//test:縦ライン
                //m_BitmapData_Data.fillRect(m_BitmapData_Data.rect, (PANEL_BLOCK<<0));//test:全体
                //右端だけ着火
                m_BitmapData_Data.setPixel(MAP_NUM_X-1,MAP_NUM_Y-1, (PANEL_FIRE_R<<16) | (PANEL_BLOCK<<0));
                //m_BitmapData_Data.setPixel(MAP_NUM_X/2,MAP_NUM_Y-1, (PANEL_FIRE_D<<16) | (PANEL_BLOCK<<0));//test:中央下

                //test
                ImageManager.DrawBlock(m_BitmapData_View, m_BitmapData_Data);
            }

            //Block
            {
                m_Root.addChild(m_Bitmap_MoveBlock);

                RecreateBlock();
            }

            //Input
            {
                //Reset
                for(i = 0; i < INPUT_NUM; i++){
                    m_Input[i] = false;
                    m_Input_Old[i] = false;
                }

                //Listener
                {
                    //キー入力を見る
                    stage.addEventListener(KeyboardEvent.KEY_DOWN, OnKeyDown);
                    stage.addEventListener(KeyboardEvent.KEY_UP, OnKeyUp);
                }
            }

            //Text
            {
                m_Text.selectable = false;
                m_Text.autoSize = TextFieldAutoSize.LEFT;
                m_Text.defaultTextFormat = new TextFormat('Verdana', 60, 0xFFFF00, true);
                m_Text.text = '';
                m_Text.filters = [new GlowFilter(0xFF0000)];

                m_Root.addChild(m_Text);
            }

            //Call Update
            {
                addEventListener(Event.ENTER_FRAME, Update);
            }

            if(IS_DEBUG){
                //Debug : Palette
                {
                    m_DebugPalette_R = new Array(256);
                    m_DebugPalette_B = new Array(256);
                    m_DebugPalette_Zero = new Array(256);
                    for(var i:int = 0; i < 256; i++){
                        //R
                        switch(i){
                        case 0://何もなし
                            m_DebugPalette_R[i] = 0xFF000000;
                            break;
                        default://炎(方向の複合もあるので、0以外は全部)
                            m_DebugPalette_R[i] = 0xFFFF0000;
                            break;
                        }
                        //B
                        switch(i){
                        case 0://空間
                            m_DebugPalette_B[i] = 0xFF000000;
                            break;
                        case 1://ブロック
                            m_DebugPalette_B[i] = 0xFF0000FF;
                            break;
                        default://else
                            m_DebugPalette_B[i] = 0xFF888888;
                            break;
                        }
                        //Zero
                        m_DebugPalette_Zero[i] = 0x00000000;
                    }
                }

                //Debug:最初の可視化
                {
                    Update_Debug();
                }
            }
        }

        //#Reset : Block
        public function RecreateBlock():void{
            //データ
            {
                //Clear
                m_Data_MoveBlock.fillRect(m_Data_MoveBlock.rect, 0);
                m_RotIter = 0;

                //Create
                m_BlockIndex = Math.random() * BLOCK_TYPE_NUM;

                for(var y:int = 0; y < 4; y++){
                    for(var x:int = 0; x < 4; x++){
                        m_Data_MoveBlock.setPixel(x, y, BLOCK_FORM[m_BlockIndex][m_RotIter][y][x]);
                    }
                }
            }

            //表示
            {
                //描画
                ImageManager.DrawBlock(m_Bitmap_MoveBlock.bitmapData, m_Data_MoveBlock);

                //位置
                m_Bitmap_MoveBlock.x = 3 * PANEL_LEN;
                m_Bitmap_MoveBlock.y = 0;
            }

            //生成時点でぶつかっているならゲームオーバー
            {
                var SrcX:int = m_Bitmap_MoveBlock.x / PANEL_LEN;
                var SrcY:int = m_Bitmap_MoveBlock.y / PANEL_LEN;

                if(! IsEmpty(SrcX, SrcY, m_RotIter)){
                    //フラグ
                    {
                        m_IsGameOver = true;
                    }

                    //表示
                    {
                        //Text
                        m_Text.text = 'GAME OVER';

                        //Centering
                        m_Text.x = (stage.stageWidth - m_Text.width) / 2;
                        m_Text.y = (stage.stageHeight - m_Text.height) / 2;
                    }
                }
            }
        }

        //#Input
        private function OnKeyDown(event:KeyboardEvent):void{
            if(event.keyCode == Keyboard.LEFT){    m_Input[INPUT_L] = true;}
            if(event.keyCode == Keyboard.RIGHT){m_Input[INPUT_R] = true;}
            if(event.keyCode == Keyboard.UP){    m_Input[INPUT_U] = true;}
            if(event.keyCode == Keyboard.DOWN){    m_Input[INPUT_D] = true;}
            if(event.keyCode == Keyboard.SPACE){m_Input[INPUT_ROT] = true;}
        }
        private function OnKeyUp(event:KeyboardEvent):void{
            if(event.keyCode == Keyboard.LEFT){    m_Input[INPUT_L] = false;}
            if(event.keyCode == Keyboard.RIGHT){m_Input[INPUT_R] = false;}
            if(event.keyCode == Keyboard.UP){    m_Input[INPUT_U] = false;}
            if(event.keyCode == Keyboard.DOWN){    m_Input[INPUT_D] = false;}
            if(event.keyCode == Keyboard.SPACE){m_Input[INPUT_ROT] = false;}
        }
        private function UpdateInput_Post():void{
            for(var i:int = 0; i < INPUT_NUM; i++){
                m_Input_Old[i] = m_Input[i];
            }
        }


        //#Update
        public function Update(e:Event):void{
            //var DeltaTime:Number = 1.0 / stage.frameRate;
            var DeltaTime:Number = 1.0 / 30.0;

            //ゲームオーバーならもう何もしない
            if(m_IsGameOver){
                return;
            }

            //ブロックを入力で動かす処理
            Update_Block(DeltaTime);

            //炎を広げたりマスを消したりする処理
            Update_Fire(DeltaTime);

            //入力更新(エッジとの兼ね合いのため、最後にやる)
            UpdateInput_Post();
        }

        //移動先のチェック用関数
        public function IsEmpty(in_OffsetX:int, in_OffsetY:int, in_RotIter:int):Boolean{
            //
            for(var y:int = 0; y < 4; y++){
                for(var x:int = 0; x < 4; x++){
                    //ブロックがなければスルー
                    {
                        if(BLOCK_FORM[m_BlockIndex][in_RotIter][y][x] == 0){
                            continue;
                        }
                    }

                    //移動先のブロック位置
                    var BlockX:int = in_OffsetX + x;
                    var BlockY:int = in_OffsetY + y;

                    //範囲チェック
                    {//範囲外は壁とみなす
                        if(BlockX < 0){return false;}
                        if(BlockX >= MAP_NUM_X){return false;}
                        if(BlockY < 0){return false;}
                        if(BlockY >= MAP_NUM_Y){return false;}
                    }

                    //衝突チェック
                    {//移動先にすでにブロックがあれば移動不可
                        if(m_BitmapData_Data.getPixel(BlockX, BlockY) != 0){
                            return false;
                        }
                    }
                }
            }

            //ひっかかるところがなかったので、空とみなす
            return true;
        }

        //#Update : Block
        public function Update_Block(in_DeltaTime:Number):void{
            //現在の位置(Index)
            var SrcX:int = m_Bitmap_MoveBlock.x / PANEL_LEN;
            var SrcY_U:int = m_Bitmap_MoveBlock.y / PANEL_LEN;//Yは2マスをまたにかけるので両方用意
            var SrcY_D:int = m_Bitmap_MoveBlock.y / PANEL_LEN + 1;

            //入力による回転
            {
                if(m_Input[INPUT_ROT] && !m_Input_Old[INPUT_ROT]){//Edge : Rot
                    var RotIter:int;
                    {
                        RotIter = m_RotIter + 1;
                        if(RotIter >= 4){RotIter -= 4;}
                    }

                    if(IsEmpty(SrcX, SrcY_U, RotIter) && IsEmpty(SrcX, SrcY_D, RotIter)){
                        //回転を実行
                        m_RotIter = RotIter;
                        //m_Data_MoveBlock
                        for(var y:int = 0; y < 4; y++){
                            for(var x:int = 0; x < 4; x++){
                                m_Data_MoveBlock.setPixel(x, y, BLOCK_FORM[m_BlockIndex][m_RotIter][y][x]);
                            }
                        }
                        //m_Bitmap_MoveBlock
                        ImageManager.DrawBlock(m_Bitmap_MoveBlock.bitmapData, m_Data_MoveBlock);
                    }
                }
            }

            //入力による左右移動
            {
                if(m_Input[INPUT_L] && !m_Input_Old[INPUT_L]){//Edge : Left
                    if(IsEmpty(SrcX-1, SrcY_U, m_RotIter) && IsEmpty(SrcX-1, SrcY_D, m_RotIter)){
                        m_Bitmap_MoveBlock.x = (SrcX-1) * PANEL_LEN;
                    }
                }
                if(m_Input[INPUT_R] && !m_Input_Old[INPUT_R]){//Edge : Right
                    if(IsEmpty(SrcX+1, SrcY_U, m_RotIter) && IsEmpty(SrcX+1, SrcY_D, m_RotIter)){
                        m_Bitmap_MoveBlock.x = (SrcX+1) * PANEL_LEN;
                    }
                }

                //再計算
                SrcX = m_Bitmap_MoveBlock.x / PANEL_LEN;
            }

            //下移動
            {
                //入力があれば通常より速く落下

                var Move:Number;
                {
                    var FallVel:Number = FALL_VEL;
                    if(m_Input[INPUT_D]){
                        FallVel *= FALL_ACCEL_RATIO;
                    }

                    Move = FallVel * in_DeltaTime;
                }

                m_Bitmap_MoveBlock.y += Move;//端数が切り捨てられそうだが大丈夫っぽいのでこれで

                //再計算
                SrcY_U = m_Bitmap_MoveBlock.y / PANEL_LEN;
                SrcY_D = m_Bitmap_MoveBlock.y / PANEL_LEN + 1;
            }

            //接地チェック
            {
                var mtx:Matrix = new Matrix(1,0,0,1, 0,0);

                if(! IsEmpty(SrcX, SrcY_D, m_RotIter)){
                    //設置位置にセット
                    //m_Bitmap_MoveBlock.y = SrcY_U * PANEL_LEN;//厳密には、ぶつからないところまで上に移動すべきかもしれないが

                    //描き写す:データ
                    mtx.tx = SrcX; mtx.ty = SrcY_U;
                    m_BitmapData_Data.draw(m_Data_MoveBlock, mtx, null, BlendMode.LIGHTEN);

                    //描き写す:描画
                    mtx.tx *= PANEL_LEN; mtx.ty *= PANEL_LEN;
                    m_BitmapData_View.draw(m_Bitmap_MoveBlock.bitmapData, mtx);

                    //次のを生成
                    RecreateBlock();
                }
            }
        }

        //#Update : Fire
        public function Update_Fire(in_DeltaTime:Number):void{
            //Timer
            {
                m_TotalTimer += in_DeltaTime;
                m_FireTimer += in_DeltaTime;
            }

            //一定周期ごとに炎のマスを進める
            if(m_FireTimer >= m_Interval){
                //残りの部分を燃やし尽くす
                {
                    Emit(1.0);
                }

                //m_FireTimer
                {
                    m_FireTimer -= m_Interval;
                }

                //m_Interval
                {
                    m_Interval = ImageManager.Lerp(INTERVAL_MIN, INTERVAL_MAX, m_Interval_HalfTime / (m_TotalTimer + m_Interval_HalfTime));
                }

                //Next
                {
                    FireSpread();
                }

                //Reset
                {
                    for(var i:int = 1; i < 16; i++){
                        m_BitmapData_Emit[i].fillRect(PANEL_RECT, 0x00);//発火リセット
                        m_BitmapData_Mask[i].fillRect(PANEL_RECT, 0xFF);//マスク復活
                    }
                }

                //Debug
                if(IS_DEBUG){
                    Update_Debug();
                }
            }

            //火種の計算
            Emit(m_FireTimer / m_Interval);

            //現在の炎の状態を描画
            DrawFire();
        }

        //デバッグ描画用更新
        public function Update_Debug():void{
            //View
            {
                const scl:Number = PANEL_LEN;
                const mtx:Matrix = new Matrix(scl,0,0,scl, 0,0);
                m_BitmapData_View.fillRect(m_BitmapData_View.rect, 0xFF888888);
                m_BitmapData_View.draw(m_BitmapData_Data, mtx);
                m_BitmapData_View.paletteMap(m_BitmapData_View, m_BitmapData_View.rect, POS_ZERO, m_DebugPalette_R,null,m_DebugPalette_B);
            }
        }

        //発火点の描画
        public function Emit(in_Ratio:Number):void{
            //燃えているパネルがあれば、方向に応じた形状の発火点にして描く

            var mtx:Matrix = new Matrix();

            //まずは方向に応じた形状を計算
            {
                //最初に[1]を描画して、それを[2]以降に回転したりして描いていく

                //[0]は特殊([1]以降とは使い方が違う)
                //・Emit:閾値判定用の基本画像として使う
                //・Mask:前回との差分を保存するための一時バッファとして使う

                var SrcBmd_Emit:BitmapData = m_BitmapData_Emit[1];
                var SrcBmd_Mask:BitmapData = m_BitmapData_Mask[1];

                //[1]
                {
                    var GapBmd_Mask:BitmapData = m_BitmapData_Mask[0];
                    //マスクの更新&差分取得
                    {
                        //まずは前回のマスク(ブロック用α)の値をコピー
                        GapBmd_Mask.copyPixels(SrcBmd_Mask, PANEL_RECT, POS_ZERO);

                        //マスクの内容を更新
                        var thr:uint = 0xFF * (1 - in_Ratio);
                        SrcBmd_Mask.fillRect(PANEL_RECT, 0xFF);//Clear(必要?)
                        SrcBmd_Mask.threshold(m_BitmapData_Emit[0], PANEL_RECT, POS_ZERO, ">=", thr, 0xFF000000, 0xFF);//閾値に従って拡大

                        //今回のマスクと前回のマスクの差分が、すなわち燃えた範囲
                        GapBmd_Mask.draw(SrcBmd_Mask, null, null, BlendMode.SUBTRACT);
                        //GapBmd_Mask.fillRect(PANEL_RECT, 0xFF);//test:1マスそのまま
                        //GapBmd_Mask.draw(SrcBmd_Mask);//test:マスクそのまま
                    }

                    //前回のEmitを減少させつつ、マスクの差分を加える
                    {
                        //前回の結果を少し残すため、ブラーを使う→うまくいかない
                        //SrcBmd_Emit.applyFilter(SrcBmd_Emit, PANEL_RECT, POS_ZERO, m_Filter_FireBlur);//m_Filter_FireBlurを流用してみる
                        const EMIT_DEC_RATIO:Number = 1.0 - Math.pow(0.2, 1+m_Interval*0.15);
                        const m_CT_EmitDec:ColorTransform = new ColorTransform(EMIT_DEC_RATIO,EMIT_DEC_RATIO,EMIT_DEC_RATIO);
                        SrcBmd_Emit.colorTransform(PANEL_RECT, m_CT_EmitDec);
                        //マスクの差分=今回燃えた量なので加える
                        SrcBmd_Emit.draw(GapBmd_Mask, null, null, BlendMode.ADD);
                    }
                }

                //[2]~
                {
                    //発火点:BlendMode.LIGHTENで大きい方の値を採用(=Max)することで合成
                    //マスク:BlendMode.DARKENで小さい方の値を採用(=Min)することで重ねがけ
                    for(var i:int = 2; i < 16; i++){
                        //Clear
                        {
                            m_BitmapData_Emit[i].fillRect(PANEL_RECT, 0x00);
                            m_BitmapData_Mask[i].fillRect(PANEL_RECT, 0xFF);
                        }

                        //PANEL_FIRE_U
                        if((i & PANEL_FIRE_U) != 0){
                            mtx.identity();
                            m_BitmapData_Emit[i].draw(SrcBmd_Emit, mtx, null, BlendMode.LIGHTEN);
                            m_BitmapData_Mask[i].draw(SrcBmd_Mask, mtx, null, BlendMode.DARKEN);
                        }
                        //PANEL_FIRE_R
                        if((i & PANEL_FIRE_R) != 0){
                            mtx.identity();
                            mtx.rotate(Math.PI * 1/2);
                            mtx.tx = PANEL_LEN;
                            m_BitmapData_Emit[i].draw(SrcBmd_Emit, mtx, null, BlendMode.LIGHTEN);
                            m_BitmapData_Mask[i].draw(SrcBmd_Mask, mtx, null, BlendMode.DARKEN);
                        }
                        //PANEL_FIRE_D
                        if((i & PANEL_FIRE_D) != 0){
                            mtx.identity();
                            mtx.rotate(Math.PI * 2/2);
                            mtx.tx = PANEL_LEN;
                            mtx.ty = PANEL_LEN;
                            m_BitmapData_Emit[i].draw(SrcBmd_Emit, mtx, null, BlendMode.LIGHTEN);
                            m_BitmapData_Mask[i].draw(SrcBmd_Mask, mtx, null, BlendMode.DARKEN);
                        }
                        //PANEL_FIRE_L
                        if((i & PANEL_FIRE_L) != 0){
                            mtx.identity();
                            mtx.rotate(Math.PI * 3/2);
                            mtx.ty = PANEL_LEN;
                            m_BitmapData_Emit[i].draw(SrcBmd_Emit, mtx, null, BlendMode.LIGHTEN);
                            m_BitmapData_Mask[i].draw(SrcBmd_Mask, mtx, null, BlendMode.DARKEN);
                        }
                    }
                }
            }

            //実際に描画していく
            {
                //・ループで調べずにDisplacementMapFilterなどでやりたいけど、それはそれで面倒なのでループでチェック

                mtx.identity();
                var pos:Point = new Point();
                var rect:Rectangle = new Rectangle(0,0,PANEL_LEN,PANEL_LEN);
                for(var y:int = 0; y < MAP_NUM_Y; y++){
                    mtx.ty = y * PANEL_LEN;
                    rect.y = y * PANEL_LEN;
                    pos.y = y * PANEL_LEN
                    for(var x:int = 0; x < MAP_NUM_X; x++){
                        mtx.tx = x * PANEL_LEN;
                        rect.x = x * PANEL_LEN;
                        pos.x = x * PANEL_LEN

                        var r:uint = ((m_BitmapData_Data.getPixel(x,y)>>16) & 0xFF);
                        if(r != 0){//燃えていれば
                            //その部分を発火させ
                            m_BitmapData_Fire_Data.draw(m_BitmapData_Emit[r], mtx, null, BlendMode.ADD);
                            //燃やした部分は消す
                            m_BitmapData_View.copyChannel(m_BitmapData_Mask[r], PANEL_RECT, pos, BitmapDataChannel.BLUE, BitmapDataChannel.ALPHA);
                        }
                    }
                }
            }
        }

        //炎の描画
        public function DrawFire():void{
            //発火元を0xFFでm_BitmapData_Fire_Dataに描きこめば、あとはこの関数で自動で炎の描画をする

            //描画処理
            {
                //ブラーで炎を広げる
                {
                    //薄めることで、上の方を細くする効果も兼ねる
                    m_BitmapData_Fire_Data.applyFilter(m_BitmapData_Fire_Data, VIEW_RECT, POS_ZERO, m_Filter_FireBlur);
                }

                //全体的に沈静化
                {
                    //パーリンノイズで減衰することで揺らぎを表現
                    m_BitmapData_Fire_Data.draw(m_BitmapData_PerlinNoise, m_Mtx_PerlinNoise, null, BlendMode.SUBTRACT);
                }

                //そして0x00~0xFFの値を炎の色に置き換えて表示
                {
                    m_BitmapData_Fire_View.paletteMap(m_BitmapData_Fire_Data, VIEW_RECT, POS_ZERO, null,null,m_Palette_Data_to_View);
                }
            }

            //次回用の更新
            {
                const ScrollVal:int = 2;

                //炎を上にスクロールさせて、燃え上がりを実現
                {
                    //切り捨てて良いスクロールなので、普通にscrollを呼ぶ
                    m_BitmapData_Fire_Data.scroll(0, -ScrollVal);
                }

                //パーリンノイズの採用位置を変更
                {
                    //横方向には少しだけ振動させ、上下方向は炎の上昇と合わせることで、それっぽい揺らぎを作り出す
                    m_Mtx_PerlinNoise.tx += (Math.random() < 0.5)? 1: -1;//振動
                    m_Mtx_PerlinNoise.ty -= ScrollVal;//追随
                    //範囲チェック
                    if(m_Mtx_PerlinNoise.tx > 0){m_Mtx_PerlinNoise.tx -= 2;}//範囲外は押し戻す
                    if(m_Mtx_PerlinNoise.tx < -VIEW_W){m_Mtx_PerlinNoise.tx += 2;}
                    if(m_Mtx_PerlinNoise.ty < -VIEW_H){m_Mtx_PerlinNoise.ty += VIEW_H;}//下にワープ
                }
            }
        }

        //炎を次のマスに進める
        public function FireSpread():void{
            //m_BitmapData_Dataを次の状態になるように更新

            //鎮火
            {
                //燃えている部分のブロックを消し、ついでに炎の有無を01表現に変換する
                m_BitmapData_Data.threshold(
                    m_BitmapData_Data, DATA_RECT, POS_ZERO,
                    ">", 0x000000, (0xFF<<24)|(0x01<<16)|(PANEL_NONE<<0), 0x00FF0000//Rの値が0以上なら、Rを1に、Bを0にする
                );//Colorの指定でαをちゃんと0xFFにする必要がある(0だとαを使わないBitmapでも無視されてしまう)
            }

            //延焼
            {
                //炎を上下左右に広げる(結果はUtilに保存)
                m_BitmapData_Util.applyFilter(m_BitmapData_Data, DATA_RECT, POS_ZERO, m_Filter_FireSpread);
            }

            //着火
            {
                //ブロックのある部分だけ延焼を採用する

                //まずは全ての延焼候補をコピー(本当はConvolutionFilterをRにだけ適用できればそれが一番いい)
                m_BitmapData_Data.copyChannel(m_BitmapData_Util, DATA_RECT, POS_ZERO, BitmapDataChannel.RED, BitmapDataChannel.RED);

                //ブロックのある部分だけを残してそれ以外は0にする
                m_BitmapData_Data.applyFilter(m_BitmapData_Data, DATA_RECT, POS_ZERO, m_Filter_FireMask);
            }
        }
    }
}


import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.net.*;
import flash.text.*;
import flash.filters.*;
import flash.ui.*;


//#ImageManager
class ImageManager
{
    //==Const==

    //#Graphic
    static public var m_GraphicIndexIter:int = 0;

    static public const GRAPHIC_INDEX_BG:int            = m_GraphicIndexIter++;

    static public const GRAPHIC_INDEX_WALL:int            = m_GraphicIndexIter++;
    static public const GRAPHIC_INDEX_WALL_X:int        = m_GraphicIndexIter++;
    static public const GRAPHIC_INDEX_WALL_Y:int        = m_GraphicIndexIter++;
    static public const GRAPHIC_INDEX_WALL_XorY:int        = m_GraphicIndexIter++;
    static public const GRAPHIC_INDEX_WALL_XandY:int    = m_GraphicIndexIter++;

    static public const GRAPHIC_INDEX_NEEDLE:int        = m_GraphicIndexIter++;
    static public const GRAPHIC_INDEX_NEEDLE_X:int        = m_GraphicIndexIter++;
    static public const GRAPHIC_INDEX_NEEDLE_Y:int        = m_GraphicIndexIter++;
    static public const GRAPHIC_INDEX_NEEDLE_XY:int        = m_GraphicIndexIter++;

    static public const GRAPHIC_INDEX_NUM:int            = m_GraphicIndexIter;


    //#enum:Quater
    static public const LU:int = 0;
    static public const RU:int = 1;
    static public const LD:int = 2;
    static public const RD:int = 3;

    //#enum:Pos
    static public const POS_X:int = 0;
    static public const POS_Y:int = 1;


    //#Graphic
    static public var m_BitmapData_View:BitmapData;

    //#Mapping
    static public var GRAPHIC_INDEX_TO_POS:Array;


    //#Palette
    static public var m_Palette_ForView:Array;


    //#Utility
    static public const POS_ZERO:Point = new Point(0,0);


    //ブロック画像で事前に用意しておくサイズ
    static public const W:int = 1000;
    static public const H:int = 1000;


    //==Var==

    //ブロック画像(事前準備分)
    static public var m_BitmapData_Block:BitmapData = new BitmapData(W, H, false, 0x000000);

    //陰付けフィルター
    static public var m_Filter_Shade:GlowFilter = new GlowFilter(
        0x000000, 0.9,//色は黒
        GameMain.PANEL_LEN/2, GameMain.PANEL_LEN/2,//サイズはパネルより小さく
        2,1,//そこそこボカして
        true//内側に生成
    );

    //陰用パレット
    static public var m_Palette_Shade:Array = new Array(256);

    //陰の作業用ビットマップ
    static public var m_BitmapData_Shade:BitmapData = new BitmapData(GameMain.VIEW_W, GameMain.VIEW_H, true, 0x00000000);


    //==Function==

    //初期化
    //#Init
    static public function Init(in_Graphic:DisplayObject):void{
        var x:int, y:int, i:int;

        //m_BitmapData_View
        {
            m_BitmapData_View = new BitmapData(256, 256, false, 0x000000);
            m_BitmapData_View.draw(in_Graphic);
        }

        //GRAPHIC_INDEX_TO_POS
        {//GRAPHIC_INDEX_~から画像の位置へのマッピング(さらにどの隅での処理かも含む)(そして左から何マス目、上から何マス目、という指定)
            GRAPHIC_INDEX_TO_POS = new Array(GRAPHIC_INDEX_NUM);

            //[LU][RU][LD][RD]

            GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_BG]            = [[3,2], [3,2], [3,2], [3,2]];

            GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_WALL]        = [[1,1], [1,1], [1,1], [1,1]];
            GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_WALL_X]        = [[0,1], [2,1], [0,1], [2,1]];
            GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_WALL_Y]        = [[1,0], [1,0], [1,2], [1,2]];
            GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_WALL_XorY]    = [[0,0], [2,0], [0,2], [2,2]];
            GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_WALL_XandY]    = [[3,0], [4,0], [3,1], [4,1]];

            GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_NEEDLE]        = [[3,3], [4,3], [3,4], [4,4]];
            GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_NEEDLE_X]    = [[0,4], [2,4], [0,4], [2,4]];
            GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_NEEDLE_Y]    = [[1,3], [1,3], [1,5], [1,5]];
            GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_NEEDLE_XY]    = [[0,3], [2,3], [0,5], [2,5]];
        }

        //m_Palette_ForView
        {
            m_Palette_ForView = new Array(256);

            var index_graphic:int = GRAPHIC_INDEX_BG;
            for(i = 0; i < 256; i++){
                //区切りごとにindexを変更。次の区切りまではその値をセット
                switch(i){
                case 0:
                case 6:
                case 18:
                case 24:
                    index_graphic = GRAPHIC_INDEX_BG; break;
                case 3:
                case 21:
                    index_graphic = GRAPHIC_INDEX_NEEDLE_Y; break;
                case 9:
                case 15:
                    index_graphic = GRAPHIC_INDEX_NEEDLE_X; break;
                case 12:
                    index_graphic = GRAPHIC_INDEX_NEEDLE_XY; break;
                case 27:
                    index_graphic = GRAPHIC_INDEX_NEEDLE; break;
                case 54:
                case 63:
                    index_graphic = GRAPHIC_INDEX_WALL_XorY; break;
                case 60:
                case 69:
                    index_graphic = GRAPHIC_INDEX_WALL_X; break;
                case 72:
                    index_graphic = GRAPHIC_INDEX_WALL_Y; break;
                case 78:
                    index_graphic = GRAPHIC_INDEX_WALL_XandY; break;
                case 80:
                    index_graphic = GRAPHIC_INDEX_WALL; break;
                }

                m_Palette_ForView[i] = index_graphic;
            }
        }

        //m_BitmapData_Block
        {
            //まずはパーリンノイズを生成
            {
                const Base:int = GameMain.PANEL_LEN * 2;//小さいほど変化が大きくなる
                const NumOctaves:int = 2;//きめ細かさ
                m_BitmapData_Block.perlinNoise(Base,Base, NumOctaves, 1000*Math.random(), true, true, BitmapDataChannel.RED);
            }

            //それを木目っぽく変換
            {
                //二つの色を周期的に配置して縞々にする
                const SrcColor:uint = 0x221100;
                const DstColor:uint = 0x8B4513;

                //0x00~0xFFを何分割して色を周期的に配置するか
                const Cycle:int = 16;

                //実際のパレット計算
                var ColorPalette:Array = new Array(256);
                for(i = 0; i < 256; i++){
                    var ratio:Number = i/255.0;
                    ratio = 0.5 - 0.5*Math.cos(Cycle * Math.PI * ratio);
                    ColorPalette[i] = LerpColor(SrcColor, DstColor, ratio);
                }

                //適用
                m_BitmapData_Block.paletteMap(m_BitmapData_Block, m_BitmapData_Block.rect, POS_ZERO, ColorPalette);
            }
        }

        //m_Palette_Shade
        {
            for(i = 0; i < 256; i++){
                var v:uint = 0xFF * Math.sin(0.5*Math.PI * i/0xFF);
                m_Palette_Shade[i] = (v << 16) | (v << 8) | (v << 0);
            }
        }
    }

    //ブロック描画
    static public function DrawBlock(out_BitmapData:BitmapData, in_MapData:BitmapData):void{
        var NumX:int = in_MapData.width;
        var NumY:int = in_MapData.height;

        var rect:Rectangle = new Rectangle(0,0, GameMain.PANEL_LEN,GameMain.PANEL_LEN);
        var pos:Point = new Point(0,0);

        //陰用
        m_BitmapData_Shade.fillRect(m_BitmapData_Shade.rect, 0x00000000);

        //ブロック画像のサンプリング位置はランダムにズラしてみる
        var OffsetX:int = Lerp(0, W - NumX*GameMain.PANEL_LEN, Math.random());
        var OffsetY:int = Lerp(0, H - NumY*GameMain.PANEL_LEN, Math.random());
        var rect_block:Rectangle = new Rectangle(0,0, GameMain.PANEL_LEN,GameMain.PANEL_LEN);

        //描画
        for(var y:int = 0; y < NumY; y++){
            rect.y = y * GameMain.PANEL_LEN;
            pos.y = y * GameMain.PANEL_LEN;
            rect_block.y = OffsetY + y * GameMain.PANEL_LEN;
            for(var x:int = 0; x < NumX; x++){
                rect.x = x * GameMain.PANEL_LEN;
                pos.x = x * GameMain.PANEL_LEN;
                rect_block.x = OffsetX + x * GameMain.PANEL_LEN;

                //
                switch(in_MapData.getPixel(x,y) & 0x0000FF){
                case GameMain.PANEL_NONE:
                    //空白化
                    out_BitmapData.fillRect(rect, 0x00000000);
                    break;
                case GameMain.PANEL_BLOCK:
                    //ブロック描画
                    out_BitmapData.copyPixels(m_BitmapData_Block, rect_block, pos);
                    //out_BitmapData.fillRect(rect, 0xFFFFFFFF);//test
                    m_BitmapData_Shade.fillRect(rect, 0xFFFF0000);//陰をGlowで付けるため、ブロックの部分を白く塗りつぶす
                    break;
                }
            }
        }

        //陰付け(Shading)
        {
            //GlowFilterのinnerで陰を付ける
            m_BitmapData_Shade.applyFilter(m_BitmapData_Shade, m_BitmapData_Shade.rect, POS_ZERO, m_Filter_Shade);

            //陰が直線的すぎるので、パレットで曲線に置き換える
            m_BitmapData_Shade.paletteMap(m_BitmapData_Shade, m_BitmapData_Shade.rect, POS_ZERO, m_Palette_Shade);

            //陰を描画する(色を減衰させる)
            out_BitmapData.draw(m_BitmapData_Shade, null, null, BlendMode.MULTIPLY);

            //まぁあんまり綺麗にならなかったけどいいや
        }
    }

    //#Draw : Blocks
    static public function DrawBlocks(in_Map:Array, out_BitmapData_View:BitmapData):void
    {
        var x:int, y:int, i:int;
        var mtx:Matrix = new Matrix();

        var NumX:int = in_Map[0].length;
        var NumY:int = in_Map.length;

        //Map => Bitmap_Base
        //Mapの要素を元に、「0:空白」「1:トゲ」「2:壁」というBitmapを生成
        var BitmapData_Base:BitmapData;
        {
            BitmapData_Base = new BitmapData(NumX, NumY, false, 0x000000);
            for(y = 0; y < NumY; y++){
                for(x = 0; x < NumX; x++){
                    var index:int = 0;//default(O, P, G)
                    {
                        switch(in_Map[y][x]){
                        case GameMain.W:
                            index = 2;
                            break;
                        case GameMain.O:
                            index = GRAPHIC_INDEX_BG;
                            break;
                        }
                    }

                    BitmapData_Base.setPixel(x, y, index);
                }
            }
        }

        //Bitmap_Base => Bitmap_LU,Bitmap_RU,Bitmap_LD,Bitmap_RD
        //Bitmap_Baseを元に、四隅の隣接状況をそれぞれ求める(Uniqueな値になるようにする)
        var BitmapData_Quater:Array = new Array(4);
        {
            //Create Filter
            const filter:Array = [
                new ConvolutionFilter(3,3,
                    [//LU
                        1,  3,  0,
                        9, 27,  0,
                        0,  0,  0,
                    ]
                ),
                new ConvolutionFilter(3,3,
                    [//RU
                        0,  3,  1,
                        0, 27,  9,
                        0,  0,  0,
                    ]
                ),
                new ConvolutionFilter(3,3,
                    [//LD
                        0,  0,  0,
                        9, 27,  0,
                        1,  3,  0,
                    ]
                ),
                new ConvolutionFilter(3,3,
                    [//RD
                        0,  0,  0,
                        0, 27,  9,
                        0,  3,  1,
                    ]
                ),
            ];

            for(i = 0; i < 4; i++){
                //Init
                BitmapData_Quater[i] = new BitmapData(NumX, NumY, false, 0x000000);

                //Apply Filter
                BitmapData_Quater[i].applyFilter(BitmapData_Base, BitmapData_Base.rect, POS_ZERO, filter[i]);
            }
        }

        //Bitmap_LU,Bitmap_RU,Bitmap_LD,Bitmap_RD => ForView
        //Uniqueな値から、対応するIndexへと変換する
        var BitmapData_ForView:Array = new Array(4);
        {
            for(i = 0; i < 4; i++){
                //Init
                BitmapData_ForView[i] = new BitmapData(NumX, NumY, false, 0x000000);

                //Apply Palette
                BitmapData_ForView[i].paletteMap(BitmapData_Quater[i], BitmapData_Quater[i].rect, POS_ZERO, null, null, m_Palette_ForView);
            }
        }


        //Draw
        //上で求めたIndexに基づき描画
        {
            var rect:Rectangle = new Rectangle(0,0, 16,16);

            for(y = 0; y < NumY; y++){
                for(x = 0; x < NumX; x++){
                    for(i = 0; i < 4; i++){
                        rect.x = x * 32 + 16 * ((i&1)>>0);
                        rect.y = y * 32 + 16 * ((i&2)>>1);

                        index = BitmapData_ForView[i].getPixel(x, y);

                        //#view
                        mtx.tx = rect.x - 16 * GRAPHIC_INDEX_TO_POS[index][i][POS_X];
                        mtx.ty = rect.y - 16 * GRAPHIC_INDEX_TO_POS[index][i][POS_Y];
                        out_BitmapData_View.draw(m_BitmapData_View, mtx, null, null, rect);
                    }
                }
            }
        }
    }

    //Utility
    static public function Lerp(in_Src:Number, in_Dst:Number, in_Ratio:Number):Number{
        return (in_Src * (1 - in_Ratio)) + (in_Dst * in_Ratio);
    }
    static public function LerpColor(in_Src:uint, in_Dst:uint, in_Ratio:Number):uint{
        var r:uint = Lerp((in_Src>>16)&0xFF, (in_Dst>>16)&0xFF, in_Ratio);
        var g:uint = Lerp((in_Src>>8)&0xFF,  (in_Dst>>8)&0xFF,  in_Ratio);
        var b:uint = Lerp((in_Src>>0)&0xFF,  (in_Dst>>0)&0xFF,  in_Ratio);
        return (0xFF<<24) | (r<<16) | (g<<8) | (b<<0);
    }
}