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

Gemz

Get Adobe Flash player
by devon_o 26 Dec 2013
/**
 * Copyright devon_o ( http://wonderfl.net/user/devon_o )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/p7Hv
 */

package 
{
    
import com.bit101.components.PushButton;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.utils.setTimeout;
import starling.core.Starling;
import starling.display.Sprite;
import starling.text.BitmapFont;
import starling.text.TextField;

/**
 * Click gems with at least one neighbor of same color...
 * @author Devon O.
 */

[SWF(width='465', height='465', backgroundColor='#000000', frameRate='60')]
public class Main extends flash.display.Sprite 
{
    private var sRoot:starling.display.Sprite;
    private var game:GameBoard;
    private var scoreText:TextField;
    private var newGameButton:PushButton;
    
    public function Main():void 
    {
        if (stage) init();
        else addEventListener(Event.ADDED_TO_STAGE, init);
    }
    
    private function init(e:Event = null):void 
    {
        removeEventListener(Event.ADDED_TO_STAGE, init);
        initStarling();
    }
    
    private function initStarling():void
    {
        var s:Starling = new Starling(starling.display.Sprite, stage);
        s.addEventListener("rootCreated", onStarlingRoot);
        s.start();
    }
    
    private function onStarlingRoot(e:*):void
    {
        e.currentTarget.removeEventListener("rootCreated", onStarlingRoot);
        sRoot = e.currentTarget.root;
        
        this.scoreText = new TextField(100, 25, "Score: 0", BitmapFont.MINI, BitmapFont.NATIVE_SIZE, 0xFFFFFF);
        sRoot.addChild(this.scoreText);
        
        this.game = new GameBoard(12, 12, 12 * 12);
        this.game.x = (stage.stageWidth - this.game.width) >> 1;
        this.game.y = (stage.stageHeight - this.game.height) >> 1;
        this.game.addEventListener("gameOver", onGameOver);
        sRoot.addChild(this.game);
        
        this.newGameButton = new PushButton(this, this.game.x, 435, "New Game", startGame);
        
        addEventListener(Event.ENTER_FRAME, onTick);
    }
    
    private function onTick(e:Event):void
    {
        this.game.update();
        this.scoreText.text = "Score: " + String(this.game.getScore());
    }
    
    private function onGameOver(e:*):void
    {
        this.newGameButton.enabled = true;
    }
    
    private function startGame(e:MouseEvent):void
    {
        this.newGameButton.enabled = false;
        this.game.newBoard();
    }
}
    
}

import flash.display.BitmapData;
import flash.display.GradientType;
import flash.display.InterpolationMethod;
import flash.display.Shape;
import flash.display.SpreadMethod;
import flash.events.SampleDataEvent;
import flash.filters.GlowFilter;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.media.Sound;
import flash.utils.getTimer;
import flash.utils.setTimeout;
import starling.display.Image;
import starling.display.Sprite;
import starling.events.Event;
import starling.events.Touch;
import starling.events.TouchEvent;
import starling.events.TouchPhase;
import starling.textures.Texture;

class Gem extends Image
{
    // For hints
    private static const NUM_FLASHES:int    = 4;
    private static const FLASH_TIME:int        = 150;
    
    public var tx:Number;
    public var ty:Number;
    
    private var mChosen:Boolean;
    private var mColor:uint;
    private var mPosition:Point;
    private var mTexture:Texture;
    private var mIsInteractive:Boolean;
    private var mNeighbors:Array;
    private var mActive:Boolean;
    private var mCurrentFlash:int;
    
    public function Gem(color:uint, xpos:int = 0, ypos:int = 0, isInteractive:Boolean = false) 
    {
        mChosen = false;
        mColor = color;
        mPosition = new Point(xpos, ypos);
        mIsInteractive = isInteractive;
        mTexture = createTexture();
        super(mTexture);
        this.color = mColor;
        
        if (isInteractive)
        {
            visible = false;
            mActive = false;
            addEventListener(TouchEvent.TOUCH, onTouch);
        }
    }
    
    override public function dispose():void 
    {
        super.dispose();
        
        mTexture = null;
        
        removeEventListener(TouchEvent.TOUCH, onTouch);
    }
    
    public function appear():void
    {
        visible = true;
    }
    
    public function hint():void
    {
        mCurrentFlash = 0;
        onHint();
    }
    
    private function onHint():void
    {
        color = 0xFFFFFF;
        setTimeout(offHint, FLASH_TIME);
    }
    
    private function offHint():void
    {
        color = mColor;
        
        if (++mCurrentFlash < NUM_FLASHES)
        {
            setTimeout(onHint, FLASH_TIME);
        }
    }
    
    private function onTouch(event:TouchEvent):void
    {
        if (!mActive) return;
        
        var t:Touch = event.getTouch(this);
        if (!t) return;
        
        if (t.phase == TouchPhase.BEGAN)
        {
            this.mIsInteractive = false;
            dispatchEventWith("click", false, this);
        }
    }
    
    public function get position():Point { return mPosition; }
    public function set position(value:Point):void { mPosition = value; }
    
    public function get gemColor():uint { return mColor; }
    
    public function get chosen():Boolean { return mChosen; }
    public function set chosen(value:Boolean):void { mChosen = value; }
    
    public function set neighbors(value:Array):void { mNeighbors = value; }
    public function get neighbors():Array { return mNeighbors; }
    
    public function get active():Boolean { return mActive; }
    public function set active(value:Boolean):void { mActive = value; }
    
    public function toString():String
    {
        return "[Gem position={" + mPosition.x + ", " + mPosition.y + "} color=0x" + mColor.toString(16) + "]";
    }
    
    private function createTexture():Texture
    {
        const size:int = 32;
        var colors:Array = [0xafadad, 0x444444]; 
        var alphas:Array = [1, 1]; 
        var ratios:Array = [0, 255]; 
         
        var matrix:Matrix = new Matrix(); 
        matrix.createGradientBox(size, size, 0, -8, -8); 
         
        var s:Shape = new Shape();
        s.graphics.lineStyle(2, 0x000000);
        s.graphics.beginGradientFill(GradientType.RADIAL, colors, alphas, ratios, matrix);
        s.graphics.drawRect(0, 0, size, size);
        s.filters = [new GlowFilter(0x000000, 1, 4, 4, 4, 3, true)];
        var dat:BitmapData = new BitmapData(size, size, false, 0x0);
        dat.draw(s);
        var t:Texture = Texture.fromBitmapData(dat);
        dat.dispose();
        s.graphics.clear();
        return t;
    }
}


class GameBoard extends Sprite
{
    
    public static const STATE_NULL:int         = -1;
    public static const STATE_INIT:int         =  0;
    public static const STATE_APPEAR:int     =  1;
    public static const STATE_DROP:int        =  2;
    public static const STATE_PLAY:int         =  3;
    public static const STATE_REMOVE:int    =  4;
    public static const STATE_COMPLETE:int     =  5;
    
    public static const COLORS:Vector.<uint> = new <uint>[0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0x00FFFF];
    
    private static const HINT_TIME:int = 6000;
    
    private var mState:int;
    private var mCurrentGem:int; // used for appearing / disappearing
    private var mTimer:uint;
    private var mGemsToRemove:Array;
    
    private var mNumGems:int;
    private var mRows:int;
    private var mColumns:int;
    private var mGems:Vector.<Gem>;
    private var mBack:Sprite;
    
    private var mScore:int;
    
    private var mTimeSinceLastClick:int;
    
    public function GameBoard(rows:int, columns:int, numGems:int) 
    {
        if (rows * columns != numGems)
            throw new Error("Instantiated GameBoard with incorrect number of rows, columns or gems!");
        
        mRows = rows;
        mColumns = columns;
        mNumGems = numGems;
        mGems = new Vector.<Gem>();
        mScore = 0;
        setState(STATE_INIT);
        
        newBack();
    }
    
    /** Starts a new game */
    public function newBoard():void
    {
        mScore = 0;
        
        removeGems();
        var col:int, row:int, i:int;
        var colors:Array = getDistribution();

        // create gems
        for (i = 0; i < mNumGems; i++)
        {
            col = i % mColumns;
            row = int(i / mColumns);

            var gem:Gem = new Gem(colors[i], col, row, true);
            gem.addEventListener("click", onGemClick);
            
            gem.x = gem.tx = col * 32;
            gem.y = gem.ty =  row * 32;
            
            mGems.push(gem);
            addChild(gem);
        }
        
        updateData();
        
        setState(STATE_APPEAR);
    }
    
    /** get current score */
    public function getScore():int
    {
        return mScore;
    }
    
    /** clean up */
    override public function dispose():void 
    {
        super.dispose();
        removeGems();
        removeBack();
    }
    
    /** clean up the backing graphic */
    private function removeBack():void
    {
        mBack.unflatten();
        while (mBack.numChildren)
        {
            var g:Gem = mBack.getChildAt(0) as Gem;
            mBack.removeChild(g);
            g.dispose();
            g = null;
        }
        removeChild(mBack);
        mBack.dispose();
        mBack = null;
    }
    
    /** Get array of colors for each number of gems */
    private function getDistribution():Array
    {
        var len:int = COLORS.length
        var numEach:int = Math.ceil(mNumGems / len);
        var ret:Array = [];
        for (var i:int = 0; i < len; i++)
        {
            for (var j:int = 0; j < numEach; j++)
            {
                ret.push(COLORS[i]);
            }
        }
        return shuffleArray(ret);
    }
    
    /** Shuffle an array */
    private function shuffleArray(arr:Array):Array 
    {
        var len:int = arr.length;
        var temp:*;
        for (var i:int = 0; i < len; i ++) {
            var rand:int = Math.floor (Math.random() * len);
            temp = arr[i];
            arr[i] = arr[rand];
            arr[rand] = temp;
        }
        return arr;
    }
    
    /** when clicking on gem */
    private function onGemClick(event:Event):void
    {
        var gem:Gem = event.data as Gem;
        var neighbors:Array = gem.neighbors;
        var len:int = neighbors.length;
        
        // if there are any gems to remove
        if (len > 1)
        {
            setGemsActive(false);
            mGemsToRemove = neighbors;
            mScore += len * 50; // higher score for larger removes
            mTimeSinceLastClick = getTimer();
            setState(STATE_REMOVE);
        }
        
    }
    
    /** Get a gem by its position */
    private function getGemByPos(pos:Point):Gem
    {
        var i:int = mGems.length;
        while (i--)
        {
            var gem:Gem = mGems[i];
            if (gem.position.x == pos.x && gem.position.y == pos.y)
                return gem;
        }
        return null;
    }
    
    /** Creates the 'back' object that sits behind the gems */
    private function newBack():void
    {
        if (mBack)
        {
            mBack.removeChildren();
            removeChild(mBack);
            mBack = null;
        }
        
        mBack = new Sprite();
        var col:int, row:int, i:int;
        
        // create empty slots
        for (i = 0; i < mNumGems; i++)
        {
            col = i % mColumns;
            row = int(i / mColumns);
            
            var emptySlot:Gem = new Gem(0x121212);
            
            emptySlot.x = col * 32;
            emptySlot.y = row * 32;
            
            mBack.addChild(emptySlot);
        }
        
        mBack.touchable = false;
        mBack.flatten();
        addChild(mBack);
    }
    
    /** flash the largest group */
    private function showHints():void
    {
        mTimeSinceLastClick = getTimer();
        
        // Get best move
        var largestPlay:int = 0;
        var play:Array;
        var i:int = mGems.length;
        var g:Gem;
        
        while (i--)
        {
            g = mGems[i];
            var len:int = g.neighbors.length;
            if (len > largestPlay)
            {
                largestPlay = len;
                play = g.neighbors;
            }
        }
        
        // show the hint
        i = play.length;
        while (i--)
        {
            g = play[i] as Gem;
            g.hint();
        }
    }
    
    /** set game state */
    private function setState(state:int):void
    {
        mTimer = 0;
        mCurrentGem = 0;
        mState = state;
    }
    
    /** make gems clickable (or not) */
    private function setGemsActive(value:Boolean):void
    {
        var i:int = mGems.length;
        while (i--)
        {
            mGems[i].active = value;
        }
    }
    
    /** Remove (and dispose) all gems */
    private function removeGems():void
    {
        var i:int = mGems.length;
        while (i--)
        {
            var g:Gem = mGems[i];
            removeChild(g);
            g.dispose();
            g = null
        }
        mGems.length = 0;
    }
    
    /** Get array of neighbors of same color of specified gem */
    public function getNeighbors(gem:Gem):Array
    {
        var ret:Array = [], q:Array = [];
        var n:Gem;
        var p:Point = new Point();
        
        // Flood fill algo from Wikipedia
        
        //Add node to the end of Q.
        q.push(gem);
        
        //While Q is not empty:
        while (q.length > 0)
        {
        //Set n equal to the last element of Q.
        //Remove last element from 
            n = q.pop();
        //If the color of n is equal to target-color:
            if (n && n.gemColor == gem.gemColor && !n.chosen)
            {
        //Set the color of n to replacement-color.
                n.chosen = true;
                ret.push(n);
        //Add west node to end of Q.
                p.x = n.position.x - 1;
                p.y = n.position.y;
                q.push(getGemByPos(p));
        //Add east node to end of Q.
                p.x = n.position.x + 1;
                p.y = n.position.y;
                q.push(getGemByPos(p));
        //Add north node to end of Q.
                p.x = n.position.x;
                p.y = n.position.y - 1;
                q.push(getGemByPos(p));
        //Add south node to end of Q.
                p.x = n.position.x;
                p.y = n.position.y + 1;
                q.push(getGemByPos(p));
            }
        }
        
        return ret;
    }
    
    /** Unselect all gems */
    public function unselectAll():void
    {
        var i:int = mGems.length;
        while (i--)
        {
            mGems[i].chosen = false;
        }
    }
    
    /** update according to state */
    public function update():void
    {
        switch(mState)
        {
            case STATE_INIT :
                updateInit();
                break;
            
            case STATE_APPEAR :
                updateAppear();
                break;
                
            case STATE_DROP :
                updateDrop();
                break;
            
            case STATE_PLAY :
                updatePlay();
                break;
                
            case STATE_REMOVE :
                updateRemove();
                break;
                
            case STATE_COMPLETE :
                updateComplete();
                break;
                
            case STATE_NULL :
            default :
                updateNull();
                break;
        }
    }
    
    /** Updates the positions, targets and neighbors of all gems */
    public function updateData():void
    {
        var i:int, j:int;
        var p:Point = new Point(), pBelow:Point = new Point(), pLeft:Point = new Point();
        var g:Gem, below:Gem, left:Gem, tmp:Gem;
        
        // Move Down
        for (i = 0; i < mColumns; i++)
        {
            for (j = mRows-1; j >-1 ; j--)
            {
                p.x = i;
                p.y = j;
                g = getGemByPos(p);
                if (!g) continue;    
                pBelow = p.clone();
                while(pBelow.y < mRows-1)
                {
                    pBelow.y++;
                    below = getGemByPos(pBelow);
                    if (!below)
                    {
                        g.position = pBelow.clone();
                        g.ty = pBelow.y * 32;
                    }
                }
            }
        }
        
        // Move Left
        for (i = 1; i < mColumns; i++)
        {
            p.x = i;
            p.y = mRows - 1;
            g = getGemByPos(p);
            if (!g) continue;
            pLeft = p.clone();
            while (pLeft.x > 0)
            {
                pLeft.x --;
                left = getGemByPos(pLeft);
                if (!left)
                {
                    g.position = pLeft.clone();
                    g.tx = pLeft.x * 32;
                }
            }
            for (j = 0; j < mRows; j++)
            {
                p.y = j;
                tmp = getGemByPos(p);
                if (tmp)
                {
                    tmp.position.x = g.position.x;
                    tmp.tx = g.tx;
                }
            }
        }
        
        // Get neighbors
        i = mGems.length;
        var gameOver:Boolean = true;
        while (i--)
        {
            g = mGems[i];
            g.neighbors = getNeighbors(g);
            if (g.neighbors.length > 1) gameOver = false;
            unselectAll();
        }
        
        if (gameOver) 
            endGame();
    }
    
    /** no state */
    private function updateNull():void
    {
        return;
    }
    
    /** add init state if desired */
    private function updateInit():void
    {
        return;
    }
    
    /** Update the gems appearing */
    private function updateAppear():void
    {
        mGems[mCurrentGem].appear();
        if (++mCurrentGem == mGems.length) 
        {
            mTimeSinceLastClick = getTimer();
            setGemsActive(true);
            setState(STATE_PLAY);
        }
    }
    
    /** update gems dropping */
    private function updateDrop():void
    {
        var numGemsSet:int = 0;
        const numGems:int = mGems.length;
        var i:int = numGems;
        while (i--)
        {
            var hset:Boolean = false;
            var vset:Boolean = false;
            
            var gem:Gem = mGems[i];
            
            gem.x += (gem.tx - gem.x) / 1.5;
            if (Math.abs(gem.x - gem.tx) < 1)
            {
                gem.x = gem.tx;
                hset = true;
            }
            
            gem.y += (gem.ty - gem.y) / 1.5;
            if (Math.abs(gem.y - gem.ty) < 1)
            {
                gem.y = gem.ty;
                vset = true;
            }
            
            if (hset && vset)
            {
                numGemsSet++;
                if (numGemsSet == numGems)
                {
                    // All gems are done falling
                    setGemsActive(true);
                    setState(STATE_PLAY);
                }
            }
        }
    }
    
    /** Update playing state */
    private function updatePlay():void
    {
        if (getTimer() - mTimeSinceLastClick > HINT_TIME)
        {
            showHints();
        }
    }
    
    /** Update removing state */
    private function updateRemove():void
    {
        if (mTimer % 4 == 0)
        {
            var g:Gem = mGemsToRemove[mCurrentGem] as Gem;
            var idx:int = mGems.indexOf(g);
            mGems.splice(idx, 1);
            removeChild(g);
            g.dispose();
            g = null;
            
            mScore += 10;
            
            if (++mCurrentGem == mGemsToRemove.length)
            {
                setTimeout(endRemove, 5);
            }
        }
        mTimer++;
    }
    
    /** End removing of gems */
    private function endRemove():void
    {
        unselectAll();
        updateData();
        setState(STATE_DROP);
    }
    
    /** Game over */
    private function updateComplete():void
    {
        setState(STATE_NULL);
        dispatchEventWith("gameOver");
    }
    
    /** End the game */
    private function endGame():void
    {
        setTimeout(setState, 1000, STATE_COMPLETE);
    }
}