Beautifl - Flash Gallery

Preview

SlotChain
o_healer 2012年5月17日 MIT License
?
操作方法(How To Play)  
・タッチ(Touch)  
 ・スロットを止める(Stop)  
  
ルール  
・スロットを止めて、縦に3つアイコンを並べてスコアを稼ぐ  
 ・前回並べたところにさらに並べると+1されていく  
  
  
比較用  
・Android版(Air)(あとで追加予定)  
 ・https://play.google.com/store/apps/details?id=air.showohealer.game.airprototype
      /*
 「スロット・チェイン」
 ・スロットを止めまくり、チェインをつなげまくるゲーム
  ・爽快感を目指したものの、実際には微妙な先読みを行う思考ゲーになった

 操作方法(How To Play)
 ・タッチ(Touch)
  ・スロットを止める(Stop)

 ルール
 ・スロットを止めて、縦に3つアイコンを並べてスコアを稼ぐ
  ・前回並べたところにさらに並べると+1されていく

 比較用
 ・Android版(Air)(あとで追加予定)
  ・https://play.google.com/store/apps/details?id=air.showohealer.game.airprototype

*/


package {
    import flash.display.*;
    import flash.events.*;
    import flash.filters.*;
    import flash.geom.*;
    import flash.net.*;
    import flash.system.*;
    import flash.text.*;
    import net.wonderfl.utils.WonderflAPI;
 
    [SWF(width="465", height="465", frameRate="30", backgroundColor="0x000000")]
    public class SlotChain extends Sprite {

        //==Const==

        //画面の大きさ
        static public const VIEW_W:int = 465;
        static public const VIEW_H:int = 465;

        //行数(何段積み重ねるか)
        //- 下2行は並べるために必須。上は2行以上あると先のことが考えられて良い。
        static public const LINE_Y_NUM_D:int    = 2;
        static public const LINE_Y_NUM_U:int    = 3;
        static public const LINE_Y_NUM:int        = LINE_Y_NUM_D + LINE_Y_NUM_U;

        //列数
        //-1スロットあたりの要素数を兼ねる
        static public const LINE_X_NUM:int    = 4;

        //アイコンの種類
        //- 列数よりも1つ大きくすることで、必ず1つ何かが欠ける=どこかで並べるアイコンを別種に切り替えるのを誘発
        static public const ICON_TYPE_NUM:int    = LINE_X_NUM+1;

        //パネルの大きさ
        static public const PANEL_W:int = 64;

        //State
        static public var s_StateIter:int = 0;
        static public const STATE_MOVE:int = s_StateIter++;

        //制限時間
        static public const TIME_LIMIT:int = 60;


        //==Var==

        //Pseudo Singleton
//        static public var Instance:TouchTennis;

        //レイヤー(ペイン)
        public var m_Layer_Game:Sprite = new Sprite();
        public var m_Layer_UI:Sprite = new Sprite();

        //スロット表示
        //- 下にズラす際に行数+1の表示が必要になるので、その分だけ確保
        public var m_Slot:Vector.<SlotOneLine> = new Vector.<SlotOneLine>(LINE_Y_NUM + 1);

        //止まっているスロットの一番下のIndex
        public var m_BottomSlotIndex:int = 0;

        //テキスト
        public var m_Text_Time:TextField = new TextField();
        public var m_Text_Score:TextField = new TextField();
        public var m_Text_ScorePop:Vector.<TextField> = new Vector.<TextField>(LINE_X_NUM);
        public var m_Text_Debug:TextField = new TextField();

        //State
        public var m_State:int = 0;//最初から

        //Score
        public var m_Score:int = 0;

        //コンボ数の保持
        public var m_Combo:Vector.<int> = new Vector.<int>(LINE_X_NUM);

        //残り時間
        public var m_RestTime:Number = TIME_LIMIT;
        public var m_RestTimeGauge:Bitmap = new Bitmap(new BitmapData(1, 1, false, 0xFFFFFF));


        //==Function==

        //Init
        public function SlotChain():void{
            //stageを参照できるようになってから初期化する(イベントリスナの追加や画面の大きさの取得のため)
            if(stage != null){
                Init();
            }else{
                addEventListener(
                    Event.ADDED_TO_STAGE,//ステージに追加されたら
                    function(e:Event):void{
                        Init();
                    }
                );
            }
        }
        public function Init():void{
            var i:int;

            //StaticInit
            {
                SlotOneLine.StaticInit();
                ScoreWindowLoader.init(this, new WonderflAPI(loaderInfo.parameters));
            }

            //BG
            {
                //主にwonderfl用
                addChild(new Bitmap(new BitmapData(stage.stageWidth, stage.stageHeight, false, 0x000000)));
            }

            //Layer
            {
                addChild(m_Layer_Game);
                addChild(m_Layer_UI);
            }

            //Clipping
            {
                var clipX:int = 0;
                var clipY:int = 0;
                var clipW:int = PANEL_W * LINE_X_NUM;
                var clipH:int = PANEL_W * LINE_Y_NUM;
                m_Layer_Game.scrollRect = new Rectangle(clipX,clipY,clipW,clipH);
            }

            //Param
            {
                for(i = 0; i < LINE_X_NUM; ++i){
                    m_Combo[i] = 0;
                }
            }

            //Slot
            {
                for(i = 0; i < LINE_Y_NUM + 1; ++i){
                    var slot:SlotOneLine = new SlotOneLine();
                    slot.y = (LINE_Y_NUM-1 - i) * PANEL_W;//下から並べる
                    if(i < LINE_Y_NUM_D){
                        //下2列は使わないタイプを同じにして止めておく
                        slot.Reset(0);
                        slot.Stop();
                    }else{
                        //それ以降はランダムに設定して回す
                        slot.Reset(int(SlotChain.ICON_TYPE_NUM * Math.random()));
                    }

                    m_Layer_Game.addChild(slot);

                    m_Slot[i] = slot;
                }
            }

            //Gauge
            {
                m_RestTimeGauge.x = LINE_X_NUM * PANEL_W;
                m_RestTimeGauge.y = LINE_Y_NUM * PANEL_W;
                m_RestTimeGauge.scaleX = 16;
                //m_RestTimeGauge.scaleX = VIEW_W - LINE_Y_NUM * PANEL_W;
                m_RestTimeGauge.scaleY = -1 * LINE_Y_NUM * PANEL_W;
                addChild(m_RestTimeGauge);
            }

            //Text
//*
            {
                m_Text_Time.selectable = false;
                m_Text_Time.autoSize = TextFieldAutoSize.LEFT;
                m_Text_Time.defaultTextFormat = new TextFormat('Verdana', 24, 0xFFFFFF, true);
                m_Text_Time.text = "Time : ";
                //m_Text_Time.filters = [new GlowFilter(0x00FFFF,1.0, 8,8)];

                m_Text_Time.x = PANEL_W * (LINE_X_NUM + 0.5);
                m_Text_Time.y = VIEW_H/2;

                addChild(m_Text_Time);
            }
//*/
            {
                m_Text_Score.selectable = false;
                m_Text_Score.autoSize = TextFieldAutoSize.LEFT;
                m_Text_Score.defaultTextFormat = new TextFormat('Verdana', 24, 0xFFFFFF, true);
                m_Text_Score.text = "Score : 0";
                //m_Text_Score.filters = [new GlowFilter(0xFFFF00,1.0, 4,4)];

                m_Text_Score.x = PANEL_W * (LINE_X_NUM + 0.5);
                m_Text_Score.y = 0;

                addChild(m_Text_Score);
            }
            {
                for(i = 0; i < LINE_X_NUM; ++i){
                    m_Text_ScorePop[i] = new TextField();

                    m_Text_ScorePop[i].selectable = false;
                    m_Text_ScorePop[i].autoSize = TextFieldAutoSize.CENTER;
                    m_Text_ScorePop[i].defaultTextFormat = new TextFormat('Verdana', PANEL_W/2, 0x000000, true);
                    m_Text_ScorePop[i].text = "";
                    m_Text_ScorePop[i].filters = [new GlowFilter(0xFFFFFF,1.0, 4,4)];

                    m_Text_ScorePop[i].x = PANEL_W * (i + 0.5);
                    m_Text_ScorePop[i].y = PANEL_W * LINE_Y_NUM_U;

                    addChild(m_Text_ScorePop[i]);
                }
            }
            {
                m_Text_Debug.selectable = false;
                m_Text_Debug.autoSize = TextFieldAutoSize.LEFT;
                m_Text_Debug.defaultTextFormat = new TextFormat('Verdana', 12, 0xFFFFFF, true);
                m_Text_Debug.text = "";
                m_Text_Debug.filters = [new GlowFilter(0xFFFF00,1.0, 4,4)];

                m_Text_Debug.x = 0;
                m_Text_Debug.y = VIEW_H - 24;

                addChild(m_Text_Debug);
            }

            //Touch
            {
                stage.addEventListener(MouseEvent.MOUSE_DOWN, OnMouseDown);
            }

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

            //OnEnd
            {
                addEventListener(Event.REMOVED_FROM_STAGE, Finish);
            }
        }

        //Finish
        public function Finish(e:Event):void{
            removeEventListener(Event.ADDED_TO_STAGE, Init);
            removeEventListener(Event.ENTER_FRAME, Update);
            removeEventListener(Event.REMOVED_FROM_STAGE, Finish);
        }

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

            var i:int;
//*
            //Time
            {
                if(m_RestTime <= 0){
                    return;
                }

                m_RestTime -= DeltaTime;

                if(m_RestTime <= 0){
                    //TimeUp

                    m_Text_Time.text = "Time : 0";
                    m_RestTimeGauge.scaleY = 0;

                    //
                    ScoreWindowLoader.show(m_Score);

                    return;
                }

                m_Text_Time.text = "Time : " + CeilInt(m_RestTime);
                m_RestTimeGauge.scaleY = -m_RestTime/TIME_LIMIT * LINE_Y_NUM * PANEL_W;
                var c:uint = 0xFF * m_RestTime/TIME_LIMIT;
                var color:uint = (0xFF << 16) | (c << 8) | (c << 0);
                m_RestTimeGauge.bitmapData.setPixel(0, 0, color);
            }
//*/
            switch(m_State){
            case STATE_MOVE:
                Update_Move(DeltaTime);
                break;
            }

            for(i = 0; i < LINE_X_NUM; ++i){
                m_Text_ScorePop[i].alpha -= 1 * DeltaTime;
                if(m_Text_ScorePop[i].alpha <= 0){
                    m_Text_ScorePop[i].alpha = 0;
                }
            }
        }

        //Update : Move
        public function Update_Move(DeltaTime:Number):void{
            var i:int;

            //リールの回転
            for(i = 0; i < LINE_Y_NUM + 1; ++i){
                m_Slot[i].Update_Move(DeltaTime);
            }

            //タッチ後のリールの下への移動(&上へのループ)
            {
                //基準となるY(このリールが一番下に来るようにして、上下のリールの位置もこれに合わせる)
                var OldY:Number = m_Slot[m_BottomSlotIndex].y;

                //まずは基準となるリールの移動
                var NewY:Number = Lerp(OldY, (LINE_Y_NUM-1) * PANEL_W, 0.1);
                m_Slot[m_BottomSlotIndex].y = NewY;

                //上下のリールをこれに合わせて移動
                for(i = 0; i < LINE_Y_NUM + 1; ++i){
                    //基準リールはすでに移動したのでスキップ
                    if(i == m_BottomSlotIndex){
                        continue;
                    }

                    //基準リールより下にあるか(下にあるなら上にループさせる必要がある)
                    var IsDown:Boolean = (OldY < m_Slot[i].y);

                    var OffsetIndex:int = (i - m_BottomSlotIndex + LINE_Y_NUM+1) % (LINE_Y_NUM+1);
                    if(IsDown){
                        OffsetIndex -= (LINE_Y_NUM + 1);
                        m_Slot[i].y = NewY - OffsetIndex * PANEL_W;

                        //下に入りきったら再び上に戻して回す
                        if(LINE_Y_NUM * PANEL_W <= m_Slot[i].y){
                            OffsetIndex += (LINE_Y_NUM + 1);
                            m_Slot[i].y = NewY - OffsetIndex * PANEL_W;
                            m_Slot[i].Reset(int(SlotChain.ICON_TYPE_NUM * Math.random()));
                        }
                    }else{
                        m_Slot[i].y = NewY - OffsetIndex * PANEL_W;
                    }
                }
            }
        }

        //Touch
        private function OnMouseDown(e:MouseEvent):void{
            //Check
            {
                if(m_RestTime <= 0){
                    return;
                }
            }

            //Stop
            {
                var stop_index:int = (m_BottomSlotIndex + LINE_Y_NUM_D) % (LINE_Y_NUM + 1);
                m_Slot[stop_index].Stop();
            }

            //Check Score
            {
                CheckScore();
            }

            //++
            {
                m_BottomSlotIndex++;
                if(LINE_Y_NUM + 1 <= m_BottomSlotIndex){
                    m_BottomSlotIndex = 0;
                }
            }
        }

        //Check Score
        private function CheckScore():void{
            m_Text_Debug.text = "";
            for(var x:int = 0; x < LINE_X_NUM; ++x){
                //縦に揃ったか
                var flag:Boolean = true;
                {
                    var bottom_type:int = m_Slot[m_BottomSlotIndex].GetType(x);
                    for(var y:int = 1; y < 3; ++y){
                        var index:int = (m_BottomSlotIndex + y) % (LINE_Y_NUM + 1);
                        if(bottom_type != m_Slot[index].GetType(x)){
                            flag = false;
                            break;
                        }
                    }
                }

                if(flag){
                    //縦に揃ったなら並べた数を記録
                    if(m_Combo[x] <= 0){//並んでいなかったら
                        m_Combo[x] = 3;//新規登録
                    }else{//並んでいたら
                        ++m_Combo[x];//一つ追加
                    }
                }else{
                    //並ばなかったらリセット
                    m_Combo[x] = 0;
                }

                m_Score += m_Combo[x];

                if(0 < m_Combo[x]){
                    m_Text_ScorePop[x].alpha = 1;
                    m_Text_ScorePop[x].text = "+" + m_Combo[x];
                }
            }
//m_Text_Debug.appendText("" + m_Slot[m_BottomSlotIndex].m_UnselectedType + ", " + m_Slot[(m_BottomSlotIndex + 1) % (LINE_Y_NUM + 1)].m_UnselectedType + ", " + m_Slot[(m_BottomSlotIndex + 1) % (LINE_Y_NUM + 2)].m_UnselectedType);
//m_Text_Debug.appendText(";" + m_Slot[m_BottomSlotIndex].m_IndexOffset + ", " + m_Slot[(m_BottomSlotIndex + 1) % (LINE_Y_NUM + 1)].m_IndexOffset + ", " + m_Slot[(m_BottomSlotIndex + 1) % (LINE_Y_NUM + 2)].m_IndexOffset);

            m_Text_Score.text = 'Score : ' + m_Score;
        }

        //Util : Lerp
        public function Lerp(in_Src:Number, in_Dst:Number, in_Ratio:Number):Number{
            return (in_Src * (1 - in_Ratio)) + (in_Dst * in_Ratio);
        }

        //Util : Ceil
        public function CeilInt(in_Number:Number):int{
            var result:int = int(in_Number);
            if(result < in_Number){
                result += 1;
            }
            return result;
        }
    }
}


import flash.display.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import flash.net.*;
import flash.system.*;
import flash.text.*;
import net.wonderfl.utils.WonderflAPI;

//スロット1行分の管理用クラス
class SlotOneLine extends Sprite
{
    //==Const==

    //パネル数
    //- 1周するのを見せるため、同じパターンを2回並べて表示する
    static public const PANEL_NUM:int    = 2 * SlotChain.LINE_X_NUM;

    //移動速度
    static public const VX_MIN:Number = SlotChain.PANEL_W * 3.0;
    static public const VX_MAX:Number = SlotChain.PANEL_W * 4.0;

    //State
    static public var s_StateIter:int = 0;
    static public const STATE_MOVE:int = s_StateIter++;
    static public const STATE_STOP:int = s_StateIter++;


    //==Var==

    //アイコンの元画像
    static public var s_BitmapData:Vector.<BitmapData> = new Vector.<BitmapData>(SlotChain.ICON_TYPE_NUM);

    //選択されていないType
    //- これを一つだけ指定することで、残りを指定したことにする
    public var m_UnselectedType:int = 0;

    //移動速度
    public var m_VX:Number = 0;

    //State
    public var m_State:int = 0;//最初から

    //止めた際の左端のアイコンを求めるためのオフセット
    public var m_IndexOffset:int = 0;


    //==Function==

    //Static Init
    static public function StaticInit():void{
/*
        for(var i:int = 0; i < SlotChain.ICON_TYPE_NUM; ++i){
            //!!仮
            var color:uint = 0x000000;
            if((i & 1) != 0){color |= 0x0000FF;}
            if((i & 2) != 0){color |= 0x00FF00;}
            if((i & 4) != 0){color |= 0xFF0000;}
            s_BitmapData[i] = new BitmapData(SlotChain.PANEL_W, SlotChain.PANEL_W, false, color);
        }
/*/
        //外枠
        var shape:Shape = new Shape();
        var g:Graphics = shape.graphics;
        g.lineStyle(2, 0x000000, 1.0);
        g.drawRect(0, 0, SlotChain.PANEL_W, SlotChain.PANEL_W);

        //中の模様
        const TEXT_W:int = SlotChain.PANEL_W * 0.8;
        var tf:TextField = new TextField();
        tf.selectable = false;
        tf.autoSize = TextFieldAutoSize.LEFT;
        var mtx:Matrix = new Matrix();

        for(var i:int = 0; i < SlotChain.ICON_TYPE_NUM; ++i){
            s_BitmapData[i] = new BitmapData(SlotChain.PANEL_W, SlotChain.PANEL_W, false, 0xF0F0F0);

            switch(i){
            case 0:
                tf.defaultTextFormat = new TextFormat('Verdana', TEXT_W, 0xFF0000, true);
                tf.text = "○";
                break;
            case 1:
                tf.defaultTextFormat = new TextFormat('Verdana', TEXT_W, 0x00FF00, true);
                tf.text = "□";
                break;
            case 2:
                tf.defaultTextFormat = new TextFormat('Verdana', TEXT_W, 0x0000FF, true);
                tf.text = "△";
                break;
            case 3:
                tf.defaultTextFormat = new TextFormat('Verdana', TEXT_W, 0x888800, true);
                tf.text = "×";
                break;
            case 4:
                tf.defaultTextFormat = new TextFormat('Verdana', TEXT_W, 0x008888, true);
                tf.text = "☆";
                break;
            }
            mtx.tx = (SlotChain.PANEL_W - tf.textWidth)  / 2;
            mtx.ty = (SlotChain.PANEL_W - tf.textHeight) / 2;

            s_BitmapData[i].draw(shape);
            s_BitmapData[i].draw(tf, mtx);
        }
//*/
    }


    //Init
    public function SlotOneLine(){
    }

    //Reset
    public function Reset(in_UnselectedType:int):void{
        //m_UnselectedType
        {
            m_UnselectedType = in_UnselectedType;
        }

        //m_VX
        {
            m_VX = Lerp(VX_MIN, VX_MAX, Math.random());
        }

        //Panel
        {
            var bmp:Bitmap;
            var offsetX:int = 0;
            while(0 < numChildren){
                removeChildAt(0);
            }
            for(var i:int = 0; i < SlotChain.ICON_TYPE_NUM; ++i){
                //選ばれなかったものはスキップ
                if(i == m_UnselectedType){
                    continue;
                }

                bmp = new Bitmap(s_BitmapData[i % SlotChain.ICON_TYPE_NUM]);
                bmp.x = offsetX;
                addChild(bmp);

                bmp = new Bitmap(s_BitmapData[i % SlotChain.ICON_TYPE_NUM]);
                bmp.x = offsetX + SlotChain.PANEL_W * SlotChain.LINE_X_NUM;
                addChild(bmp);

                offsetX += SlotChain.PANEL_W;
            }
        }

        //m_State
        {
            m_State = STATE_MOVE;
        }
    }

    //Update : Move
    public function Update_Move(DeltaTime:Number):void{
        switch(m_State){
        case STATE_MOVE:
            this.x -= m_VX * DeltaTime;
            if(this.x <= -SlotChain.PANEL_W * SlotChain.LINE_X_NUM){
                this.x += SlotChain.PANEL_W * SlotChain.LINE_X_NUM;
            }
            break;
        case STATE_STOP:
            break;
        }
    }

    //Stop : Move
    public function Stop():void{
        m_IndexOffset = int(-this.x / SlotChain.PANEL_W + 0.5);
//        if(SlotChain.LINE_X_NUM <= m_IndexOffset){m_IndexOffset -= SlotChain.LINE_X_NUM;}

        this.x = -m_IndexOffset * SlotChain.PANEL_W;

        m_State = STATE_STOP;
    }

    //Get Type
    public function GetType(in_IndexX:int):int{
/*
        var index:int = in_IndexX + m_IndexOffset;

        var result:int = 0;
        var i:int = 0;
        for(;;){
            //未使用分は1つ余計に飛ばす
            if((i % SlotChain.ICON_TYPE_NUM) == m_UnselectedType){
                ++result;
            }

            if(i == index){
                break;
            }

            ++i;
            ++result;
        }

        return (result % SlotChain.ICON_TYPE_NUM);
/*/
        var index:int = (in_IndexX + m_IndexOffset) % SlotChain.LINE_X_NUM;

        var result:int = 0;
        for(var i:int = 0; i < SlotChain.ICON_TYPE_NUM; ++i){
            //未使用分は1つ余計に飛ばす
            if(i == m_UnselectedType){
                ++result;
            }

            if(i == index){
                break;
            }

            ++result;
        }

        return result;
//*/
    }

    //Util : Lerp
    public function Lerp(in_Src:Number, in_Dst:Number, in_Ratio:Number):Number{
        return (in_Src * (1 - in_Ratio)) + (in_Dst * in_Ratio);
    }
}


//*
//bkzenさんのコードを利用
//@see http://wonderfl.net/c/cuY4
//@see http://wonderfl.net/c/kYyY
class ScoreWindowLoader
{
    private static var _top: DisplayObjectContainer;
    private static var _api: WonderflAPI;
    private static var _content: Object;
    //private static const URL: String = "wonderflScore.swf";
    private static const URL: String = "http://swf.wonderfl.net/swf/usercode/5/57/579a/579a46e1306b5770d429a3738349291f05fec4f3.swf";
    private static const TWEET: String = "Playing Slot Chain [score: %SCORE%] #wonderfl";
    
    public static function init(top: DisplayObjectContainer, api: WonderflAPI): void 
    {
        _top = top, _api = api;
        var loader: Loader = new Loader();
        var comp: Function = function(e: Event): void
        {
            loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, comp);
            _content = loader.content;
//            handler();
        }
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, comp);
        loader.load(new URLRequest(URL), new LoaderContext(true));
    }
    
    public static function show( score: int): void
    {
        var window: DisplayObject = _content.makeScoreWindow(_api, score, "Slot Chain", 1, TWEET);
//        var close: Function = function(e: Event): void
//        {
//            window.removeEventListener(Event.CLOSE, close);
//            closeHandler();
//        }
//        window.addEventListener(Event.CLOSE, close);
        _top.addChild(window);
    }
    
}
//*/