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

TwiSphere

・タイマ追加
・難しいと言われたのでしばらくするとヒント表示機能追加
・無駄にツイート機能追加(合わせて名前を簡略)
・アイコンロードのタイムアウト処理追加

遊び方
1)
下の検索窓に何か単語をいれてください。
twitter 検索を元にステージデータを作成します。unique チェックがついていると同一人物の発言は省きます。
2)
ブロックが配置されたらマウスを動かすだけ。
正しい場所にマウスをあわせるとアイコンが浮かび上がり、検索結果の発言がでてきます。
3)
あきるまで繰り返す。

参考にさせていただいた巨匠たち(Thanks!)
・さめがめ - 完成版 http://wonderfl.net/c/nYXj
・Twitterの検索z軸つき http://wonderfl.net/c/g2Gi
・Picasaから画像検索をして表示する。 http://wonderfl.net/c/342x/
・美人すぎったー http://wonderfl.net/c/mXqD
Get Adobe Flash player
by ug24k8 27 Apr 2011
/**
 * Copyright ug24k8 ( http://wonderfl.net/user/ug24k8 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/uDFr
 */

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

// forked from yawakisin's TwiSearch Sphere
/**
TwiSphere (タイム・ヒント・Tweet追加)

・難しいと言われたのでしばらくするとヒント表示。
・無駄にツイート機能追加。
・アイコンのロードタイムアウト追加。

遊び方
1)
下の検索窓に何か単語をいれてください。
twitter 検索を元にステージデータを作成します。unique チェックがついていると同一人物の発言は省きます。
2)
ブロックが配置されたらマウスを動かすだけ。
正しい場所にマウスをあわせるとアイコンが浮かび上がり、検索結果の発言がでてきます。
3)
あきるまで繰り返す。

参考にさせていただいた巨匠たち(Thanks!)
・さめがめ - 完成版 http://wonderfl.net/c/nYXj
・Twitterの検索z軸つき http://wonderfl.net/c/g2Gi
・Picasaから画像検索をして表示する。 http://wonderfl.net/c/342x/
・美人すぎったー http://wonderfl.net/c/mXqD
*/

package {
    
    import flash.display.Sprite;
    import flash.events.Event;

    [SWF(width="465", height="465", backgroundColor="0x202040", fps="30")]
    public class TwiSearch extends Sprite {

        private var manager_:Manager;

        public function TwiSearch() {
            manager_ = addChild(new Manager()) as Manager;

            addEventListener(Event.ENTER_FRAME,
                function(evt:Event):void {
                    manager_.update();
                    });
        }
    }
}


import adobe.utils.CustomActions;
import flash.filters.DropShadowFilter;
import flash.filters.GlowFilter;
import flash.utils.escapeMultiByte;
import org.libspark.betweenas3.events.TweenEvent;
import org.libspark.betweenas3.tweens.ITween;
//import Box2D.Dynamics.Joints.b2RevoluteJointDef;
import com.adobe.images.BitString;
import com.bit101.components.CheckBox;
import flash.display.*;
import flash.events.*;
import flash.geom.Matrix;
import flash.geom.Matrix3D;
import flash.geom.PerspectiveProjection;
import flash.geom.Point;
import flash.geom.Vector3D;
import flash.net.*;
import flash.system.LoaderContext;
import flash.text.*;
import flash.ui.Keyboard;
import flash.utils.ByteArray;
import flash.utils.getTimer;
import jp.nium.utils.ObjectUtil;
import jp.progression.casts.buttons.ParentButton;
import jp.progression.commands.Var;
import jp.progression.core.display.ButtonBase;
import org.libspark.betweenas3.BetweenAS3;
import com.bit101.components.PushButton;

class GameWork {
    public static const INIT_WORD:String = "#TwiSphere";
    public static const TWEET_POST:String = " #TwiSphere #wonderfl http://t.co/TNz3TOh";
    public static const ICON_SIZE:int = 25;
    public static const PANEL_SIZE:Number = 14;

    public var search_word_:String;
    public var entry_list_:Vector.<EntryData>;
}

class EntryData extends Object {
    public var link:String;
    public var uri:String;
    public var author_name:String;
    public var title:String;
    public var img_url:String;

    public function EntryData(src:EntryData = null) {
        if (src) {
            link = src.link;
            uri = src.uri;
            author_name = src.author_name;
            title = src.title;
            img_url = src.img_url;
        }
    }
}

class Panel extends Shape {
    public var position:Vector3D;
    public var color:uint;
    public var z_value:Number;
    public var z_range:Number;
    public var enable:Boolean;
}

/**
 * 管理体
 */
class Manager extends Sprite{

    private var game_work_:GameWork;
    private var search_bar_:SearchBar;
    private var game_main_:GameMain;

    /**
     * コンストラクタ
     */
    public function Manager() {
        game_work_ = new GameWork();
        game_work_.search_word_ = GameWork.INIT_WORD;

        // ゲームメイン追加
        game_main_ = addChild(new GameMain(game_work_)) as GameMain;

        // 検索バー追加
        search_bar_ = addChild(new SearchBar(game_work_)) as SearchBar;
        search_bar_.y = 465 - SearchBar.HEIGHT;
    }

    /**
     * 更新処理
     * @return true … 処理中
     */
    public function update():Boolean {

        if (search_bar_.isUpdate()) {
            // 更新があった
            game_main_.reloadList();
        }

        game_main_.update();

        return true;
    }
}


/**
 * 検索バー
 */
class SearchBar extends Sprite {

    public static const WIDTH:int = 465;
    public static const HEIGHT:int = 25;

    // 共通ワーク用
    private var game_work_:GameWork;
    private var unique_:Boolean = true;

    private var status_:TextField;
    private var updated_:Boolean;

    /**
     * コンストラクタ
     */
    public function SearchBar(game_work:GameWork) {
        game_work_ = game_work;
        //ヘッダーのサーチエリア

        //背景
        var title_area:Shape = addChild(new Shape()) as Shape;
        title_area.graphics.beginFill(0x000000);
        title_area.graphics.drawRect(0, 0, WIDTH, HEIGHT);

        //Serchの文字
        var serch_word:TextField = addChild(Utility.createTextField(12, 0xFFFFFF)) as TextField;
        serch_word.text = "Search:";
        serch_word.x = 2;
        serch_word.y = 2;

        //テキスト入力欄。キーボードのキーを押し上げた際にonKeyUpが実行される
        var search_box:TextField = addChild(Utility.createTextField(11, 0x000000)) as TextField;
        search_box.text = game_work_.search_word_;
        search_box.autoSize = TextFieldAutoSize.NONE;
        search_box.width = 200;
        search_box.height = 18;
        search_box.x = 50;
        search_box.y = 2;
        search_box.border = true;
        search_box.borderColor = 0xFFFFFF;
        search_box.background = true;
        search_box.backgroundColor = 0xC0C0C0;
        search_box.type = TextFieldType.INPUT;
        search_box.selectable = true;
        search_box.addEventListener(KeyboardEvent.KEY_UP, onSearchKeyUp);

        // チェックボックスの準備
        var check_box:CheckBox = new CheckBox(this, 260, 6, "unique",
            function (evt:MouseEvent):void {
                unique_ = evt.currentTarget.selected;
            });
        check_box.selected = unique_;

        // 状態表示欄
        status_ = addChild(Utility.createTextField(11, 0xFF8080)) as TextField;
        status_.text = "";
        status_.x = 310;
        status_.y = 2;

        requestSearch(game_work_.search_word_);
    }

    /**
     * 更新があったか?
     * 更新フラグはクリアされる
     * @return true 更新があった
     */
    public function isUpdate():Boolean {
        var ret:Boolean = updated_;
        updated_ = false
        return ret;
    }

    /**
     * サーチボックスキー判定
     */
    private function onSearchKeyUp(evt:KeyboardEvent):void {
        var tf:TextField = evt.currentTarget as  TextField;
        if (evt.keyCode == Keyboard.ENTER) {
            if (tf.text.length > 0) {
                requestSearch(tf.text);
            }
        }
    }

    /**
     * 検索リクエスト
     */
    private function requestSearch(word:String):void {
        var loader:TwitterXmlLoader = new TwitterXmlLoader();
        loader.addEventListener(Event.COMPLETE, onLoadSearch);
        loader.loadSearchXml(word);
    }

    /**
     * 検索終了処理
     */
    private function onLoadSearch(evt:Event):void {
        var xml_data:XML = evt.currentTarget.xmlObj;

        if (xml_data == null) {
            status_.text = "Search Fail.";
            return;
        }

        // 解析開始ー
        //namespaceを設定
        default xml namespace = new Namespace("http://www.w3.org/2005/Atom");
        var entry_size:int = xml_data.entry.length();

        if (entry_size <= 0) {
            status_.text = "No Articles.";
            return;
        }

        // リスト再構築
        game_work_.entry_list_ = new Vector.<EntryData>();

        //要素を取り出して、textFieldを作りならべる
        for (var i:int = 0; i < entry_size; ++i) {
            var entry:EntryData = new EntryData();

            //xmlからauthorNameを取得
            entry.author_name = xml_data.entry[i].author.name;

            // 既に登録されていればはじく
            if (unique_) {
                if (game_work_.entry_list_.some(
                        function(item:EntryData, index:int, list:Vector.<EntryData>):Boolean {
                            return item.author_name == this;
                        },
                        entry.author_name)) continue;
            }

            //xmlからlink,titleの各値を取得
            entry.link = xml_data.entry[i].link[0].@href;
            entry.uri = xml_data.entry[i].author.uri;
            entry.title = xml_data.entry[i].title;

            //アイコン画像のURLを取得
            entry.img_url = xml_data.entry[i].link[1].@href;

            //bmpフォーマットのアイコン画像を使っている人も少なくないため、Flashで使えるファイルの拡張子かどうかを判別
            var extention:String = entry.img_url.substr( -4);
            extention = extention.toLowerCase();
            if (extention != ".jpg" && extention != ".gif" && extention != ".png") {
                //Flashで使える画像の拡張子ではなかった場合にはデフォルトアイコンを指定
                entry.img_url = "http://a3.twimg.com/sticky/default_profile_images/default_profile_0_normal.png";
            }

            //画像のファイル名が日本語などの規定外の文字である場合を考慮しエンコードする
            entry.img_url = encodeURI(entry.img_url);

            game_work_.entry_list_.push(entry);
        }
        status_.text = "Count: " + game_work_.entry_list_.length.toString();

        game_work_.search_word_ = xml_data.title.toString().replace(" - Twitter Search", "");
        updated_ = true;
    }
}

/**
 * ゲームメイン画面
 */
class GameMain extends Sprite {

    public static const WIDTH:int = 465;
    public static const HEIGHT:int = 440;

    private static const STATE_STANDBY:int = 0;
    private static const STATE_LOAD:int = 1;
    private static const STATE_GAME:int = 2;
    private static const STATE_CLEAR_CALL:int = 3;
    private static const STATE_CLEAR:int = 4;

    // 共通ワーク
    private var game_work_:GameWork;

    // ステージ情報
    private var stage_entry_:EntryData;
    private var stage_index_:int;
    private var stage_bmd_:BitmapData;
    private var offset_pitch_:Number;
    private var offset_head_:Number;

    // 結果画面
    private var result_sprite_:Sprite;
    private var result_message_:ResultMessage;

    // パネル表示
    private var panel_base_:Sprite;
    private var panel_list_:Vector.<Panel>;
    private var panel_order_:Vector.<int>;
    private var pitch_:Number = 0.0;
    private var head_:Number = 0.0;
    
    // ヒント画面
    private var hint_area_:Sprite;
    
    // つぶやきボタン
    private var tw_btn_:PushButton;

    // ゲーム時計
    private var timer_:GameTimer;

    // デバッグ用
    private var loadStart_:int;
    private var status_:TextField;
    private var frist_message_:Boolean;

    private var state_:int;

    // 座標変換
    private var lookat_:Matrix3D;
    private var local_:Matrix3D;
    private var screen_:Matrix3D;

    /**
     * コンストラクタ
     */
    public function GameMain(game_work:GameWork) {
        game_work_ = game_work;
        const ICON_SIZE:int = GameWork.ICON_SIZE;
        const PANEL_SIZE:Number = GameWork.PANEL_SIZE;

        // 背景描画
        graphics.beginFill(0x808090);
        graphics.drawRect(0, 0, WIDTH, HEIGHT);
        graphics.endFill();

        // パネル背景
        panel_base_ = addChild(new Sprite()) as Sprite;
        panel_base_.x = WIDTH / 2.0;
        panel_base_.y = HEIGHT / 2.0;
        panel_base_.alpha = 0.0;

        // パネル生成
        const CENTER_OFFSET:Number = PANEL_SIZE * ICON_SIZE * 0.5;
        const SPHERE_RANGE:Number = CENTER_OFFSET * CENTER_OFFSET + 10000;
        
        panel_list_ = new Vector.<Panel>(ICON_SIZE * ICON_SIZE, true);
        panel_order_ = new Vector.<int>();
        for (var y:int = 0; y < ICON_SIZE; ++y) {
            for (var x:int = 0; x < ICON_SIZE; ++x) {
                var panel:Panel = new Panel();
                panel.position = new Vector3D(x * PANEL_SIZE - CENTER_OFFSET, y * PANEL_SIZE - CENTER_OFFSET, 0, 1);
                var xy2:Number = panel.position.x * panel.position.x + panel.position.y * panel.position.y;
                if (SPHERE_RANGE <= xy2) {
                    continue;
                }
                panel.z_range = Math.sqrt(SPHERE_RANGE - xy2) * 2;

                panel_list_[y * ICON_SIZE + x] = panel;
                panel_base_.addChild(panel);
                panel_order_.push(y * ICON_SIZE + x);
            }
        }

        // ステージにつかうbitmapデータ
        stage_bmd_ = new BitmapData(ICON_SIZE, ICON_SIZE);

        lookat_ = Utility.createLookAt(
                    new Vector3D(0, 0, 700, 1),
                    new Vector3D(0, 0, 0, 1),
                    new Vector3D(0, 1, 0, 1));

        local_ = Utility.createOrtho( -WIDTH * 0.5, WIDTH * 0.5, -HEIGHT * 0.5, HEIGHT * 0.5, 200, 1000);
        screen_ = Utility.createViwPort( -WIDTH / 2, -HEIGHT / 2, WIDTH, HEIGHT);

        // 正解画面
        result_sprite_ = addChild(new Sprite()) as Sprite;
        result_sprite_.visible = false;
        result_sprite_.mouseEnabled = false;

        // 状態表示欄
        status_ = addChild(Utility.createTextField(13, 0xF0C0C0, 0x000000, true, TextFormatAlign.CENTER)) as TextField;
        status_.text = "検索ワードからデータを用意しています。";
//        status_.border = true;
        status_.autoSize = TextFieldAutoSize.NONE;
        status_.width = 310;
        status_.height = 55;
        status_.mouseEnabled = false;
        status_.x = 100;
        status_.y = 2;
                
        // クリアメッセージ画面
        result_message_ = addChild(new ResultMessage()) as ResultMessage;
        result_message_.x = WIDTH * 0.5;
        result_message_.y = 375;
        result_message_.visible = false;
        
        // ツイートボタン
        tw_btn_ = new PushButton(this, 355, 290, "tweet", onTweetButton);
        tw_btn_.visible = false;
        
        // 時計
        timer_ = addChild(new GameTimer()) as GameTimer;

        // ヒント画面
        hint_area_ = addChild(new Sprite()) as Sprite;
        hint_area_.visible = false;

        frist_message_ = true;
        
        addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
        addEventListener(MouseEvent.CLICK, onMouseClick);
        state_ = STATE_STANDBY;
    }

    /**
     * 更新処理
     * @return true … 処理中
     */
    public function update():Boolean {
        
        // ロードタイムアウト
        if (state_ == STATE_LOAD) {
            var pass_time:int = getTimer() - loadStart_;
            if (pass_time > 8 * 1000) {
                // データとれない?
                // デフォアイコンでやってみるか・・・
                var entry_index:int = stage_index_ % game_work_.entry_list_.length;
                game_work_.entry_list_[entry_index].img_url =
                    "http://a3.twimg.com/sticky/default_profile_images/default_profile_1_normal.png";
                loadAndStart();
            }
        }
        
        if (state_ == STATE_GAME) {
            if (! hint_area_.visible) {
                // ヒント表示
                if (timer_.lap > (10 + stage_index_ * 2.5)  * 1000) {
                    hint_area_.visible = true;
                    BetweenAS3.tween(hint_area_, { alpha:1.0 }, { alpha:0.0 }, 1).play()
                    status_.appendText("\n答えはみどりのワクの中です。");
                }
            }
        }
        
        // Panelの更新
        updatePanels();

        // クリア判定
        if (isClearGame()) {
            resultGame();
        }

        return true;
    }

    /**
     * リストの更新通知
     */
    public function reloadList():void {
        
        // タイマークリア
        timer_.resetTimer();

        stage_index_ = 0;
        loadAndStart();
    }


    /**
     * マウス移動イベント
     */
    private function onMouseMove(evt:MouseEvent):void {
        if (state_ == STATE_GAME) {
            pitch_ = mouseX / WIDTH;
            head_ = mouseY / HEIGHT;
        }
    }

    /**
     * マウスクリックイベント
     */
    private function onMouseClick(evt:MouseEvent):void {
        if (state_ == STATE_CLEAR) {
            ++stage_index_;

            // 次のステージへ
            loadAndStart();
        }
    }
    
    private function onTweetButton(evt:MouseEvent):void {
        if (evt.eventPhase == EventPhase.AT_TARGET) {
            
            // ツブヤキ
            postToTwitter();
            
            // 後ろへイベントながさなーい
            evt.stopPropagation();
        }
    }

    /**
     * アイコンの読み込み
     */
    private function loadAndStart():void {
        var entry_index:int = stage_index_ % game_work_.entry_list_.length;
        
        stage_entry_ = EntryData(game_work_.entry_list_[entry_index]);

        var loader:Loader = new Loader();
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadIcon);
        var loader_context:LoaderContext = new LoaderContext(true);
        loader.load(new URLRequest(stage_entry_.img_url), loader_context);

        result_sprite_.visible = false;
        result_message_.visible = false;
        tw_btn_.visible = false;
        
        status_.text = "アイコンロード中…";
        loadStart_ = getTimer();

        state_ = STATE_LOAD;
    }

    /**
     * アイコン読み込みイベント
     */
    private function onLoadIcon(evt:Event):void {
        var src_bmd:BitmapData = evt.target.content.bitmapData;
        var src_bmd_width:int = src_bmd.width;
        var src_bmd_height:int = src_bmd.height;

        for (var h:int = 0; h < GameWork.ICON_SIZE; ++h) {
            for (var w:int = 0; w < GameWork.ICON_SIZE; ++w) {
                var src_x:int = w * Number(src_bmd_width) / GameWork.ICON_SIZE;
                var src_y:int = h * Number(src_bmd_height) / GameWork.ICON_SIZE;
                var color:uint = src_bmd.getPixel(src_x, src_y);
                stage_bmd_.setPixel(w, h, color);
            }
        }

        var mtx:Matrix = new Matrix();
        mtx.translate(-0.5 * src_bmd.width, -0.5 * src_bmd.height);
        result_sprite_.graphics.clear();
        result_sprite_.graphics.beginBitmapFill(src_bmd, mtx);
        result_sprite_.graphics.drawRect(-0.5 * src_bmd.width, -0.5 * src_bmd.height, src_bmd.width, src_bmd.height);
        result_sprite_.graphics.endFill();

        startGame();
    }

    /**
     * ゲーム開始処理
     */
    private function startGame():void {

        for (var h:int = 0; h < GameWork.ICON_SIZE; ++h) {
            for (var w:int = 0; w < GameWork.ICON_SIZE; ++w) {
                var panel:Panel = getPanel(w, h);
                if (panel == null) continue;

                panel.color = stage_bmd_.getPixel(w, h);
                panel.graphics.clear();
                panel.graphics.beginFill(panel.color);
                panel.graphics.lineStyle(1, 0x000000);
                panel.graphics.drawRect(
                    -GameWork.PANEL_SIZE / 2.0, -GameWork.PANEL_SIZE / 2.0,
                    GameWork.PANEL_SIZE, GameWork.PANEL_SIZE);
                panel.graphics.endFill();

                var z_range:Number = panel.z_range;        //PANEL_SIZE * ICON_SIZE;
                panel.position.z = (Math.random() - 0.5) * z_range;
            }
        }

        // クリアメッセージ登録
        result_message_.setName(stage_entry_.author_name, stage_entry_.link);
        result_message_.setMessage(stage_entry_.title);

        // 正解値設定
        offset_pitch_ = Math.random() * (0.98 - 0.02) + 0.02;
        offset_head_ = Math.random() * (0.98 - 0.02) + 0.02;

        // ヒントエリア設定
        var hint_size:Number = Math.min(0.1 + 0.025 * stage_index_ * stage_index_, 0.4);
        var aspect:Number = 0.5 + Math.random() * 1.5;
        var hint_width:Number = hint_size * aspect;
        var hint_height:Number = hint_size / aspect;
        var hint_pitch:Number = offset_pitch_ - Math.random() * hint_width * 0.8;
        var hint_head:Number = offset_head_ - Math.random() * hint_height * 0.8;
        
        if (hint_pitch < 0) hint_pitch = 0;
        else if (hint_pitch + hint_width > 1) hint_pitch = 1 - hint_width;
        if (hint_head < 0) hint_head = 0;
        else if (hint_head + hint_height > 1) hint_head = 1 - hint_height;

        hint_area_.graphics.clear();
        hint_area_.graphics.lineStyle(5, 0x00ff00, 0.4);
        hint_area_.graphics.drawRect(
                    hint_pitch * WIDTH, hint_head * HEIGHT,
                    hint_width * WIDTH, hint_height * HEIGHT);
        hint_area_.visible = false;
                    
        // パネル表示
        BetweenAS3.tween(panel_base_, { alpha:1.0 }, null, 0.5).play();

        // 時計開始
        timer_.startLap();
        
        if (stage_index_ == 0) {
            status_.text = "マウスを動かして隠された絵を探します。";
            if (frist_message_) {
                status_.appendText("\nまずは下の検索窓に好きな言葉を入れてみよう。");
                frist_message_ = false;
            }
        }
        else {
            status_.text = stage_index_ + 1 + "人目";
        }

        state_ = STATE_GAME;
    }

    /**
     * クリア画面の呼び出し
     */
    private function resultGame():void {
        // 時計記録
        timer_.stopLap();
        
        // ヒント消する
        hint_area_.visible = false;

        head_ = offset_head_;
        pitch_ = offset_pitch_;

        result_sprite_.visible = true;
        result_sprite_.width = GameWork.ICON_SIZE * GameWork.PANEL_SIZE;
        result_sprite_.height = GameWork.ICON_SIZE * GameWork.PANEL_SIZE;

        result_sprite_.x = panel_base_.x - GameWork.PANEL_SIZE / 2;
        result_sprite_.y = panel_base_.y - GameWork.PANEL_SIZE / 2;

        result_message_.visible = true;
        tw_btn_.visible = true;
        status_.text = (stage_index_ + 1) + "人目クリア!";

        var itw:ITween;
        itw = BetweenAS3.parallel(
            BetweenAS3.tween(panel_base_, { alpha:0.0 }, null, 1),
            BetweenAS3.tween(result_sprite_, { alpha:1.0 }, { alpha:0.0 }, 1),
            BetweenAS3.tween(result_message_, { scaleX:1 }, { scaleX:0 }, 0.5),
            BetweenAS3.tween(result_message_, { scaleY:1 }, { scaleY:0 }, 0.5),
            BetweenAS3.tween(result_message_, { alpha:1 }, { alpha:0 }, 0.5),
            BetweenAS3.delay(
                BetweenAS3.tween(tw_btn_, { alpha:1.0 }, { alpha:0.0 }, 0.2), 0.5)
            );
        
        itw.addEventListener(TweenEvent.COMPLETE, function():void {
            status_.appendText("\nクリックで次へ");
            state_ = STATE_CLEAR;
        });
        itw.play();

        state_ = STATE_CLEAR_CALL;
    }

    // クリア判定
    private function isClearGame():Boolean {
        if (state_ != STATE_GAME) return false;

        return (new Point(pitch_ - offset_pitch_, head_ - offset_head_)).length < 0.008;
    }

    /**
     * パネル 取得
     * @param    x    icon x座標
     * @param    y    icon y座標
     * @return    パネル
     */
    private function getPanel(x:int, y:int):Panel {
        return panel_list_[y * GameWork.ICON_SIZE + x];
    }

    /**
     * パネルの更新
     */
    private function updatePanels():void {
        var trans:Matrix3D = new Matrix3D();
        trans.appendRotation(180 * (pitch_ - offset_pitch_), new Vector3D(0, 1, 0));
        trans.appendRotation(180 * (head_ - offset_head_), new Vector3D(1, 0, 0));
        trans.append(lookat_);
        trans.append(local_);
        trans.append(screen_);

        for (var h:int = 0; h < GameWork.ICON_SIZE; ++h) {
            for (var w:int = 0; w < GameWork.ICON_SIZE; ++w) {
                var panel:Panel = getPanel(w, h);
                if (panel == null) continue;

                var new_pos:Vector3D = trans.transformVector(panel.position);
                new_pos.project();

                panel.x = new_pos.x;
                panel.y = new_pos.y;
                panel.z_value = new_pos.z;
            }
        }

        panel_order_.sort(function(l:int, r:int):Number {
            return panel_list_[r].z_value - panel_list_[l].z_value;
        });

        for (var i:int = 0; i < panel_order_.length; ++i) {
            panel_base_.setChildIndex(panel_list_[panel_order_[i]], i);
        }
    }
    
    /**
     * Twitter 投稿
     */
    private function postToTwitter():void {
        // クリア中のみ
        if (state_ != STATE_CLEAR) return;
        var post:String = "/「" + game_work_.search_word_ + "」で"
                        + (stage_index_ + 1) + "人クリアしました。[Time " + timer_.totalText() + "]"
                        + GameWork.TWEET_POST;

        navigateToURL(new URLRequest(
            "http://twitter.com/home?status=" + escapeMultiByte(post)),
            "_blank");
    }
}

/*
 * 結果画面
 * */
class ResultMessage extends Sprite {

    public static const WIDTH:int = 440;
    public static const HEIGHT:int = 120;

    private var name_:TextField;
    private var message_:TextField;

    public function ResultMessage() {
        // 背景枠
        graphics.lineStyle(2, 0xD0FFD0, 0.5);
        graphics.beginFill(0x000000, 0.5);
        graphics.drawRoundRect(-0.5 * WIDTH, -0.5 * HEIGHT, WIDTH, HEIGHT, 10, 10);
        graphics.endFill();
        mouseEnabled = false;

        name_ = addChild(Utility.createTextField(16, 0xFFD0D0)) as TextField;
        name_.htmlText = "";
        name_.x = -0.5 * WIDTH + 10;
        name_.y = -0.5 * HEIGHT + 5;
        name_.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void {
            if (evt.eventPhase == EventPhase.AT_TARGET) {
                evt.stopPropagation();
            }
        });
        
        message_ = addChild(Utility.createTextField(12, 0xFFFFFF)) as TextField;
        message_.text = "";
        message_.autoSize = TextFieldAutoSize.NONE;
        message_.multiline = true;
        message_.wordWrap = true;
        message_.width = WIDTH - 40;
        message_.height = HEIGHT;
        message_.x = -0.5 * WIDTH + 20;
        message_.y = -0.5 * HEIGHT + 35;
        message_.mouseEnabled = false;
    }

    /**
     * 名前の設定
     * @param    name    名前
     * @param    link    リンク
     */
    public function setName(name:String, link:String = null):void {
        if (link) {
            name_.htmlText = "<a href=\"" + link + "\" target=\"_blank\">" + name + "</a>";
        }
        else {
            name_.htmlText = name;
        }
    }


    /**
     * メッセージ本文
     * @param    msg
     */
    public function setMessage(msg:String):void {
        message_.text = msg;
    }
}

/**
 * 時計
 */
class GameTimer extends Sprite {

    private static const STATE_STOP:int = 0;
    private static const STATE_RUN:int = 1;
    private static const STATE_PAUSE:int = 2;
    
    private var total_:int;
    private var lap_:int;
    private var lap_start_:int;
    private var pause_start_:int;
    private var state_:int;
    private var lap_text_:TextField;
    private var total_text_:TextField;
    
    public function GameTimer() {
        lap_ = 0;
        total_ = 0;
        state_ = STATE_STOP;
        
        graphics.beginFill(0x000020, 0.6);
        graphics.drawRoundRect(0, 0, 100, 50, 20, 20);
        graphics.endFill();
        
        total_text_ = addChild(Utility.createTextField(18, 0xFFFFFF)) as TextField;
        total_text_.text = "00:00:00";
        total_text_.x = 10;
        total_text_.y = 2;

        lap_text_ = addChild(Utility.createTextField(18, 0xFFFFFF)) as TextField;
        lap_text_.text = "00:00:00";
        lap_text_.x = 10;
        lap_text_.y = 20;
        
        addEventListener(Event.ENTER_FRAME, onEnterFrame);
    }
    
    /**
     * タイマーリセット
     */
    public function resetTimer():void {
        lap_ = 0;
        total_ = 0;
        state_ = STATE_STOP;
    }
    
    /**
     * ラップ開始
     */
    public function startLap():void {
        if (state_ != STATE_STOP) {
            stopLap();
        }
        
        lap_start_ = getTimer();
        state_ = STATE_RUN;
    }
    
    /**
     * ラップ停止
     */
    public function stopLap():void {
        switch(state_) {
        case STATE_STOP :
            return;
        case STATE_PAUSE :
            resumeLap();    // 一度再開する
        default :
        }
        
        lap_ = getTimer() - lap_start_;
        total_ += lap_;
        state_ = STATE_STOP;
    }
    
    /**
     * ラップ時間
     */
    public function get lap():int { return lap_ }

    /**
     * トータル時間文字列
     * @return 文字列
     */
    public function totalText():String {
        return total_text_.text;
    }
    
    /**
     * ラップポーズ
     */
    private function pauseLap():void {
        if (state_ != STATE_RUN) return;
        // ポーズ
        pause_start_ = getTimer();
        state_ = STATE_PAUSE;
    }
    
    /**
     * ラップ再開
     */
    private function resumeLap():void {
        if (state_ != STATE_PAUSE) return;
        // 再開
        var now_time:int = getTimer();
        lap_start_ += now_time - pause_start_;
        lap_ = now_time - lap_start_;
        state_ = STATE_RUN;

        drawTimer();    // 更新
    }
    
    /**
     * 更新処理
     */
    private function onEnterFrame(evt:Event):void {
        if (state_ == STATE_RUN) {
            lap_ = getTimer() - lap_start_;
        }
        
        drawTimer();
    }
    
    /**
     * 描画する
     */
    private function drawTimer():void {
        if (state_ == STATE_STOP) return;
        // ラップタイム
        lap_text_.text = createTimerString(lap_);
        // トータルタイム
        total_text_.text = createTimerString(total_ + lap_);
    }
    
    /**
     * 時間文字列作成
     * @param    time    時間
     * @return    00:00:00の形式で出力
     */
    private function createTimerString(time:int):String {
        // ラップタイム
        var lap_cnm:int = time / 10;
        var lap_sec:int = lap_cnm / 100;
        var lap_min:int = lap_sec / 60;
        lap_cnm %= 100;        lap_sec %= 60;        lap_min %= 60;

        return "" + int(lap_min / 10) + int(lap_min % 10)
                            + ":" + int(lap_sec / 10) + int(lap_sec % 10)
                            + ":" + int(lap_cnm / 10) + int(lap_cnm % 10);
    }
}

/**
 * Twitter情報読み込み
 */
class TwitterXmlLoader extends EventDispatcher  {

    private var xmlLoader_:URLLoader;
    private var standby_:Boolean;
    public var xmlObj:XML;

    /**
     * コンストラクタ
     */
    function TwitterXmlLoader() {
        //URLLoader作成
        xmlLoader_ = new URLLoader();
        //読み込み完了イベントのイベントリスナーになる
        xmlLoader_.addEventListener(Event.COMPLETE, onXMLloaded);
        xmlLoader_.addEventListener(IOErrorEvent.IO_ERROR, onIoError);
        standby_ = true;
    }

    /**
     * 読み込み完了
     */
    private function onXMLloaded(evt:Event):void {
        //XMLオブジェクトに変換する
        xmlObj = new XML(xmlLoader_.data);
        //カスタムイベントを配信する
        dispatchEvent(new Event(Event.COMPLETE));
        standby_ = true;
    }

    /**
     * エラー処理
     */
    private function onIoError(evt:IOErrorEvent):void {
        standby_ = true;
    }

    /**
     * ユーザー情報取得
     * @param    user 対象のユーザー名
     */
    public function loadSearchXml(search_word:String):Boolean {
        if (! standby_) return false;
        //URL
        var xmlUrl:String = "http://search.twitter.com/search.atom?rpp=100&q=";
        xmlUrl += encodeURIComponent(search_word);
        //読み込み開始
        xmlLoader_.load(new URLRequest(xmlUrl));
        standby_ = false;

        return true;
    }
}

/**
 * 汎用処理
 */
class Utility {

    /**
     * テキストフィールドの生成
     * @param    size    フォントサイズ
     * @param    color    フォントカラー
     * @param    align    アライン
     * @return    テキストフィールドインスタンス
     */
    public static function createTextField(
        size:int, color:int, line_color:int = -1, shadow:Boolean = false,
        align:String = TextFormatAlign.LEFT):TextField {

        var tf:TextField = new TextField();
        tf.defaultTextFormat = new TextFormat("Arial", size, color, true, null, null, null, null, align);
        tf.autoSize = TextFieldAutoSize.LEFT;
        tf.selectable = false;
        
        var filter:Array = [];
        
        if (line_color >= 0) filter.push(new GlowFilter(line_color, 1, 4, 4, 3, 1));
        if (shadow) filter.push(new DropShadowFilter(2, 45, 0x000000, 0.4, 4, 4, 3));
        
        if (filter.length > 0) {
            tf.filters = filter;
        }

        return tf;
    }


    /**
     * 正射影行列を得る。
     */
    public static function createOrtho(
            left:Number, right:Number, bottom:Number, top:Number, near:Number, far:Number):Matrix3D {
        var div_r_l:Number = 1.0 / (right - left);
        var div_t_b:Number = 1.0 / (top - bottom);
        var div_n_f:Number = 1.0 / (near - far);

        var raw_data:Vector.<Number> = new Vector.<Number>(16);

        raw_data[0 * 4 + 0] = 2.0 * div_r_l;
        raw_data[1 * 4 + 1] = 2.0 * div_t_b;
        raw_data[2 * 4 + 2] = 2.0 * div_n_f;
        raw_data[3 * 4 + 0] = -(right + left) * div_r_l;
        raw_data[3 * 4 + 1] = -(top + bottom) * div_t_b;
        raw_data[3 * 4 + 2] = (near * far) * div_n_f;
        raw_data[3 * 4 + 3] = 1.0;

        return new Matrix3D(raw_data);
    }


    /**
     *       LookAt行列を得る。
     * @param eye            視点ポジション
     * @param target        注視ポジション
     * @param up            カメラ上方
     * @return    LookAt行列
     */
    static public function createLookAt(
            eye:Vector3D, target:Vector3D, up:Vector3D):Matrix3D {

        var d0:Number, d1:Number, d2:Number;
        var r0:Vector3D, r1:Vector3D, r2:Vector3D;
        var n_eye:Vector3D;
        r2 = eye.subtract(target);
        r2.normalize();
        r0 = up.crossProduct(r2);
        r0.normalize();
        r1 = r2.crossProduct(r0);
        n_eye = eye.clone();
        n_eye.scaleBy( -1.0);
        d0 = r0.dotProduct(n_eye);
        d1 = r1.dotProduct(n_eye);
        d2 = r2.dotProduct(n_eye);

        var raw_data:Vector.<Number> = new Vector.<Number>(16);

        raw_data[0] = r0.x;        raw_data[1] = r1.x;        raw_data[2] = r2.x;
        raw_data[4] = r0.y;        raw_data[5] = r1.y;        raw_data[6] = r2.y;
        raw_data[8] = r0.z;        raw_data[9] = r1.z;        raw_data[10] = r2.z;
        raw_data[12] = d0;        raw_data[13] = d1;
        raw_data[14] = d2;        raw_data[15] = 1.0;

        return new Matrix3D(raw_data);
    }

    /**
     * スクリーン行列の作成
     * @param    x    左上オフセット
     * @param    y    左上オフセット
     * @param    w    幅
     * @param    h    高さ
     * @return        スクリーン行列
     */
    static public function createViwPort(
        x:Number, y:Number, w:Number, h:Number): Matrix3D {

        var w2:Number = w * 0.5;
        var h2:Number = h * 0.5;

        var raw_data:Vector.<Number> = new Vector.<Number>(16);

        raw_data[0] = w2;
        raw_data[5] = h2;
        raw_data[10] = 1000;
        raw_data[12] = x + w2;
        raw_data[13] = y + h2;
        raw_data[14] = 0;
        raw_data[15] = 1;

        return new Matrix3D(raw_data);
    }
}