forked from: TypeShoot

by hacker_1_l2muzf forked from TypeShoot (diff: 49)
Create a Flash app using Union Platform,
* where you can collaborate with more than 4 people online.
*
* UnionRamen is an example app,
* you can write code based on this, or build from scratch.
*
* UnionRamen is a multiuser bowl of ramen built on the Union Platform.
* Press the 'n' key to add naruto to the bowl.
* For Union Platform documentation, see www.unionplatform.com.
* 
* @author   Colin Moock
* @date     July 2009
* @location Toronto, Canada

■ TypeShoot ver 1.0.0.20090823.1 
* 
* これはタイピングゲームです。
* ワードを打つとミサイルを発射し、敵陣営に到達すると敵のダメージとなります。
* 一定量のダメージを与えると勝利となります。
* 
* 敵が発射したミサイルはワードを打って撃墜できます。
* 自陣を守って敵陣を落としましょう!
* 
* 日本語ワードを入力するときは、ローマ字表現を英字で打ち込んでください。
* かな入力は未対応です・・。
* 
* 攻撃ワードは、ゲーム中に現れるワードは全て受け付けますが、
* 入力窓のすぐ上にあるワードを打つとダメージが2倍のミサイルになります。
* また、アタックコンボボーナスの対象となります。
* 
* 攻撃・防御をミスタイプなしで連続して行うと、コンボ数がカウントされます。
* 一定値に達すると、攻撃・防御の際にボーナス攻撃が発生します。
* 攻撃コンボ→ダブルアタック:一定確率で2発同時に発射します。
* 防御コンボ→オートリフレクション:撃墜したワードを一定確率でそのまま打ち返します。
* 
* ■ 戦闘中のショートカットキー
* F1 チャット入力
* TAB 防御対象の切り替え
* ESC 防御入力中のワードの取り消
♥0 | Line 4014 | Modified 2009-08-23 22:47:20 | MIT License
play

ActionScript3 source code

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

// forked from keno42's TypeShoot
// forked from checkmate's colin challenge for professionals
/**
 * Create a Flash app using Union Platform,
 * where you can collaborate with more than 4 people online.
 *
 * UnionRamen is an example app,
 * you can write code based on this, or build from scratch.
 *
 * UnionRamen is a multiuser bowl of ramen built on the Union Platform.
 * Press the 'n' key to add naruto to the bowl.
 * For Union Platform documentation, see www.unionplatform.com.
 * 
 * @author   Colin Moock
 * @date     July 2009
 * @location Toronto, Canada
 */

 /**
  * ■ TypeShoot ver 1.0.0.20090823.1 
  * 
  * これはタイピングゲームです。
  * ワードを打つとミサイルを発射し、敵陣営に到達すると敵のダメージとなります。
  * 一定量のダメージを与えると勝利となります。
  * 
  * 敵が発射したミサイルはワードを打って撃墜できます。
  * 自陣を守って敵陣を落としましょう!
  * 
  * 日本語ワードを入力するときは、ローマ字表現を英字で打ち込んでください。
  * かな入力は未対応です・・。
  * 
  * 攻撃ワードは、ゲーム中に現れるワードは全て受け付けますが、
  * 入力窓のすぐ上にあるワードを打つとダメージが2倍のミサイルになります。
  * また、アタックコンボボーナスの対象となります。
  * 
  * 攻撃・防御をミスタイプなしで連続して行うと、コンボ数がカウントされます。
  * 一定値に達すると、攻撃・防御の際にボーナス攻撃が発生します。
  * 攻撃コンボ→ダブルアタック:一定確率で2発同時に発射します。
  * 防御コンボ→オートリフレクション:撃墜したワードを一定確率でそのまま打ち返します。
  * 
  * ■ 戦闘中のショートカットキー
  * F1 チャット入力
  * TAB 防御対象の切り替え
  * ESC 防御入力中のワードの取り消し・防御対象の取り消し
  * ←→ 防御中の自機を左右に移動
  * ↑ 攻撃モードに切り替え
  * ↓ 防御モードに切り替え
  * 
  * ■ SETTINGS画面の説明
  * YOUR NAMEの入力欄で名前を変更できます。空欄は受け付けません。
  * 
  * ATTACK LANGUAGEで攻撃ワード候補がどの言語で現れるかを選択できます。
  * 防御ワードは敵が打ったものがそのまま出てくるので、ここでは制御できません。
  * 
  * COLOR SETTINGで自機の色を変えられます。
  * ゲーム回数を重ねると選択できる色が増えます。最大20色です。
  * 
  * チャット画面でユーザー名の横に表示されるかっこ内の数字は、
  * それぞれ攻撃時/防御時のそのプレイヤーの最大毎秒タイプ数です。
  * チームを選択する際におおまかな目安として利用してください。
  * 
  * VS COMを押すとコンピュータと対戦できます。
  * 勝敗に応じてコンピュータのレベルが変わります。
  * 
  * ■ ■ ■ あなたがこのゲームを楽しめますように!
  * 
  * 制作 Kenichi UENO (Keno)
  * 音素材 Yoichi KANEKO / ザ・マッチメイカァズ2nd 【フリー効果音素材】
  * テストプレイやアドバイスなど LOGOSWARE PDGの皆さん / 某黒猫の皆さん
  * 
  * 遊んでくれた人 YOU!
  */

package  
{
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import net.user1.logger.Logger;
	import net.user1.reactor.Reactor;
	import net.user1.reactor.ReactorEvent;
	import net.user1.reactor.Room;
	import net.user1.utils.LocalData;
	
        [SWF(width = "465", height = "465", backgroundColor = "0", fps = "30")]
	public class Missiles extends Sprite
	{
		protected var reactor:Reactor = new Reactor();
		protected var gameController:GameController;
		
		protected var enterUI:EnterUI = new EnterUI();
		protected var infoUI:InfoUI = new InfoUI();
		protected var gameStage:GameStage = new GameStage();
		
		protected var nowProgress:Number = 0;
		
		public function Missiles() 
		{
			SoundManager.initChecker.addEventListener(ProgressEvent.PROGRESS, onProgress);
			SoundManager.init();
			Score.initScore();
			Score.lastLevel = Score.getLevel();
			Roma.init();
			this.graphics.lineStyle(2, 0x00FF00);
			this.graphics.moveTo(0, 230);
			this.addEventListener(Event.ENTER_FRAME, onProgress);
			connect();
			
			Log.init(stage);
			Log.out("test");
		}
		
		// サウンドのロードエフェクト + ついでにunion接続
		private function onProgress(e:Event):void {
			nowProgress = nowProgress + 0.25 * (((SoundManager.count + (reactor.isConnected()?1:0)) / (SoundManager.countMax+1)) - nowProgress);
			this.graphics.lineTo(465 * nowProgress, 230);
			if ( nowProgress >= 0.99 ) {
				this.alpha *= 0.75;
				if ( this.alpha < 0.1 ) {
					this.removeEventListener(Event.ENTER_FRAME, onProgress);
					this.graphics.clear();
					this.alpha = 1.0;
//					start();
					gameStart();
				}
			}
		}
		// 入室画面表示
		private function start():void{
			this.addChild( enterUI ); // 名前いれないほうが入りやすいかなあ
			enterUI.x = 0.5 * (465 - enterUI.width);
			enterUI.y = 0.5 * (465 - enterUI.height);
			enterUI.addEventListener("enter", onEnter);
		}
		protected function onEnter(e:Event):void {
			enterUI.removeEventListener("enter", onEnter);
			gameStart();
		}
		protected function connect():void{
			reactor.addEventListener(ReactorEvent.READY, onProgress);
			reactor.connect("tryunion.com", 9100);
		}
		protected function gameStart():void{
			reactor.getLog().setLevel(Logger.FATAL);
			ServerClockUtil.init( reactor.getServer() );
//			this.removeChild( enterUI );
			this.addChild( gameStage );
			this.addChild( infoUI );
			infoUI.x = infoUI.y = 30;
			infoUI.visible = false;
			gameController = new GameController(reactor, infoUI, gameStage); // 選択画面とゲーム画面をコントロール
			
			gameController.startInfo();
		}
	}
}
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.filters.BitmapFilter;
	import flash.filters.BlurFilter;
	import flash.filters.GlowFilter;
	import flash.geom.Rectangle;
	import flash.net.navigateToURL;
	import flash.system.IME;
	import flash.system.IMEConversionMode;
	import flash.system.Capabilities;
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.SpreadMethod;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.events.ProgressEvent;
	import flash.events.TextEvent;
	import flash.events.TimerEvent;
	import flash.filters.ColorMatrixFilter;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	import flash.net.URLRequest;
	import flash.system.System;
	import flash.text.engine.FontDescription;
	import flash.text.Font;
	import flash.text.TextField;
	import flash.text.TextFieldType;
	import flash.text.TextFormat;
	import flash.ui.Keyboard;
	import flash.ui.Mouse;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	import flash.utils.escapeMultiByte;
	import flash.utils.getTimer;
	import flash.utils.Timer;
	import net.user1.reactor.Client;
	import net.user1.reactor.CustomClient;
	import net.user1.reactor.filters.AttributeComparison;
	import net.user1.reactor.filters.AttributeFilter;
	import net.user1.reactor.filters.CompareType;
	import net.user1.reactor.filters.Filter;
	import net.user1.reactor.filters.IFilter;
	import net.user1.reactor.IClient;
	import net.user1.reactor.MessageListener;
	import net.user1.reactor.RoomEvent;
	import net.user1.reactor.Server;
	import net.user1.reactor.ServerEvent;
	import net.user1.utils.LocalData;
	import net.user1.reactor.Room;
	import net.user1.reactor.Reactor;
	import net.user1.utils.UDictionary;
	
	// 背景エフェクト
	class BackGround extends Bitmap {
		protected var bd:BitmapData = new BitmapData(465, 300, true, 0x0);
		protected var p:Point = new Point();
		protected var filter:BitmapFilter = new BlurFilter();
		public var sprite:Sprite;
		public function BackGround(sprite:Sprite) {
			this.sprite = sprite;
			this.bitmapData = bd;
			this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		protected function onEnterFrame(e:Event):void {
			bd.draw(sprite);
			bd.applyFilter(bd, bd.rect, p, filter);
		}
		public function setFilter(type:int):void {
			switch(type) {
				case 0:
					filter = new ColorMatrixFilter([0.98, 0, 0, 0, 0, 0, 0.98, 0, 0, 0, 0, 0, 0.9, 0, 0, 0, 0, 0, 0.98, 0]);
					break;
				case 1:
					filter = new BlurFilter(10, 2, 2);
					break;
			}
		}
	}
	
	
	class EffectSprite extends Sprite {
		public function EffectSprite() {
			this.mouseEnabled = this.mouseChildren = false;
		}
		public function missileDestroy(p:Point):void {
			var am:AttackMotion = new AttackMotion();
			
			this.addChild( am );
			am.x = p.x;
			am.y = p.y;
			for ( var i:int = 0; i < 5; i++ ){
				var yakochu:Yakochu = new Yakochu();
				this.addChild( yakochu );
				yakochu.x = p.x;
				yakochu.y = p.y;
			}
		}
	}
	
	class Yakochu extends Sprite {
		private var vx:Number = Math.random() * 2.0 - 1;
		private var vy:Number = Math.random() * 2.0 - 1;
		private var ax:Number = Math.random() * 0.5 - 0.25;
		private var ay:Number = Math.random() * 0.5 - 0.25;
		public function Yakochu():void {
			graphics.beginFill(Math.random() * 0xFFFFFF | 0xAAAAAA, 0.5);
			graphics.drawCircle(0, 0, Math.random() * 2);
			this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
			this.alpha = 6;
		}
		protected function onEnterFrame(e:Event):void {
			vx += ax; vy += ay;
			this.x += vx;
			this.y += vy;
			this.alpha *= 0.94;
			if ( this.alpha < 0.1 ) {
				this.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
				if ( this.parent != null ) this.parent.removeChild( this );
			}
		}
	}
	
	// 攻撃用ワード表示
	class AttackQuestion extends Sprite {
		protected var tf:TextField = new TextField();
		protected var word:String = "";
		protected var jWord:Array;
		public var isJapanese:Boolean;
		protected var missile:Sprite = new Sprite();
		protected var angle:Number = 0;
		public function AttackQuestion() {
			missile.graphics.lineStyle(0, 0x00FF00);
			missile.graphics.moveTo(15, 0);
			missile.graphics.lineTo(0, 5);
			missile.graphics.lineTo(0, -5);
			missile.graphics.lineTo(15, 0);
			tf.defaultTextFormat = new TextFormat(TextFieldUtil.getFont(), 14, 0xFFFFFF);
			tf.autoSize = "left";
			this.addChild( missile );
			this.addChild( tf );
			tf.x = 17;
			tf.y = -11;
			do {
				isJapanese = Score.isJapanese();
				if ( isJapanese ) {
					jWord = Words.getJRandom();
					//word = jWord[0]; // 漢字
					word = jWord[1]; // ひらがな
					//word = Roma.getRomaToType(jWord[1]).toLowerCase(); // ローマ字
				} else {
					word = Words.getRandom();
				}
				tf.text = HandicapUtil.getHandicapWord(word);
			} while( tf.text.length > 26 ) // 最大26文字
			this.mouseChildren = false;
		}
		// 現在有効なワードを明るくする
		public function setBright():void {
			missile.graphics.beginFill(0x88FF88);
			missile.graphics.moveTo(15, 0);
			missile.graphics.lineTo(0, 5);
			missile.graphics.lineTo(0, -5);
			missile.graphics.lineTo(15, 0);
			this.addEventListener(Event.ENTER_FRAME, onEnterFrame, false, 0, true);
		}
		protected function onEnterFrame(e:Event):void {
			angle+=8.5;
			missile.rotationX = angle;
		}
		// このワードのストリング値を返す
		public function getText():String {
			return word;
		}
	}
	
	// 攻撃用ユーザーインターフェース
	class AttackUI extends Sprite {
		protected var input:TextField = new TextField();
		protected var questionNum:int = 3;
		protected var questionList:Array = [];
		public function AttackUI() {
			TextFieldUtil.setupBorder(input);
			input.width = 210;
			input.height = 20;
			input.type = "input";
			input.y = 42;
			this.addChild( input );
			
			input.addEventListener( KeyboardEvent.KEY_DOWN, onKeyDown );
			input.addEventListener( KeyboardEvent.KEY_UP, onKeyUp );
		}
		protected var addToType:int = 0;
		public function setup():void {
			for ( var i:int = 0; i < questionNum; i++ ) {
				questionList.push(new AttackQuestion());
			}
			for ( i = 0; i < questionNum; i++ ) {
				addChild( questionList[i] );
			}
			for ( i = 1; i < questionNum; i++ ) {
				questionList[i].y = questionList[i-1].y -13;
			}
			
			questionList[0].setBright();
			setFocus();
			this.addEventListener( Event.ENTER_FRAME, onEnterFrame);
		}
		public function reset():void {
			this.removeEventListener( Event.ENTER_FRAME, onEnterFrame);
			while ( questionList.length ) {
				removeChild(questionList[questionList.length-1]);
				questionList.pop();
			}
			input.text = "";
			addToType = 0;
		}
		// 入力部分にフォーカスをあてる
		public function setFocus():void {
			if ( this.stage != null ) {
				stage.focus = input;
				if ( Capabilities.hasIME ) {
					try {
						IME.enabled = false;
					} catch(e:*){}
				}
			}
		}
		// 
		protected function onEnterFrame(e: Event):void {
			if ( questionList[0].y < 28 ) {
				for ( var i:int = 0; i < questionNum; i++ )
					questionList[i].y ++;
			}
		}
		protected var beforeIMEText:String = "";
		protected function onKeyUp(e:KeyboardEvent):void {
			if ( beforeIMEText != "" ) input.text = beforeIMEText;
			if ( textClear ) {
				input.text = "";
				textClear = false;
			}
		}
		protected var textClear:Boolean = false;
		protected function onKeyDown(e:KeyboardEvent):void {
			if ( e.keyCode == 229 ) {
				if ( Capabilities.hasIME ) {
					try {
						IME.enabled = false;
					} catch(e:*){}
				}
				beforeIMEText = input.text;
				return;
			} else {
				beforeIMEText = "";
			}
			if ( e.keyCode == Constants.CHAT_KEY ) return;
			if ( e.keyCode == Keyboard.DOWN || e.keyCode == Keyboard.UP ) return;
			if ( e.keyCode == Keyboard.ENTER || e.keyCode == Keyboard.SPACE ) {
				var word:String = HandicapUtil.getUnHandicapWord(input.text);
				var power:Number = 0;
				
				if ( input.text.length <= addToType ) {
					// 選択状態のお方でいらっしゃいますか
					if ( questionList[0].isJapanese ) {
						var jword:String = Roma.getHiragana(word); // ひらがなモード用
						if ( questionList[0].getText() == jword ) word = jword;
					}
					if ( questionList[0].getText() == word ) { // 選択ワードと一致しました
						power = 1;
						removeChild( questionList[0] );
						questionList.shift();
						questionList[0].setBright();
						var aq:AttackQuestion = new AttackQuestion();
						aq.y = questionList[questionNum-2].y - 13;
						questionList.push( aq );
						addChild(aq);
						if ( input.text.length == addToType ) {
							Score.attackCombo++;
							if ( Score.attackCombo == Constants.ATTACK_COMBO_START ) {
								dispatchEvent(new GameEvent(GameEvent.ATTACK_COMBO_START, true));
								SoundManager.ComboSound.play();
							}
						}
					} else {
						if ( Words.isExist( word ) ) {
							power = 0.5;
						} else if ( Words.isJExist( word ) ) {
							word = Roma.getHiragana( word );
							power = 0.5;
						}
					}
					if ( (power == 0) || (input.text.length != addToType) ) {
						if ( Score.attackCombo >= Constants.ATTACK_COMBO_START ) {
							dispatchEvent(new GameEvent(GameEvent.ATTACK_COMBO_END, true));
						}
						Score.attackCombo = 0;
					}
					if ( power > 0 ) {
						Score.correct += input.text.length;
						Score.type += addToType;
						addToType = 0;
						dispatchEvent(new GameEvent(GameEvent.ATTACK, true, false, word + "-" + power));
						if ( Score.getAttackBonusRate() > (Math.random() * 100) ) 
							dispatchEvent(new GameEvent(GameEvent.ATTACK_DOUBLE, true, false, word + "-" + power));
					}
				}
//				input.text = "";
				textClear = true;
			} else {
				if ( e.keyCode == Keyboard.LEFT || e.keyCode == Keyboard.RIGHT ) {
					e.stopPropagation();
				}
				addToType++;
			}
			
		}
	}
	class HandicapUtil {
		public static function getUnHandicapWord(text:String):String {
			return text;
/*			var temp:Array = text.split(" ");
			var flg:Boolean = true;
			for ( var i:int = 1; i < temp.length; i++ ) {
				flg = flg && (temp[0] == temp[i]);
			}
			if ( flg && (Score.handicap == temp.length) ) {
				return temp[0];
			} else {
				return "";
			}
*/
		}
		public static function getHandicapWord(text:String):String {
			return text;
/*
			var ret:String = text;
			for ( var i:int = 1; i < Score.handicap; i++ ) {
				ret += " " + text;
			}
			return ret;
*/
		}

	}
	
	class DefenceUI extends Sprite {
		public function DefenceUI() {
		}
		public function reset():void {
			//
		}
		public function set enable(value:Boolean):void {
			if( value ){
				this.stage.addEventListener( KeyboardEvent.KEY_DOWN, onKeyDown );
			}else {
				if( this.stage != null )
					this.stage.removeEventListener( KeyboardEvent.KEY_DOWN, onKeyDown );
			}
		}
		protected function onKeyDown(e:KeyboardEvent):void {
			dispatchEvent(new GameEvent(GameEvent.DEFENCE_TYPE, true, false, String(e.charCode)));
		}
	}
	
	class ScoreUI extends Sprite {
		protected var baseTF:TextField = new TextField();
		protected var scoreTF:TextField = new TextField();
		public function ScoreUI() {
			baseTF.defaultTextFormat = new TextFormat(TextFieldUtil.getFont(), null, 0xFFFFFF, false, null, null, null, null, null, null, null, null, 2);
			scoreTF.autoSize = "right";
			scoreTF.defaultTextFormat = new TextFormat(TextFieldUtil.getFont(), null, 0xFFFFFF, false, null, null, null, null, "right", null, null, null, 2);
			baseTF.autoSize = "left";
			
			var timer:Timer = new Timer( 1000 );
			timer.start();
			timer.addEventListener(TimerEvent.TIMER, onTimer);
			updateScore();
			
			baseTF.text = "TOTAL TYPE:\nATTACK COMBO:\nDEFENCE COMBO:\nSCORE:\nBONUS:";
			this.addChild( baseTF );
			this.addChild( scoreTF );
		}
		protected function onTimer(e:TimerEvent):void {
			updateScore();
		}
		protected function updateScore():void{
			var typeStr:String = String(Score.type);
			if ( Score.type > 0 ) typeStr += ( "(" + int(100*Score.correct / Score.type) + "%)");
			scoreTF.text = typeStr + "\n" + int(Score.attackCombo) + "\n" + int(Score.defenceCombo) + "\n" + int(Score.score) + "\n";
			if ( Score.attackCombo < Constants.ATTACK_COMBO_START && Score.defenceCombo < Constants.DEFENCE_COMBO_START ) {
				scoreTF.appendText( "NONE" );
			} else {
				if ( Score.attackCombo >= Constants.ATTACK_COMBO_START ) {
					scoreTF.appendText( "[DA "+ Score.getAttackBonusRate() +"%]" );
				}
				if ( Score.defenceCombo >= Constants.DEFENCE_COMBO_START ) {
					scoreTF.appendText( "[AR "+ Score.getDefenceBonusRate() +"%]" );
				}
			}
			scoreTF.x = 130 - scoreTF.width;
		}
		
	}
	
	class OperationUI extends Sprite {
		protected var attackerBtn:Button = new Button(90, 25, " ATTACK MODE");
		protected var defenderBtn:Button = new Button(90, 25, "DEFENCE MODE");
		
		protected var attackUI:AttackUI = new AttackUI();
		protected var defenceUI:DefenceUI = new DefenceUI();
		protected var scoreUI:ScoreUI = new ScoreUI();
		
		protected var playMode:String;
		public function OperationUI() {
			this.addChild( attackerBtn );
			this.addChild( defenderBtn );
			this.addChild( attackUI );
			this.addChild( defenceUI );
			this.addChild( scoreUI );
			
			attackUI.visible = defenceUI.visible = defenceUI.enable = false;
			attackerBtn.x = 10;
			attackerBtn.y = 15;
			defenderBtn.x = 10;
			defenderBtn.y = 50;
			attackUI.x = 110;
			attackUI.y = 15;
			defenceUI.x = 110;
			defenceUI.y = 15;
			scoreUI.x = 330;
			scoreUI.y = 10;
			attackerBtn.addEventListener(MouseEvent.CLICK, onAttackerClick);
			defenderBtn.addEventListener(MouseEvent.CLICK, onDefenderClick);
		}
		public function getPlayMode():String {
			return playMode;
		}
		public function setup():void {
			attackUI.setup();
		}
		public function reset():void {
			attackUI.reset();
			defenceUI.reset();
		}
		protected function tempDisabled():void {
			this.mouseChildren = false;
		}
		public function switchMode():void {
			if ( playMode == Constants.PLAY_MODE_ATTACKER ) {
				SoundManager.ClickSound.play();
				dispatchEvent( new GameEvent(GameEvent.MODE_DEFENCE, true) );
			} else if ( playMode == Constants.PLAY_MODE_DEFENDER ) {
				SoundManager.ClickSound.play();
				dispatchEvent( new GameEvent(GameEvent.MODE_ATTACK, true) );
			}
		}
		// 基本的にmessage経由で呼び出す
		public function setMode(playMode:String):void {
			this.playMode = playMode;
			if ( playMode == Constants.PLAY_MODE_ATTACKER ) {
				attackerBtn.selected = true;
				defenderBtn.selected = false;
				attackUI.visible = true;
				defenceUI.visible = false;
				defenceUI.enable = false;
				setFocus();
			} else if ( playMode == Constants.PLAY_MODE_DEFENDER ) {
				attackerBtn.selected = false;
				defenderBtn.selected = true;
				attackUI.visible = false;
				defenceUI.visible = true;
				defenceUI.enable = true;
			} else if ( playMode == "" ) {
				attackerBtn.selected = false;
				defenderBtn.selected = false;
				attackUI.visible = false;
				defenceUI.visible = false;
				defenceUI.enable = false;
			}
		}
		public function setFocus():void {
			attackUI.setFocus();
		}
		protected function onAttackerClick(e:MouseEvent):void {
			if( !attackerBtn.selected ){
				attackerBtn.selected = defenderBtn.selected = false;
				dispatchEvent( new GameEvent(GameEvent.MODE_ATTACK, true) );
			}
		}
		protected function onDefenderClick(e:MouseEvent):void {
			if( !defenderBtn.selected ){
				attackerBtn.selected = defenderBtn.selected = false;
				dispatchEvent( new GameEvent(GameEvent.MODE_DEFENCE, true) );
			}
		}
	}
	
	class Battery extends Sprite {
		protected var base:Sprite = new Sprite();
		protected var cannonBarrel:Sprite = new Sprite();
		protected var attackBase:Sprite = new Sprite();
		protected var tf:TextField = new TextField();
		protected var color:int;
		protected var baseColor:int;
		protected var attackComboEffect:Sprite = new Sprite();
		protected var defenceComboEffect:Sprite = new Sprite();
		public function Battery(isMySide:Boolean, baseLevel:int, color:int, baseColor:int) {
			draw(baseLevel, color, baseColor);
			this.addChild( attackBase );
			this.addChild( base );
			base.addChild( cannonBarrel );
			this.addChild( tf );
			tf.autoSize = "left";
			tf.defaultTextFormat = new TextFormat(TextFieldUtil.getFont(), null, 0xFFFFFF);
			setSide(isMySide);
			attackComboEffect.graphics.lineStyle(0, 0xFF0000);
			attackComboEffect.graphics.beginFill(0xFF8888, 0.5);
			attackComboEffect.graphics.drawCircle(0, 0, 50);
			attackComboEffect.alpha = 1.0;  attackComboEffect.scaleX = attackComboEffect.scaleY = 0;
			defenceComboEffect.graphics.lineStyle(0, 0x0000FF);
			defenceComboEffect.graphics.beginFill(0x8888FF, 0.5);
			defenceComboEffect.graphics.drawEllipse(-70, -10, 140, 20);
			defenceComboEffect.alpha = 1.0;  defenceComboEffect.scaleX = defenceComboEffect.scaleY = 0;
			defenceComboEffect.y = -20;
			base.addChild( defenceComboEffect );
			attackBase.addChild( attackComboEffect );
			attackComboEffect.visible = defenceComboEffect.visible = false;
			this.mouseEnabled = this.mouseChildren = false;
		}
		public function setSide(isMySide:Boolean):void {
			if( isMySide ){
				tf.y = 13;
				base.rotation = attackBase.rotation = 0;
				defenceComboEffect.rotation = 0;
				base.y = 0;
				attackBase.y = 0;
			} else {
				tf.y = -Constants.BATTERY_DEFAULT_Y;
				base.rotation = attackBase.rotation = 180;
				defenceComboEffect.rotation = 180;
				base.y = tf.y + 16 + Constants.BATTERY_DEFAULT_Y;
				attackBase.y = tf.y + 16 + Constants.BATTERY_DEFAULT_Y;
			}
		}
		protected function onEnterFrame(e:Event):void {
			if ( attackComboEffect.visible ) {
				attackComboEffect.alpha *= 0.88;
				attackComboEffect.scaleX += (1 - attackComboEffect.scaleX) * 0.05;
				attackComboEffect.scaleY += (1 - attackComboEffect.scaleY) * 0.05;
				if ( attackComboEffect.alpha <= 0.1 ) {
					attackComboEffect.alpha = 2.0;  attackComboEffect.scaleX = attackComboEffect.scaleY = 0;
				}
			}
			if ( defenceComboEffect.visible ) {
				defenceComboEffect.alpha *= 0.88;
				defenceComboEffect.scaleX += (1 - defenceComboEffect.scaleX) * 0.05;
				defenceComboEffect.scaleY += (1 - defenceComboEffect.scaleY) * 0.05;
				if ( defenceComboEffect.alpha <= 0.1 ) {
					defenceComboEffect.alpha = 2.0;  defenceComboEffect.scaleX = defenceComboEffect.scaleY = 0;
				}
			}
		}
		public function updateCombo(name:String, isStart:Boolean):void {
			switch ( name ) {
				case "attackCombo":
					attackComboEffect.visible = isStart;
					break;
				case "defenceCombo":
					defenceComboEffect.visible = isStart;
					break;
			}
			if ( isStart ) {
				this.addEventListener( Event.ENTER_FRAME, onEnterFrame, false, 0, true );
			}
		}
		public function setAngle(angle:Number):void {
			cannonBarrel.rotation = angle;
		}
		public function setMode(playMode:String):void {
			if ( playMode == Constants.PLAY_MODE_ATTACKER ) {
				base.visible = false;
				attackBase.visible = true;
			} else {
				attackBase.visible = false;
				base.visible = true;
			}
		}
		public function setName(name:String):void {
			tf.text = name;
			tf.x = -0.5 * tf.width;
		}
		public function draw(baseLevel:int = 0, color:int = 0x999999, baseColor:int = 0x999999):void {
			attackBase.graphics.clear();
			base.graphics.clear();
			cannonBarrel.graphics.clear();
//			switch( baseLevel ) {
//				case Constants.BASE_LEVEL_0:
//				default:
					attackBase.graphics.lineStyle(0, 0xFFFFFF, 0.5);
					attackBase.graphics.beginFill(color);
					attackBase.graphics.moveTo(0, -8);
					attackBase.graphics.lineTo(10, 10);
					attackBase.graphics.lineTo( -10, 10);
					attackBase.graphics.endFill();
					attackBase.graphics.beginFill(baseColor);
					attackBase.graphics.moveTo(0, -4);
					attackBase.graphics.lineTo(7, 8);
					attackBase.graphics.lineTo( -7, 8);
					attackBase.graphics.endFill();
					
					base.graphics.lineStyle(1, 0xFFFFFF, 0.5);
					base.graphics.beginFill(color);
					base.graphics.drawCircle(0, 0, 10);
					base.graphics.beginFill(baseColor);
					base.graphics.drawCircle(0, 0, 7);
					cannonBarrel.graphics.lineStyle(1, 0xFFFFFF, 0.5);
					cannonBarrel.graphics.beginFill(baseColor);
					cannonBarrel.graphics.drawRect( -2, -15, 4, 16 );
					cannonBarrel.graphics.beginFill(color);
					cannonBarrel.graphics.drawRect( -3, 0, 6, 3);
//					break;
//			}
		}
		public function attackMotion():void {
			attackBase.addChild( new AttackMotion() );
		}
		public function reflectMotion():void {
			base.addChild( new ReflectMotion() );
		}
	}
	
	class AttackMotion extends Sprite {
		public function AttackMotion() {
			this.mouseChildren = this.mouseEnabled = false;
			this.graphics.lineStyle(0, 0xFFFF00);
			this.graphics.drawCircle(0, 0, 10);
			this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		protected function onEnterFrame(e:Event):void {
			this.scaleX *= 1.1;
			this.scaleY *= 1.1;
			this.alpha *= 0.9;
			if ( this.alpha < 0.1 ) {
				this.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
				if ( this.parent != null ) this.parent.removeChild( this );
			}
		}
	}
	class ReflectMotion extends Sprite {
		public function ReflectMotion() {
			this.mouseChildren = this.mouseEnabled = false;
			this.graphics.lineStyle(0, 0x00FF00);
			this.graphics.beginFill(0, 0x88FF88);
			this.graphics.drawEllipse(-8, 0, 16, 3);
			this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
			this.y = -20;
		}
		protected function onEnterFrame(e:Event):void {
			this.scaleX *= 1.1;
			this.alpha *= 0.92;
			if ( this.alpha < 0.1 ) {
				this.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
				if ( this.parent != null ) this.parent.removeChild( this );
			}
		}
	}
	
	class DamageMotion extends Sprite {
		public function DamageMotion(vx:Number, vy:Number) {
			this.mouseChildren = this.mouseEnabled = false;
			this.graphics.beginFill(0x00FF00 | (int(Math.random()*80)<<24) | (int(Math.random()*80)));
			this.graphics.drawCircle( -60*vx + -3 + Math.random() * 6, -60*vy + -3 + Math.random() * 6, 0.8);
			this.graphics.drawCircle( -60*vx + -3 + Math.random() * 6, -60*vy + -3 + Math.random() * 6, 0.8);
			this.graphics.drawCircle( -60*vx + -3 + Math.random() * 6, -60*vy + -3 + Math.random() * 6, 0.8);
			this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		protected function onEnterFrame(e:Event):void {
			this.scaleX *= 1.1;
			this.scaleY *= 1.1;
			this.alpha *= 0.9;
			if ( this.alpha < 0.1 ) {
				this.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
				if ( this.parent != null ) this.parent.removeChild( this );
			}
		}
	}
	
	// 本拠地のダメージモーション
	class HitMotion extends Sprite {
		public function HitMotion() {
			this.mouseChildren = this.mouseEnabled = false;
			this.graphics.beginFill(0xFF0000);
			this.graphics.drawRect(0, -1, 465, 2);
			this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		protected function onEnterFrame(e:Event):void {
			this.scaleY = this.scaleY + 0.3;
			this.alpha *= 0.9;
			if ( this.alpha < 0.1 ) {
				this.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
				if ( this.parent != null ) this.parent.removeChild( this );
			}
		}
	}
	
	// CPUクライアント
	class CPUClient extends Sprite implements IClient {
		public var self:IClient;
		private var attr:Object = { };
		public var missiles:Array = [];
		private var _word:String;
		public var attackid:int;
		public var focus:Missile;
		public function CPUClient() {
			
		}
		public function start():void {
			enable();
		}
		protected var timer:Timer;
		public function enable():void {
			attackid = 0;
			typedLength = 0;
			defenceLength = 0;
			_word = Words.getJRandom()[1];
			timer = new Timer(1000 / Score.cpuLV);
			missiles = [];
			timer.start();
			timer.addEventListener(TimerEvent.TIMER, onTimer);
		}
		public function disable():void {
			timer.stop();
		}
		protected var typedLength:int = 0;
		protected var defenceLength:int = 0;
		protected function onTimer(e:TimerEvent):void {
			for ( var i:int = 0; i < missiles.length; i++ ) {
				if ( missiles[i].y > 150 && missiles[i].isMySide && (missiles[i].word.length > 0)) {
					if ( focus == null ) focus = missiles[i];
					else if ( focus.y < missiles[i].y ) focus = missiles[i];
				}
			}
			if ( focus != null ) {
				focus.word = focus.word.substr(0, focus.word.length - 1);
				if ( focus.word.length > 0 ) {
					dispatchEvent(new GameEvent(GameEvent.SHOOT, false, false, "0"));
				} else {
					dispatchEvent(new GameEvent(GameEvent.SHOOT, false, false, "1"));
					focus = null;
				}
			} else {
				
				typedLength++;
				if ( Roma.getRomaToType(_word).length == typedLength ) {
					dispatchEvent(new GameEvent(GameEvent.ATTACK, false, false, _word));
					_word = Words.getJRandom()[1];
					attackid++;
					typedLength = 0;
				}
			}
		}
		public function getConnectTime () : Number {return 0;}
		public function getConnectionState () : int { return 0; }
		public function IClient ():void{}
		public function isSelf () : Boolean { return false; }
		public function getAttribute (attrScope:String, attrName:String) : String{return attr[attrName]}
		public function getRoomIDs () : Array { return []; }
		public function getPing () : int { return 0; }
		public function isInRoom (roomID:String) : Boolean { return false; }
		public function setClientClass (scope:String, clientClass:Class, ...rest) : void{}
		public function getAttributes () : Object{ return {}}
		public function getAttributesByScope () : Object{ return {}}
		public function deleteAttribute (attrName:String, attrScope:String = null) : void{}
		public function getConnection () : Reactor { return null; }
		public function getTimeOnline () : int { return 0; }
		public function setAttribute (attrName:String, attrValue:String, attrScope:String = null, isShared:Boolean = true, isPersistent:Boolean = false, isUnique:Boolean = false, evaluate:Boolean = false) : void { attr[attrName] = attrValue; }
		public function getClientID () : String{ return "0"}
		public function getIP () : String{ return ""}
		public function sendMessage (messageName:String, ...rest) : void { }
	}
	
	// ゲーム全体・メッセージ制御
	class GameController extends EventDispatcher {
		protected var gameRoom:Room;
		protected var lobbyRoom:Room;
		protected var reactor:Reactor;
		
		protected var _lobbyUserNum:int;
		
		protected var infoUI:InfoUI;
		protected var gameStage:GameStage;
		protected var missileID:int = 0;
		
		protected var blueNum:int;
		protected var redNum:int;
		
		protected var cpu:CPUClient = new CPUClient();

		
		protected function clientStatusInit( selfClient:IClient ):void {
			LocalData.remove(Constants.LD_FIELD, Constants.USER_BATTERY_BASE_LEVEL);
			if ( LocalData.read(Constants.LD_FIELD, Constants.USER_NAME) == null ) {
				LocalData.write(Constants.LD_FIELD, Constants.USER_NAME, "Guest" + selfClient.getClientID());
			}
			
			var userName:String = String(LocalData.read(Constants.LD_FIELD, Constants.USER_NAME));
			if( userName != null ) selfClient.setAttribute(Constants.USER_NAME, userName);
			selfClient.setAttribute("status", Constants.STATUS_CHATING);
			selfClient.setAttribute(Constants.HANDICAP, String(Score.handicap));
			cpu.self = selfClient;
			
			// 砲台の形とか色とか
			var baseLevel:String = (LocalData.read(Constants.LD_FIELD, Constants.USER_BATTERY_BASE_LEVEL))?
				String(LocalData.read(Constants.LD_FIELD, Constants.USER_BATTERY_BASE_LEVEL)):
				String(Constants.DEFAULT_USER_BATTERY_BASE_LEVEL);
			selfClient.setAttribute(Constants.USER_BATTERY_BASE_LEVEL, baseLevel, "");
			var color:String = (LocalData.read(Constants.LD_FIELD, Constants.USER_BATTERY_COLOR))?
				String(LocalData.read(Constants.LD_FIELD, Constants.USER_BATTERY_COLOR)):
				String(Constants.DEFAULT_USER_BATTERY_COLOR);
			selfClient.setAttribute(Constants.USER_BATTERY_COLOR, color, "");
			var baseColor:String = (LocalData.read(Constants.LD_FIELD, Constants.USER_BATTERY_BASE_COLOR))?
				String(LocalData.read(Constants.LD_FIELD, Constants.USER_BATTERY_BASE_COLOR)):
				String(Constants.DEFAULT_USER_BATTERY_BASE_COLOR);
			selfClient.setAttribute(Constants.USER_BATTERY_BASE_COLOR, baseColor, "");
			
		}
		// コンストラクタ
		public function GameController(reactor:Reactor, infoUI:InfoUI, gameStage:GameStage) {
			this.reactor = reactor;
			this.infoUI = infoUI;
			this.gameStage = gameStage;
			
			clientStatusInit( reactor.getClientManager().self() );
			
			
			
			reactor.getMessageManager().addMessageListener(Constants.ATTACK_LOG, attackLogAction);
			
			// ゲームのメッセージ類
//			gameRoom = reactor.getRoomManager().createRoom(Score.myGameRoom);
//			setupGameRoom();

			lobbyRoom = reactor.getRoomManager().createRoom(Constants.LOBBY_ROOM);
			lobbyRoom.addEventListener(RoomEvent.UPDATE_ROOM_ATTRIBUTE, onLobbyUpdateRoomAttribute);
			lobbyRoom.addEventListener(RoomEvent.UPDATE_CLIENT_ATTRIBUTE, onLobbyUpdateClientAttribute);
			lobbyRoom.addEventListener(RoomEvent.CLIENT_COUNT, onLobbyClientCount);
			
			// 情報画面からのイベントを受け取る
			infoUI.addEventListener(GameEvent.JOIN_BLUE, onJoinBlue);
			infoUI.addEventListener(GameEvent.JOIN_RED, onJoinRed);
			infoUI.addEventListener(GameEvent.JOIN_SOLO, onJoinSolo );
			infoUI.addEventListener(GameEvent.COLOR_SELECT, onColorSelect);
			infoUI.addEventListener(GameEvent.HANDICAP_SELECT, onHandicapSelect); // ハンデかわりました
			infoUI.addEventListener(GameEvent.NAME_CHANGE, onNameChange); // 名前かわりました
			
			// ゲーム画面からのイベントを受け取る
			gameStage.addEventListener(GameEvent.MOVE_BATTERY, onMoveBattery);
			gameStage.addEventListener(GameEvent.MODE_ATTACK, onModeAttack);
			gameStage.addEventListener(GameEvent.MODE_DEFENCE, onModeDefence);
			gameStage.addEventListener(GameEvent.ATTACK, onAttack); // 通常攻撃
			gameStage.addEventListener(GameEvent.ATTACK_DOUBLE, onAttackDouble); // ダブルアタック!
			gameStage.addEventListener(GameEvent.ATTACK_REFLECT, onAttackReflect); // オートリフレクション!
			gameStage.addEventListener(GameEvent.HIT, onHit); // 敵陣到達!
			
			gameStage.addEventListener(GameEvent.ATTACK_COMBO_START, onAttackComboStart);
			gameStage.addEventListener(GameEvent.ATTACK_COMBO_END, onAttackComboEnd);
			gameStage.addEventListener(GameEvent.DEFENCE_COMBO_START, onDefefnceComboStart);
			gameStage.addEventListener(GameEvent.DEFENCE_COMBO_END, onDeffenceComboEnd);
			
			gameStage.addEventListener(GameEvent.SHOOT, onShoot); // 撃ってます
			gameStage.addEventListener(GameEvent.MISSILE_DESTROY, onMissileDestroy); // ミサイルいっこ壊した
			gameStage.addEventListener(GameEvent.CHAT, onChat); // チームチャット
			gameStage.addEventListener(GameEvent.END_GAME, endGame); // エンディング描画完了
			
			
		}
		protected function onAttackComboStart(e:GameEvent):void {
			reactor.getClientManager().self().setAttribute("attackCombo", "true", Score.myGameRoom);
		}
		protected function onAttackComboEnd(e:GameEvent):void {
			reactor.getClientManager().self().setAttribute("attackCombo", "false", Score.myGameRoom);
		}
		protected function onDefefnceComboStart(e:GameEvent):void {
			reactor.getClientManager().self().setAttribute("defenceCombo", "true", Score.myGameRoom);
		}
		protected function onDeffenceComboEnd(e:GameEvent):void {
			reactor.getClientManager().self().setAttribute("defenceCombo", "false", Score.myGameRoom);
		}
		protected function onNameChange(e:GameEvent):void {
			if( e.data != null ) reactor.getClientManager().self().setAttribute(Constants.USER_NAME, e.data, "");
		}
		protected function onHandicapSelect(e:GameEvent):void {
			Score.handicap = Number(e.data);
			reactor.getClientManager().self().setAttribute(Constants.HANDICAP, e.data, "");
			infoUI.updateHandicap();
		}
		protected function onColorSelect(e:GameEvent):void {
			var temp:Array = e.data.split("-");
			var colorType:String = temp[0];
			var color:String = temp[2];
			if ( colorType == Constants.USER_BATTERY_BASE_COLOR || colorType == Constants.USER_BATTERY_COLOR ) {
				reactor.getClientManager().self().setAttribute(colorType, color, "");
			}
		}
		protected function setupGameRoom(startTime:String = "0"):void {
			addListeners();
			
			var nowTime:Number = ServerClockUtil.getTime();
			var waitTime:Number = Number(startTime) - nowTime;
			if ( waitTime > 0 ) {
				var timer:Timer = new Timer(waitTime, 1);
				timer.addEventListener(TimerEvent.TIMER_COMPLETE, onStart);
				gameStage.setTimer(Number(startTime));
				timer.start();
			} else {
				gameStage.enable = true;
			}
		}
		protected function onStart(e:TimerEvent):void {
			e.target.removeEventListener(TimerEvent.TIMER_COMPLETE, onStart);
			gameRoom.setAttribute("status", Constants.STATUS_PLAYING); // 複数人でセットしてしまっている
			if (Score.isSolo) gameStage.cpu = cpu;
			else gameStage.cpu = null;
			
			gameStage.enable = true;
		}
		protected function addListeners():void{
			gameRoom.addMessageListener(Constants.ATTACK, attackAction);
			gameRoom.addMessageListener(Constants.ATTACK_REFLECT, attackReflectAction);
			gameRoom.addMessageListener(Constants.HIT, hitAction);
			gameRoom.addMessageListener(Constants.SEND_ME_LOG, onAddClient);
			
			gameRoom.addEventListener(RoomEvent.UPDATE_ROOM_ATTRIBUTE, onGameUpdateRoomAttribute);
			gameRoom.addEventListener(RoomEvent.UPDATE_CLIENT_ATTRIBUTE, onGameUpdateClientAttribute);
			
			gameRoom.addEventListener(RoomEvent.REMOVE_CLIENT, onLeaveClient);
			
			gameRoom.addMessageListener(Constants.SHOOT, shootAction);
			gameRoom.addMessageListener(Constants.DESTROY, destroyAction);
			gameRoom.addMessageListener(Constants.GAME_CHAT, gameChatAction);
		}
		protected function removeListeners():void {
			gameRoom.removeMessageListener(Constants.ATTACK, attackAction);
			gameRoom.removeMessageListener(Constants.ATTACK_REFLECT, attackReflectAction);
			gameRoom.removeMessageListener(Constants.HIT, hitAction);
			gameRoom.removeMessageListener(Constants.SEND_ME_LOG, onAddClient);
			
			gameRoom.removeEventListener(RoomEvent.UPDATE_ROOM_ATTRIBUTE, onGameUpdateRoomAttribute);
			gameRoom.removeEventListener(RoomEvent.UPDATE_CLIENT_ATTRIBUTE, onGameUpdateClientAttribute);
			
			gameRoom.removeEventListener(RoomEvent.REMOVE_CLIENT, onLeaveClient);
			
			gameRoom.removeMessageListener(Constants.SHOOT, shootAction);
			gameRoom.removeMessageListener(Constants.DESTROY, destroyAction);
			gameRoom.removeMessageListener(Constants.GAME_CHAT, gameChatAction);
		}
		protected function gameChatAction(fromClient:IClient, msg:String):void {
			gameStage.putChatMessage(ClientUtil.getUserName(fromClient) + "> " + String(decodeURI(msg)));
		}
		protected function onChat(e:GameEvent):void {
			var filter:AttributeFilter = new AttributeFilter();
			var myTeam:String = reactor.getClientManager().self().getAttribute("","team");
			filter.addComparison( new AttributeComparison("team", myTeam, CompareType.EQUAL) );
			gameRoom.sendMessage(Constants.GAME_CHAT, true, filter, String(encodeURI(e.data)));
		}
		protected function endGame(e:GameEvent):void { // ゲーム終了。部屋から出る。
			startInfo();
			
			var self:IClient = reactor.getClientManager().self();
			Score.playMode = self.getAttribute(Score.myGameRoom, Constants.PLAY_MODE);
			
			gameRoom.leave();
			gameStage.clearAll();
			gameStage.removeMissilesOf(reactor.getClientManager().self());
			reactor.getClientManager().self().setAttribute("status", Constants.STATUS_CHATING);
			reactor.getClientManager().self().setAttribute("team", Constants.TEAM_NONE);
			reactor.getClientManager().self().setAttribute("isSolo", "");
			reactor.getClientManager().self().setAttribute(Constants.BATTERY_X, "", Score.myGameRoom);
		}
		protected function shootAction(fromClient:IClient, clientId:int, missileId:int, shootTime:Number, bulletSpeed:Number, isDestroy:String):void {
			gameStage.addBullet(fromClient, reactor.getClientManager().getClient(String(clientId)), missileId, shootTime, bulletSpeed, isMySide(fromClient), (isDestroy == "true"));
		}
		protected function destroyAction(fromClient:IClient, clientId:String, missileId:int):void {
			if ( fromClient.isSelf() ) {
				Score.defence++;
			}
			gameStage.destroy(fromClient, clientId, missileId);
		}
		protected function onShoot(e:GameEvent):void {
			var sch:SoundChannel = SoundManager.TypeSound.play();
			if( sch != null ) sch.soundTransform = new SoundTransform(0.5);
			var arr:Array = e.data.split("-");
			var clientId:int = arr[0];
			var missileId:int = arr[1];
			var isDestroy:Boolean = arr[2] == "1";
			gameRoom.sendMessage(Constants.SHOOT, true, null, clientId, missileId, ServerClockUtil.getTime(), Constants.BULLET_SPEED, isDestroy);
			LogAnalyzer.addDefenceLog(1);
		}
		protected function onMissileDestroy(e:GameEvent):void {
			var arr:Array = e.data.split("-");
			var clientId:int = arr[0];
			var missileId:int = arr[1];
			gameRoom.sendMessage(Constants.DESTROY, true, null, clientId, missileId);
		}
		protected function attackLogAction(fromClient:IClient, text:String, id:int, power:Number, attackTime:Number, arriveTime:Number, xpos:Number):void {
			gameStage.addAttackLog(fromClient, text, isMySide(fromClient), id, power, attackTime, arriveTime, xpos);
		}
		protected function onAddClient(fromClient:IClient):void {
			gameStage.sendLogTo(fromClient);
		}
		protected function onLeaveClient(e:RoomEvent):void {
			gameStage.removeMissilesOf(e.getClient());
		}
		protected function hitAction(fromClient:IClient, id:String, damage:String):void {
			if ( gameStage.cpu != null && damage == "cpu" ) {
				fromClient = gameStage.cpu;
				damage = "1";
			}
			SoundManager.HitSound.play();
			var score:Number = 100;
			var attName:String = "";
			if ( fromClient.getAttribute("", "team") == Constants.TEAM_BLUE ) {
				score *= redNum;
				attName = "bluePoint";
			} else if ( fromClient.getAttribute("", "team") == Constants.TEAM_RED ) {
				score *= blueNum;
				attName = "redPoint";
			}
			if ( isMySide( fromClient ) ) {
				gameStage.hitMotion(true);
				Score.score += score;
			} else {
				gameStage.hitMotion(false);
				Score.score -= score;
			}
			if ( fromClient.isSelf() ) {
				Score.hit++;
				gameRoom.setAttribute(attName, "%v+" + Number(damage), true, false, true);
			} else if ( ClientUtil.isCPU( fromClient ) ) {
				gameRoom.setAttribute(attName, "%v+" + Number(damage), true, false, true);
			}
			setDamage();
		}
		protected function setDamage():void {
			var bluePoint:int = int(gameRoom.getAttribute("bluePoint"));
			var redPoint:int = int(gameRoom.getAttribute("redPoint"));
			if ( ClientUtil.isTeamBlue(reactor.getClientManager().self()) ) {
				gameStage.setDamage( redPoint, bluePoint );
			} else {
				gameStage.setDamage( bluePoint, redPoint );
			}
		}
		protected function onHit(e:GameEvent):void {
			var temp:Array = e.data.split("-");
			var missileId:String = temp[0];
			var damage:String = temp[1];
			gameRoom.sendMessage(Constants.HIT, true, null, temp[0], temp[1]);
		}
		protected function attackAction(fromClient:Client, msg:String, xpos:Number, arriveTime:Number, id:int, power:Number):void {
			SoundManager.AttackSound.play();
			gameStage.addAttack(fromClient, msg, isMySide(fromClient), id, power, ServerClockUtil.getTime()+arriveTime, xpos);
		}
		protected function attackReflectAction(fromClient:Client, msg:String, xpos:Number, arriveTime:Number, id:int, power:Number):void {
			SoundManager.ReflectSound.play();
			gameStage.addAttack(fromClient, msg, isMySide(fromClient), id, power, ServerClockUtil.getTime()+arriveTime, xpos, true);
		}
		protected function onAttack(e:GameEvent):void {
			attackHandler(e, true);
		}
		protected function onAttackDouble(e:GameEvent):void {
			attackHandler(e, false);
		}
		protected function attackHandler(e:GameEvent, logTrack:Boolean):void {
			var temp:Array = e.data.split("-");
			var missileMsg:String = temp[0];
			var missilePower:String = temp[1];
			Score.attack++;
			var attackLoc:Number = 425-Number(reactor.getClientManager().self().getAttribute(Score.myGameRoom, Constants.BATTERY_X)) - 50 + Math.random() * 100;
			if ( attackLoc < 0 ) attackLoc = 50 + Math.random() * 50;
			if ( attackLoc > 385 ) attackLoc = 285 + Math.random() * 50;
			gameRoom.sendMessage(Constants.ATTACK, true, null, missileMsg, attackLoc, Constants.MISSILE_ARRIVE_TIME, missileID++, Number(missilePower));
			if( logTrack && (missilePower == "1"))LogAnalyzer.addAttackLog(e.data.length);
		}
		protected function onAttackReflect(e:GameEvent):void {
			var temp:Array = e.data.split("-");
			var missileMsg:String = temp[0];
			var missilePower:String = temp[1];
			Score.attack++;
			var attackLoc:Number = 425-Number(reactor.getClientManager().self().getAttribute(Score.myGameRoom, Constants.BATTERY_X)) - 20 + Math.random() * 40;
			if ( attackLoc < 0 ) attackLoc = 20 + Math.random() * 20;
			if ( attackLoc > 385 ) attackLoc = 345 + Math.random() * 20;
			gameRoom.sendMessage(Constants.ATTACK_REFLECT, true, null, missileMsg, attackLoc, Constants.MISSILE_ARRIVE_TIME, missileID++, Number(missilePower));
		}
		protected function onModeAttack(e:GameEvent):void {
			reactor.getClientManager().self().setAttribute(Constants.PLAY_MODE, Constants.PLAY_MODE_ATTACKER, Score.myGameRoom);
		}
		protected function onModeDefence(e:GameEvent):void {
			reactor.getClientManager().self().setAttribute(Constants.PLAY_MODE, Constants.PLAY_MODE_DEFENDER, Score.myGameRoom);
		}
		protected function isMySide(client:IClient):Boolean {
			return ( reactor.getClientManager().self().getAttribute("", "team") == client.getAttribute("", "team") );
		}
		protected function onMoveBattery(e:GameEvent):void {
			reactor.getClientManager().self().setAttribute(Constants.BATTERY_X, String(gameStage.myBatteryMoveTo), Score.myGameRoom);
		}
		protected function onJoin(e:RoomEvent = null):void {
			SoundManager.playGameBgm();
			infoUI.fadeOut();
		}
		protected function onSynchronize(e:RoomEvent = null):void {
			gameRoom.removeEventListener(RoomEvent.JOIN, onJoin);
			gameRoom.removeEventListener(RoomEvent.SYNCHRONIZE, onSynchronize);
			ServerClockUtil.init( reactor.getServer() );
			Score.resetCombo();
			gameRoom.sendMessage(Constants.SEND_ME_LOG);
			var self:Client = reactor.getClientManager().self();
			
			// 既存のゲーム内情報を追加
			var users:Array = gameRoom.getClients();
			
			var teamNum:int = 1;
			for ( var i:int = 0; i < users.length; i++ ) {
				if ( !users[i].isSelf() ) {
					if ( isMySide(users[i]) ) teamNum++;
					var batteryX:String = users[i].getAttribute(Score.myGameRoom, Constants.BATTERY_X);
					if ( batteryX != null ) gameStage.setBattery(users[i], Number(batteryX), isMySide(users[i]));
					var playMode:String = users[i].getAttribute(Score.myGameRoom, Constants.PLAY_MODE);
					if ( playMode != null ) gameStage.setPlayMode(users[i], playMode);
				}
			}
			var loc:Number = 22;
			var tempLoc:Number = 210;
			while ( teamNum > 0 ) {
				loc += tempLoc * (teamNum & 1);
				tempLoc *= 0.5;
				teamNum >>= 1;
			}
			self.setAttribute(Constants.BATTERY_X, String(loc), Score.myGameRoom);
			self.setAttribute("status", Constants.STATUS_WAITING);
			gameStage.setBattery(self, loc, true);
			gameStage.setPlayMode(self, Score.playMode);
			
			if ( Score.isSolo ) {
				cpu.setAttribute(Constants.USER_NAME, "CPU");
				cpu.setAttribute(Constants.BATTERY_X, "232");
				cpu.setAttribute(Constants.PLAY_MODE, Constants.PLAY_MODE_ATTACKER);
				cpu.setAttribute("team", Constants.TEAM_RED);
				gameStage.setBattery(cpu, Number(cpu.getAttribute("", Constants.BATTERY_X)), false);
				gameStage.setPlayMode(cpu, Constants.PLAY_MODE_ATTACKER);
			} else {
				self.setAttribute("isSolo", "false");
			}
			
			var str:String = gameRoom.getAttribute("status");
			trace( "gameroom.status", str );
			if ( str == null ) {
				gameRoom.setAttribute("status", Constants.STATUS_WAITING);
				gameRoom.setAttribute("startTime", String(ServerClockUtil.getTime() + Constants.GAME_START_WAITTIME));
				setupGameRoom(String(ServerClockUtil.getTime() + Constants.GAME_START_WAITTIME));
			} else {
				if ( str == Constants.STATUS_PLAYING ) {
					setupGameRoom();
					reactor.getClientManager().self().setAttribute("status", Constants.STATUS_PLAYING);
				} else if ( str == Constants.STATUS_WAITING ) {
					setupGameRoom(gameRoom.getAttribute("startTime"));
					reactor.getClientManager().self().setAttribute("status", Constants.STATUS_WAITING);
				}
			}
			
			setDamage();
			LogAnalyzer.reset();
		}
		protected function onJoinBlue(e:GameEvent):void {
			reactor.getClientManager().self().setAttribute("team", Constants.TEAM_BLUE);
			gameStage.setup(Constants.TEAM_BLUE);
			
			Score.isSolo = false;
			Score.myGameRoom = Constants.GAME_ROOM;
			if ( Score.playMode == null )
				Score.playMode = Constants.PLAY_MODE_DEFENDER;
				
			joinTeam();
		}
		protected function onJoinRed(e:GameEvent):void {
			reactor.getClientManager().self().setAttribute("team", Constants.TEAM_RED);
			gameStage.setup(Constants.TEAM_RED);
			
			Score.isSolo = false;
			Score.myGameRoom = Constants.GAME_ROOM;
			if ( Score.playMode == null )
				Score.playMode = Constants.PLAY_MODE_ATTACKER;
				
			joinTeam();
		}
		protected function joinTeam():void {
			gameRoom = reactor.getRoomManager().createRoom(Score.myGameRoom);
			gameRoom.addEventListener(RoomEvent.JOIN, onJoin);
			gameRoom.addEventListener(RoomEvent.SYNCHRONIZE, onSynchronize);
			reactor.getClientManager().self().setAttribute(Constants.PLAY_MODE, Score.playMode, Score.myGameRoom);

			if ( gameRoom.clientIsInRoom() ) {
				onJoin();
				onSynchronize();
			} else {
				gameRoom.join();
			}
		}
		protected function onObserve(e:RoomEvent):void {
			trace( gameRoom.getClients().length );
		}
		protected function onObserveSynchronize(e:RoomEvent):void {
			trace( gameRoom.getClients().length );
		}
		protected function onJoinSolo(e:GameEvent):void {
			reactor.getClientManager().self().setAttribute("isSolo", "true");
			reactor.getClientManager().self().setAttribute("team", Constants.TEAM_BLUE);
			gameStage.setup(Constants.TEAM_BLUE);
			
			Score.isSolo = true;
			Score.myGameRoom = Constants.CPU_ROOM_DEFAULT + "_" + reactor.getClientManager().self().getClientID();
			if ( Score.playMode == null )
				Score.playMode = Constants.PLAY_MODE_ATTACKER;
				
			joinTeam();
		}
		// 情報ウィンドウ表示。ロビー入室
		public function startInfo():void {
			var self:IClient = reactor.getClientManager().self();
			var typeNums:Array = LogAnalyzer.analyze();
			self.setAttribute(Constants.ATTACK_POWER, String(Score.attackPower));
			self.setAttribute(Constants.DEFENCE_POWER, String(Score.defencePower));
			self.setAttribute("attackCombo", "false", Score.myGameRoom);
			self.setAttribute("defenceCombo", "false", Score.myGameRoom);
			SoundManager.stopGameBgm();
			if ( !lobbyRoom.clientIsInRoom() ) {
				lobbyRoom.join();
				infoUI.setChatRoom(lobbyRoom);
			}
			if ( typeNums[0] != -1 ) {
				infoUI.setInfoMessage("Type count: " + typeNums[0] + "("+ int(typeNums[1]/typeNums[0]*10000)/100 +"%)");
			}
			infoUI.display();
			infoUI.updateSoloInfo("COM LV: " + Score.cpuLV);
			infoUI.setBatteryColors(ClientUtil.getBaseLevel(self), ClientUtil.getColor(self), ClientUtil.getBaseColor(self));
		}
		
		// ゲームルームの属性変更
		protected function onGameUpdateRoomAttribute(e:RoomEvent):void {
			switch( e.getChangedAttr().name ) {
				case "startTime": // スタート時間変化
					setupGameRoom( e.getChangedAttr().value );
					break;
				case "redPoint": // RED 勝利
					if ( int(e.getChangedAttr().value) >= (0.8 * Constants.VICTORY_POINT) ) {
						if ( e.getClient().isSelf() )gameRoom.setAttribute("status", Constants.STATUS_FINISHING);
					}
					if ( int(e.getChangedAttr().value) >= Constants.VICTORY_POINT ) {
						if ( reactor.getClientManager().self().getAttribute("", "team") == Constants.TEAM_RED ) {
							infoUI.setInfoMessage("おめでとう!あなたの勝利です!");
							Score.win++;
							gameStage.end(true);
						} else {
							if (gameStage.cpu != null ) {
								gameStage.cpu.disable();
								infoUI.setInfoMessage("あなたは敗北しました。");
								Score.cpuLV-= 0.5;
								if ( Score.cpuLV < 0.5 ) Score.cpuLV = 0.5;
							} else {
								infoUI.setInfoMessage("あなたのチームは敗北しました。");
							}
							SoundManager.DangerSound.play();
							Score.lose++;
							gameStage.end(false);
						}
						removeListeners();
						if ( e.getClient().isSelf() ) {
							gameRoom.setAttribute("bluePoint", "0", true, false, true);
							gameRoom.setAttribute("redPoint", "0", true, false, true);
						}
						gameRoom.setAttribute("status", Constants.STATUS_WAITING);
						gameRoom.setAttribute("startTime", String(ServerClockUtil.getTime() + Constants.GAME_END_NEXT_WAITTIME));
						Score.save();
					}
					break;
				case "bluePoint": // BLUE 勝利
					if ( int(e.getChangedAttr().value) >= (0.8 * Constants.VICTORY_POINT) ) {
						if ( e.getClient().isSelf() )gameRoom.setAttribute("status", Constants.STATUS_FINISHING);
					}
					if ( int(e.getChangedAttr().value) >= Constants.VICTORY_POINT ) {
						if ( reactor.getClientManager().self().getAttribute("", "team") == Constants.TEAM_BLUE ) {
							if (gameStage.cpu != null ) {
								gameStage.cpu.disable();
								infoUI.setInfoMessage("おめでとう!あなたの勝利です!");
								Score.cpuLV += 0.5;
							} else {
								infoUI.setInfoMessage("おめでとう!あなたの勝利です!");
							}
							Score.win++;
							gameStage.end(true);
						} else {
							infoUI.setInfoMessage("あなたのチームは敗北しました。");
							SoundManager.DangerSound.play();
							Score.lose++;
							gameStage.end(false);
						}
						removeListeners();
						if ( e.getClient().isSelf() ) {
							gameRoom.setAttribute("bluePoint", "0", true, false, true);
							gameRoom.setAttribute("redPoint", "0", true, false, true);
						}
						gameRoom.setAttribute("status", Constants.STATUS_WAITING);
						gameRoom.setAttribute("startTime", String(ServerClockUtil.getTime() + 20000));
						Score.save();
					}
					break;
			}
		}
		protected function onLobbyUpdateRoomAttribute(e:RoomEvent):void {
		}
		
		// ゲームルーム中のクライアントの属性変更
		protected function onGameUpdateClientAttribute(e:RoomEvent):void {
//			if ( ClientUtil.getStatus(e.getClient()) != Constants.STATUS_PLAYING ) return;
			switch( e.getChangedAttr().name ) {
				case Constants.BATTERY_X: // 位置の移動
					gameStage.setBattery(e.getClient(), Number(e.getChangedAttr().value), isMySide(e.getClient()) );
					break;
				case Constants.PLAY_MODE: // プレイモード変更
					gameStage.setPlayMode(e.getClient(), e.getChangedAttr().value);
					break;
				case Constants.ANGLE: // 砲台の角度
					gameStage.setBatteryAngle(e.getClient(), Number(e.getChangedAttr().value));
					break;
				case "attackCombo":
				case "defenceCombo":
					gameStage.updateComboStatus(e.getClient(), e.getChangedAttr().name, (e.getChangedAttr().value == "true"));
					break;
				case "status": // 当然STATUS_PLAYING
					gameStage.setBattery(e.getClient(), Number(e.getClient().getAttribute(Score.myGameRoom, Constants.BATTERY_X)), isMySide(e.getClient()) );
					gameStage.setPlayMode(e.getClient(), e.getClient().getAttribute(Score.myGameRoom,Constants.PLAY_MODE));
					break;
				case "team":
					gameStage.setBattery(e.getClient(), Number(e.getClient().getAttribute(Score.myGameRoom, Constants.BATTERY_X)), isMySide(e.getClient()) );
					break;
			}
		}
		// ロビー中のクライアントの属性変更
		protected function onLobbyUpdateClientAttribute(e:RoomEvent):void {
			updateTeamUsersNum();
			if( e.getClient().isSelf() ){
				switch( e.getChangedAttr().name ) {
					case Constants.USER_BATTERY_BASE_COLOR:
					case Constants.USER_BATTERY_COLOR:
					case Constants.USER_BATTERY_BASE_LEVEL:
						var client:IClient = e.getClient();
						infoUI.setBatteryColors(ClientUtil.getBaseLevel(client), ClientUtil.getColor(client), ClientUtil.getBaseColor(client));

				}
			}
		}
		
		protected function onLobbyClientCount(e:RoomEvent):void {
			lobbyUserNum = e.getNumClients();
			updateTeamUsersNum();
		}
		protected function updateTeamUsersNum():void {
			var clients:Array = lobbyRoom.getClients();
			var blueNum:int = 0;
			var redNum:int = 0;
			var blueAttack:Number = 0;
			var blueDefence:Number = 0;
			var redAttack:Number = 0;
			var redDefence:Number = 0;
			for ( var i:int = 0; i < clients.length; i++ ) {
				var teamAtt:String = Client( clients[i] ).getAttribute("", "team");
				var isSolo:Boolean = Client( clients[i] ).getAttribute("", "isSolo") == "true";
				var attackPower:Number = ClientUtil.getAttackPower(clients[i]);
				var defencePower:Number = ClientUtil.getDefencePower(clients[i]);
				var handiCap:Number = ClientUtil.getHandicap(clients[i]);
				
				if( !isSolo ){
					if ( teamAtt == Constants.TEAM_BLUE ) {
						blueNum++;
						blueAttack += attackPower/handiCap;
						blueDefence += defencePower/handiCap;
					}
					if ( teamAtt == Constants.TEAM_RED ) {
						redNum++;
						redAttack += attackPower/handiCap;
						redDefence += defencePower/handiCap;
					}
				}
			}
			this.blueNum = blueNum;
			this.redNum = redNum;
			infoUI.setBlueTeamInfo( blueNum, blueAttack, blueDefence );
			infoUI.setRedTeamInfo( redNum, redAttack, redDefence );
		}
		public function get lobbyUserNum():int { return _lobbyUserNum; }
		
		public function set lobbyUserNum(value:int):void 
		{
			_lobbyUserNum = value;
			infoUI.setLobbyUserNum( value );
		}
	}
	
	// 測定くん
	class LogAnalyzer {
		protected static var attackLog:Array = [];
		protected static var defenceLog:Array = [];
		protected static var old_type:Number = -1;
		protected static var old_correct:Number = -1;
		
		public static function reset():void {
			attackLog = [];
			defenceLog = [];
			old_type = Score.type;
			old_correct = Score.correct;
		}
		
		public static function addAttackLog(wordLength:int):void {
			attackLog.push([wordLength, getTimer()]);
		}
		public static function addDefenceLog(wordLength:int):void {
			defenceLog.push([wordLength, getTimer()]);
		}
		protected static function analyzeArray(log:Array):Number {
			var power:Number = 0;
			var count:int = log.length;
			var lastEnd:int = 0;
			var limitTime:Number;
			var strNum:Number;
			if ( count < 2 ) return 0;
			for ( var i:int = 0; i < count; i++ ) {
				limitTime = log[i][1] + 10000;
				for ( var j:int = lastEnd; j < count; j++ ) {
					if ( log[j][1] > limitTime ) {
						break;
					}
				}
				lastEnd = j;
				strNum = 0;
				for ( j = i+1; j < lastEnd; j++ ) {
					strNum += log[j][0];
				}
				if ( i == lastEnd ) continue;
				var temp:Number = (log[lastEnd - 1][1] - log[i][1]);
				if ( temp < 3000 ) continue; // 最低3秒間持続
				temp = 1000000 * strNum / temp;
				if ( temp > power ) power = temp;
				if ( lastEnd == count ) break;
			}
			return power;
		}
		public static function analyze():Array {
			var attackPower:Number = analyzeArray(attackLog);
			var defencePower:Number = analyzeArray(defenceLog);
			if( attackPower > 0 ){
				if ( Score.attackPower < attackPower ) {
					Score.attackPower = (attackPower + Score.attackPower/4)/1.25;
				} else {
					Score.attackPower = (attackPower/16 + Score.attackPower)/1.0625;
				}
			}
			trace( defenceLog, defencePower );
			if( defencePower > 0 ){
				if ( Score.defencePower < defencePower ) {
					Score.defencePower = (defencePower + Score.defencePower/4)/1.25;
				} else {
					Score.defencePower = (defencePower/16 + Score.defencePower)/1.0625;
				}
			}
			Score.save();
			if ( old_type == -1 ) {
				return [-1, -1];
			} else {
				return [Score.type - old_type, Score.correct - old_correct];
			}
		}
	}
	
	// 弾の軌道制御とか
	class Bullet extends Sprite {
		
		protected var startX:Number;
		protected var startY:Number;
		protected var vx:Number;
		protected var vy:Number;
		protected var startTime:Number;
		protected var hitTime:Number;
		protected var target:Missile;
		protected var isDestroy:Boolean;
		public function Bullet(targetMissile:Missile, bulletSpeed:Number, startX:Number, startY:Number, startTime:Number, isDestroy:Boolean) {
			
			this.graphics.beginFill(0xFFFFFF - int(Math.random() * 0x40));
			this.graphics.drawRect(0, 0, 2, 2);
			this.mouseEnabled = this.mouseChildren = false;
			this.startX = startX;
			this.startY = startY;
			this.startTime = startTime;
			this.target = targetMissile;
			this.isDestroy = isDestroy;
			var x:Number = (385 - targetMissile.x) - startX;
			var yd:Number = Constants.DISTANCE - targetMissile.y - startY + 5;
			var missileSpeed:Number = targetMissile.durationT;
			var vd:Number = missileSpeed * missileSpeed;
			var md:Number = bulletSpeed * bulletSpeed;
			var meetTime:Number = (yd * missileSpeed - Math.sqrt( yd * yd * vd - (x * x + yd * yd) * (vd - md) ) ) / (vd - md);
			vx = x / meetTime;
			vy = Math.sqrt(md - vx * vx);
			hitTime = startTime + meetTime;
		}
		public function getAngle():String {
			return String( 180 * Math.atan2(-vx, vy) / Math.PI );
		}
		// 弾が進む。ヒットしたか画面外にいったらtrueを返してremoveChildしてもらう。 効果音つき
		public function update(time:Number):Boolean {
			var ellapse:Number = time - startTime;
			this.x = ellapse * vx + startX;
			this.y = ellapse * vy + startY;
			if ( target.parent != null && time > hitTime ) {
//				dispatchEvent(new GameEvent(GameEvent.MISSILE_DAMAGE, true));
				target.damage(vx, vy, isDestroy);
				var sch:SoundChannel = SoundManager.ShootSound.play();
				if( sch != null ) sch.soundTransform = new SoundTransform(0.5);
				return true;
			}
			if ( this.x > 665 || this.x < -200 || this.y > 665 || this.y < -200 ) {
				return true;
			}
			return false;
		}
	}

	// ミサイルの軌道制御とか
	class Missile extends Sprite {
		public var arriveTime:Number;
		public var attackTime:Number;
		public var durationT:Number;
		public var id:int;
		public var client:IClient;
		public var isSelf:Boolean;
		public var text:String = "";
		public var word:String = "";
		public var isMySide:Boolean;
		public var power:Number;
		protected var body:Sprite = new Sprite();
		protected var inputIndex:int = 0;
		protected var tf:TextField = new TextField();
		protected var index:int = 0;
		protected var isJapaneseWord:Boolean = false;
		
		public function Missile(client:IClient, attackTime:Number, arriveTime:Number, id:int, power:Number, isSelf:Boolean) {
			this.mouseChildren = false;
			this.mouseEnabled = false;
			this.client = client;
			this.attackTime = attackTime;
			this.arriveTime = arriveTime;
			this.power = power;
			durationT = Constants.DISTANCE / (arriveTime - attackTime);
			this.id = id;
			this.isSelf = isSelf;
			if ( ClientUtil.isTeamBlue(client) ) {
				body.graphics.lineStyle(0, 0x3366FF);
				body.graphics.beginFill(0x3399FF);
			} else if ( ClientUtil.isTeamRed(client) ) {
				body.graphics.lineStyle(0, 0xFF3333);
				body.graphics.beginFill(0xFF8888);
			}
			body.graphics.lineTo(power * 4, power * -16)
			body.graphics.lineTo(power * -4, power * -16);
			body.graphics.lineTo(0, 0);
			addChild( body );
			body.y = power * 8;
			tf.defaultTextFormat = new TextFormat(TextFieldUtil.getFont(), 14, 0xFFFFFF);
			tf.autoSize = "left";
			tf.border = true;
			tf.background = true;
			tf.backgroundColor = 0x0;
			tf.borderColor = 0xFFFFFF;
		}
		public function damage(vx:Number, vy:Number, isDestroy:Boolean):void {
			var damageMotion:DamageMotion = new DamageMotion(vx, vy);
			if ( this.parent != null ) {
				this.parent.addChild( damageMotion );
				damageMotion.x = this.x;
				damageMotion.y = this.y;
			}
			if ( isDestroy && (client.isSelf() || ClientUtil.isCPU(client) ) ) { // 自分のミサイルだったら破壊判定
				dispatchEvent(new GameEvent(GameEvent.MISSILE_DESTROY, true, false, client.getClientID() + "-" + id));
			}
		}
		// ミサイル破壊モーション 効果音つき
		public function destroiedMotion():void {
			SoundManager.DamageSound.play();
			tf.visible = false;
			
			this.graphics.clear();
			this.graphics.lineStyle(1, 0xFFFF00, 0.5);
			this.graphics.drawCircle(0, 0, 5);
			body.visible = false;
			this.addEventListener(Event.ENTER_FRAME, onDestroyEnterFrame);
		}
		protected function onDestroyEnterFrame(e:Event):void {
			this.scaleX *= 1.2;
			this.scaleY *= 1.2;
			this.alpha *= 0.85;
			if ( this.alpha < 0.1 ) {
				this.removeEventListener(Event.ENTER_FRAME, onDestroyEnterFrame);
				this.parent.removeChild(this);
			}
		}
		public function update(time:Number):void {
			this.y = (time-attackTime) * durationT;
			body.rotationY += power * 8;
		}
		public function setText(text:String, isMySide:Boolean):void {
			isJapaneseWord = Roma.isJapaneseWord(text);
			this.word = text;
			this.text = HandicapUtil.getHandicapWord(text);
			this.isMySide = isMySide;
			if( !isMySide ){
				this.addChild( tf );
				isJapaneseWord?setJIndex(Roma.getRomaToType(word)):setIndex(0);
			}
		}
		public function tryType(char:String):int {
			
			Log.out("#1");
			if ( isJapaneseWord ) {
			Log.out("#2");
				char = char.toUpperCase();
				var str:String = Roma.getRomaToType(text, typed + char );
				//if ( str ) {
				if ( true ) {
			Log.out("#3");
					typed += char;
					var toType:String = str.substr(typed.length);
//					setJIndex(toType);
					setJIndex("<font color='#888888'>" + typed + "</font><font color='#FFFFFF'>" + toType + "</font>");
				} else {
					return Constants.RESULT_FAULT;
				}
				if ( toType.length ) {
					return Constants.RESULT_SUCCESS;
				} else {
					tf.visible = false;
					return Constants.RESULT_DESTROY;
				}
			} else {
			Log.out("#4");
				//if ( text.substr(index, 1) == char ) {
				if ( true ) {
					setIndex(++index);
				} else {
					return Constants.RESULT_FAULT;
				}
				if ( text.length == index ) {
					tf.visible = false;
					return Constants.RESULT_DESTROY;
				} else {
					return Constants.RESULT_SUCCESS;
				}
			}
		}
		protected var typed:String = "";
		public function tryStart(char:String):Boolean {
			if ( isMySide || (typed != "") || !tf.visible ) return false;
			
			if ( isJapaneseWord ) {
				char = char.toUpperCase();
				var str:String = Roma.getRomaToType(text, char);
				if ( str ) {
					typed = char;
					setJIndex("<font color='#888888'>" + typed + "</font><font color='#FFFFFF'>" + str.substr(typed.length) + "</font>");
					setFocus();
					return true;
				}
				return false;
			} else {
				if ( text.charAt(0) == char ) {
					setIndex(1);
					setFocus();
					return true;
				}
				return false;
			}
		}
		public function setFocus():void {
			tf.borderColor = 0xFFFF00;
		}
		public function reset():void {
			tf.borderColor = 0xFFFFFF;
			isJapaneseWord?setJIndex(Roma.getRomaToType(word)):setIndex(0);
		}
		protected function setJIndex(toType:String):void {
			tf.htmlText = "  " + text + "  " + "\n" + "  " + toType + "  ";
			tf.x = -0.5 * tf.width;
		}
		protected function setIndex(num:int):void {
			index = num;
			tf.htmlText = "  " + text.substr(num) + "  ";
			tf.x = -0.5 * tf.width;
		}
	}
	
	// チームチャット
	class ChatItem extends Sprite {
		protected var tf:TextField = new TextField();
		public function ChatItem(msg:String) {
			tf.autoSize = "left";
			tf.defaultTextFormat = new TextFormat(null, null, 0xFFFFFF);
			tf.text = msg;
			this.addChild(tf);
			this.mouseChildren = this.mouseEnabled = false;
			var timer:Timer = new Timer(Constants.TEAM_CHAT_DURATION, 1);
			timer.start();
			timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
		}
		protected function onTimer(e:TimerEvent):void {
			e.target.removeEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
			this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		protected function onEnterFrame(e:Event):void {
			this.alpha *= 0.9;
			if ( this.alpha < 0.1 ) {
				this.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
				if( this.parent != null ){
					this.parent.removeChild(this);
				}
			}
		}
	}
	
	// ディフェンスUI
	class DefenceTargets extends Sprite {
		protected var missileList:Array;
		protected var firstClaster:Array = [];
		protected var secondClaster:Array = [];
		protected var thirdClaster:Array = [];
		public function DefenceTargets() {
			
		}
	}
	
	// ゲーム画面管理
	class GameStage extends Sprite {
		protected var bgGrid:Sprite = new Sprite();
		protected var myMissiles:Sprite = new Sprite();
		protected var enemyMissiles:Sprite = new Sprite();
		protected var operationBG:Sprite = new Sprite();
		protected var operationUI:OperationUI = new OperationUI();
		protected var moveUI:MoveBatteryUI = new MoveBatteryUI();
		protected var enemyBG:Sprite = new Sprite();
		protected var batteryList:UDictionary = new UDictionary(false);
		protected var bulletList:Array = [];
		protected var _missileList:Array = [];
		protected var chatInputBar:TextField = new TextField();
		protected var effectSp:EffectSprite = new EffectSprite();
		protected var bg:BackGround = new BackGround(effectSp);
		protected var defenceTargets:DefenceTargets = new DefenceTargets();
		
		
		protected var _myBatteryMoveTo:Number;
		protected var chatUI:Sprite = new Sprite(); // インプットバーとログの表示
		public var cpu:CPUClient;
		
		public function GameStage() {
			drawBG();
			this.addChild( bgGrid );
			this.addChild( bg );
			bg.y = 40;
			effectSp.y = 40;
			
			bgGrid.addChild( myMissiles );
			myMissiles.rotation = 180;
			bgGrid.addChild( enemyMissiles );
			enemyMissiles.x = 40;
			myMissiles.x = 425;
			myMissiles.y = 340;
			enemyMissiles.y = 40;
			myMissiles.mouseEnabled = enemyMissiles.mouseEnabled = 
			myMissiles.mouseChildren = enemyMissiles.mouseChildren = false;
			this.addChild( operationBG );
			operationBG.addChild( moveUI ); // 砲台移動用
			moveUI.addEventListener(MouseEvent.CLICK, onMoveClick);
			operationBG.addChild( operationUI);
			operationUI.y = 40;
			
			operationBG.y = 340;
			this.addChild( enemyBG );
			bgGrid.mouseEnabled = bgGrid.mouseChildren = false;
			operationBG.mouseEnabled = false;
			
			operationUI.addEventListener(GameEvent.DEFENCE_TYPE, onDefenceKeyDown);

			this.addChild(chatUI);
			chatUI.x = 15;
			chatUI.y = 315;
			this.addChild(chatInputBar);
			chatInputBar.border = true;
			chatInputBar.type = "input";
			chatInputBar.borderColor = 0xFFFFFF;
			chatInputBar.defaultTextFormat = new TextFormat(null, null, 0xFFFFFF);
			chatInputBar.width = 250;
			chatInputBar.height = 20;
			chatInputBar.y = 315;
			chatInputBar.x = 15;
			chatInputBar.addEventListener(KeyboardEvent.KEY_UP, onChatKeyUp);
			chatInputBar.addEventListener(KeyboardEvent.KEY_DOWN, onChatKeyDown);
			chatInputBar.multiline = true;
			chatInputBar.visible = false;
		}
		protected var startTime:Number;
		public function setTimer(startTime:Number):void {
			bg.setFilter(0);
			this.startTime = startTime;
			this.addEventListener(Event.ENTER_FRAME, onCountDown);
		}
		protected var lastTime:Number = -1;
		protected function onCountDown(e:Event):void {
			var rest:int = ((startTime - ServerClockUtil.getTime()) / 1000);
			if ( rest <= 0 ) this.removeEventListener(Event.ENTER_FRAME, onCountDown);
			if ( lastTime == rest ) return;
			lastTime = rest;
			var temp:TextField = new TextField();
			temp.defaultTextFormat = new TextFormat("Arial", 60, 0xFFFF00, true);
			temp.text = (rest >= 0)?String(1+int(rest)):"";
			temp.autoSize = "left";
			bg.bitmapData.draw(temp, new Matrix(1,0,0,1,232-0.5*temp.textWidth,130));
		}
		public function updateComboStatus(client:IClient, attrName:String, isStart:Boolean):void {
			if( batteryList[client.getClientID()] != undefined )
				batteryList[client.getClientID()].updateCombo(attrName, isStart);
		}
		protected function clearLoserAndMissiles():void {
			for ( var i:int = 0; i < _missileList.length; i++ ) {
				if (_missileList[i].parent != null )_missileList[i].parent.removeChild(_missileList[i]);
			}
			_missileList = [];
			for ( i = 0; i < bulletList.length; i++ ) {
				if (bulletList[i].parent != null )bulletList[i].parent.removeChild(bulletList[i]);
			}
			bulletList = [];
			
			var clearTarget:Sprite = (mySideWon)?enemyBG:operationBG;
			for( var id:String in batteryList ) {
				if( batteryList[id].parent == clearTarget ){
					clearTarget.removeChild(batteryList[id]);
				}
			}
		}
		public function clearAll():void {
			for ( var i:int = 0; i < _missileList.length; i++ ) {
				if (_missileList[i].parent != null )_missileList[i].parent.removeChild(_missileList[i]);
			}
			_missileList = [];
			for ( i = 0; i < bulletList.length; i++ ) {
				if (bulletList[i].parent != null )bulletList[i].parent.removeChild(bulletList[i]);
			}
			bulletList = [];
			
			for each( var battery:Battery in batteryList ) {
				if( battery.parent != null )
					battery.parent.removeChild(battery);
			}
			batteryList = new UDictionary(false);
		}
		protected function onShortcutKeyDown(e:KeyboardEvent):void {
			switch( e.keyCode ) {
				case Keyboard.TAB:
						switch( operationUI.getPlayMode() ) {
							case Constants.PLAY_MODE_ATTACKER:
									operationUI.setFocus();
								break;
							case Constants.PLAY_MODE_DEFENDER:
									changeFocus(e.shiftKey);
								break;
						}
					break;
				case Constants.CHAT_KEY:
						chatInputMode = true;
					break;
				case Keyboard.UP:
						SoundManager.ClickSound.play();
						dispatchEvent( new GameEvent(GameEvent.MODE_ATTACK, true) );
					break;
				case Keyboard.DOWN:
						SoundManager.ClickSound.play();
						dispatchEvent( new GameEvent(GameEvent.MODE_DEFENCE, true) );
					break;
				case Keyboard.RIGHT:
						myBatteryMoveTo = myBatteryMoveTo + 30;
					break;
				case Keyboard.LEFT:
						myBatteryMoveTo = myBatteryMoveTo - 30;
					break;
			}
		}
		public function hitMotion(isMySide:Boolean):void {
			var hitMotion:HitMotion = new HitMotion();
			this.addChild( hitMotion );
			if ( isMySide ) {
				hitMotion.y = 40;
			} else {
				hitMotion.y = 340;
				this.addEventListener(Event.ENTER_FRAME, onHitEnterFrame);
			}
		}
		protected function onHitEnterFrame(e:Event):void {
			if ( this.x == 0 ) {
				this.x = -5 + 10 * Math.random();
				this.y = -5 + 10 * Math.random();
			} else {
				this.x = this.y = 0;
				this.removeEventListener(Event.ENTER_FRAME, onHitEnterFrame);
			}
		}
		public function putChatMessage(msg:String):void {
			var chatItem:ChatItem = new ChatItem(msg);
			chatUI.addChild( chatItem );
			var count:int = chatUI.numChildren;
			for ( var i:int = 0; i < count; i++ ){
				chatUI.getChildAt(i).y -= chatItem.height;
			}
		}
		protected var endEffect:Sprite = new Sprite();
		protected var mySideWon:Boolean;
		public function end(isVictory:Boolean):void {
			mySideWon = isVictory;
			this.stage.removeEventListener(KeyboardEvent.KEY_DOWN, onShortcutKeyDown);
			
			this.addEventListener( Event.ENTER_FRAME, onEndEnterFrame);
			this.addChild( endEffect );
			endEffect.graphics.clear();
			endEffect.scaleX = 1.0;
			endEffect.scaleY = 1.0;
			if ( isVictory ) {
				endEffect.graphics.lineStyle(8, 0xFFFFFF);
				endEffect.x = 230;
				endEffect.y = 20;
			} else {
				endEffect.graphics.lineStyle(8, 0xFFCCCC);
				endEffect.x = 230;
				endEffect.y = 360;
			}
			endEffect.graphics.moveTo( -400, 0);
			endEffect.graphics.lineTo( 400, 0);
			endEffect.graphics.moveTo( 0, -400);
			endEffect.graphics.lineTo( 0, 400);
			endEffect.alpha = 1.5;
			firstFlg = true;
		}
		protected var firstFlg:Boolean;
		protected function onEndEnterFrame(e:Event):void {
			endEffect.scaleX *= 1.1;
			endEffect.scaleY *= 1.1;
			endEffect.alpha *= 0.99;
			if ( endEffect.alpha < 1.0 && firstFlg ) {
				clearLoserAndMissiles();
				firstFlg = false;
			}
			if ( endEffect.alpha < 0.3 ) {
				this.removeChild(endEffect);
				this.removeEventListener( Event.ENTER_FRAME, onEndEnterFrame );
				onEnd();
			}
		}
		protected function onEnd():void{
			this.removeEventListener( Event.ENTER_FRAME, onEnterFrame);
			dispatchEvent(new GameEvent(GameEvent.END_GAME, true));
		}
		public function setBatteryAngle(client:IClient, angle:Number):void {
			if ( batteryList[client.getClientID()] == undefined ) return;
			batteryList[client.getClientID()].setAngle(angle);
		}
		protected function onMissileDamage(e:GameEvent):void { // 未使用だったり
			for ( var i:int = 0; i < bulletList.length; i++ ) {
				var bullet:Bullet = bulletList[i];
				if ( e.target == bullet ) {
					bulletList.splice(i, 1);
					bullet.parent.removeChild(bullet);
					break;
				}
			}
		}
		public function destroy(destroyer:IClient, missileClientId:String, missileId:int):void {
			for ( var i:int = 0; i < _missileList.length; i++ ) {
				var missile:Missile = _missileList[i];
				if ( missile.client.getClientID() == missileClientId && missile.id == missileId ) {
					var p:Point = effectSp.globalToLocal(missile.localToGlobal(new Point(0, 0)));
					effectSp.missileDestroy(p);
					missile.destroiedMotion();
					_missileList.splice(i, 1);
					break;
				}
			}
		}
		protected var focus:Missile = null;
		public function addBullet(shooter:IClient, target:IClient, missileId:int, shootTime:Number, bulletSpeed:Number, isMySide:Boolean, isDestroy:Boolean):void {
			var count:int = _missileList.length;
			for ( var i:int = 0; i < count; i++ ) {
				var missile:Missile = _missileList[i];
				if ( missile.client == target && missile.id == missileId ) {
					var bullet:Bullet = new Bullet(missile, bulletSpeed, 425 - Number(shooter.getAttribute(Score.myGameRoom, Constants.BATTERY_X)), -Constants.BATTERY_DEFAULT_Y, shootTime, isDestroy );
					if ( shooter.isSelf() ) { shooter.setAttribute(Constants.ANGLE, bullet.getAngle(), Score.myGameRoom) };
					if ( cpu != null && ClientUtil.isCPU(shooter) ) batteryList["0"].setAngle(bullet.getAngle());
					var bulletLayer:Sprite = (isMySide?myMissiles:enemyMissiles);
					bulletLayer.addChild( bullet );
					bulletList.push(bullet);
					break;
				}
			}
		}
		protected var focusIndex:int = -1;
		protected function changeFocus(isShiftKeyDown:Boolean):void {
			if ( focus != null ) {
				focus.reset();
				focus = null;
			}
			var count:int = _missileList.length;
			if ( count == 0 ) return;
			for ( var i:int = 0; i < count; i++ ) {
				if ( !Missile(_missileList[i]).isMySide ) break;
			}
			if ( i == count ) return;
			do {
//				focusIndex = ( (focusIndex + (isShiftKeyDown)? -1:1) + count) % count;
				focusIndex += (isShiftKeyDown)? -1:1;
				focusIndex = (focusIndex + count) % count;
			} while ( Missile(_missileList[focusIndex]).isMySide )
			focus = _missileList[focusIndex];
			focus.setFocus();
			var focusParent:DisplayObjectContainer = focus.parent;
			if ( focusParent != null ) {
				focusParent.addChild( focus );
			}
		}
		protected var isPerfect:Boolean = true;
		protected function onDefenceKeyDown(e:GameEvent):void {
						
			focus = _missileList[0];
			
			var keyCode:int = int(e.data);
			var char:String = String.fromCharCode(keyCode).toLowerCase();
			if ( focus != null && focus.parent == null ) {
				focus = null;
			}
			if ( focus != null ) {
				if ( focus.parent != null ) {
					if ( keyCode == Keyboard.ESCAPE ) {
						focus.reset();
						focus = null;
						return;
					}
					var result:int = focus.tryType(char);
					switch( result ) {
						case Constants.RESULT_SUCCESS:
							Score.type++;
							Score.correct++;
							if ( cpu != null ) {
								LogAnalyzer.addDefenceLog(1);
								addBullet(cpu.self, cpu, focus.id, ServerClockUtil.getTime(), Constants.BULLET_SPEED, true, false);
							} else {
								dispatchEvent( new GameEvent(GameEvent.SHOOT, false, false, focus.client.getClientID() + "-" + focus.id + "-0") );
							}
							break;
						case Constants.RESULT_FAULT:
							Score.type++;
							if ( (Score.defenceCombo >= Constants.DEFENCE_COMBO_START) ) {
								dispatchEvent( new GameEvent(GameEvent.DEFENCE_COMBO_END) );
							}
							Score.defenceCombo = 0;
							isPerfect = false;
							break;
						case Constants.RESULT_DESTROY:
							Score.type++;
							Score.correct++;
							Score.defence++;
							if ( isPerfect ) Score.defenceCombo++;
							if ( (Score.defenceCombo == Constants.DEFENCE_COMBO_START) ) {
								dispatchEvent( new GameEvent(GameEvent.DEFENCE_COMBO_START) );
								SoundManager.ComboSound.play();
							}
							if ( cpu != null ) {
								LogAnalyzer.addDefenceLog(1);
								addBullet(cpu.self, cpu, focus.id, ServerClockUtil.getTime(), Constants.BULLET_SPEED, true, true);
							} else {
								dispatchEvent( new GameEvent(GameEvent.SHOOT, false, false, focus.client.getClientID() + "-" + focus.id + "-1") ); // 破壊フラグk	
							}
							if ( Score.getDefenceBonusRate() > (Math.random()*100) ) {
								dispatchEvent( new GameEvent(GameEvent.ATTACK_REFLECT, false, false, focus.word + "-" + focus.power) );
							}
							focus = null;
							break;
					}
				}
			} else {
				var count:int = _missileList.length;
				for ( var i:int = 0; i < count; i++ ) {
					if ( Missile(_missileList[i]).tryStart(char) ) {
						Score.correct++;
						focus = _missileList[i];
						var focusParent:DisplayObjectContainer = focus.parent;
						if ( focusParent != null ) {
							focusParent.addChild( focus );
						}
						if ( cpu != null ) {
							addBullet(cpu.self, cpu, focus.id, ServerClockUtil.getTime(), Constants.BULLET_SPEED, true, false);
							LogAnalyzer.addDefenceLog(1);
						} else {
							dispatchEvent( new GameEvent(GameEvent.SHOOT, false, false, focus.client.getClientID() + "-" + focus.id + "-0") );
						}
						break;
					}
				}
				if ( focus != null ) {
					Score.type++;
					isPerfect = true;
				}
			}
		}
		// クライアント退室時にミサイルと砲台を除去
		public function removeMissilesOf(client:IClient):void {
			var count:int = _missileList.length;
			var removeList:Array = [];
			for ( var i:int = 0; i < count; i++ ) {
				if ( Missile(_missileList[i]).client == client ) {
					removeList.unshift(i);
				}
			}
			for ( i = 0; i < removeList.length; i++ ) {
				_missileList[removeList[i]].parent.removeChild( _missileList[removeList[i]] );
				_missileList.splice(removeList[i], 1);
			}
			
			if ( batteryList[client.getClientID()] != undefined ) {
				batteryList[client.getClientID()].parent.removeChild(batteryList[client.getClientID()]);
			}
		}
		public function sendLogTo(client:IClient):void {
			var count:int = _missileList.length;
			for ( var i:int = 0; i < count; i++ ) {
				var missile:Missile = Missile(_missileList[i]);
				if (  missile.isSelf ) {
					client.sendMessage(Constants.ATTACK_LOG, missile.text, missile.id, missile.power, missile.attackTime, missile.arriveTime, missile.x);
				}
			}
		}
		protected function onEnterFrame(e:Event):void {
			if( cpu != null ) cpu.missiles = _missileList;
			var count:int = _missileList.length;
			var removeList:Array = [];
			var nowTime:Number = ServerClockUtil.getTime();
			for ( var i:int = 0; i < count; i++ ) {
				var missile:Missile = _missileList[i];
				missile.update( nowTime );
				if ( missile.y > 300 ) {
					removeList.unshift(i);
					if ( missile.isSelf ) {
						dispatchEvent(new GameEvent(GameEvent.HIT, false, false, String(missile.id)+"-"+String(missile.power)) );
					} else if ( cpu && ClientUtil.isCPU(missile.client) ) {
						dispatchEvent(new GameEvent(GameEvent.HIT, false, false, String(missile.id)+"-cpu") );
					}
				}
			}
			for ( i = 0; i < removeList.length; i++ ) {
				_missileList[removeList[i]].parent.removeChild( _missileList[removeList[i]] );
				_missileList.splice(removeList[i], 1);
			}
			var bcount:int = bulletList.length;
//			for ( i = 0; i < bcount; i++ ) {
			for ( i = bulletList.length-1; i >= 0; i-- ) {
				if ( bulletList[i].update( nowTime ) ) {
					bulletList[i].parent.removeChild(bulletList[i]);
					bulletList.splice(i, 1);
				}
			}
		}
		public function addAttack(client:IClient, text:String, isMySide:Boolean, id:int, power:Number, arriveTime:Number, xpos:Number, isReflect:Boolean = false):void {
			var missile:Missile = new Missile(client, ServerClockUtil.getTime(), arriveTime, id, power, client.isSelf());
			var target:Sprite = (isMySide?myMissiles:enemyMissiles);
			target.addChildAt( missile, 0 );
			missile.x = xpos;
			_missileList.push(missile);
			missile.setText( text, isMySide );
			
			if( batteryList[client.getClientID()] != undefined ) {
				if ( isReflect ) {
					Battery(batteryList[client.getClientID()]).reflectMotion();
				} else {
					Battery(batteryList[client.getClientID()]).attackMotion();
				}
			}
		}
		
		// addAttackと統一しても問題ないとは思う・・
		public function addAttackLog(client:IClient, text:String, isMySide:Boolean, id:int, power:Number,attackTime:Number, arriveTime:Number, xpos:Number):void {
			var missile:Missile = new Missile(client, attackTime, arriveTime, id,  power, client.isSelf());
			var target:Sprite = (isMySide?myMissiles:enemyMissiles);
			target.addChildAt( missile, 0 );
			missile.x = xpos;
			_missileList.push(missile);
			missile.setText( text, isMySide );
		}

		protected function onMoveClick(e:MouseEvent):void {
			myBatteryMoveTo = moveUI.mouseX;
		}
		
		// 砲台設置など
		public function setBattery(client:IClient, position:Number, isMySide:Boolean):void {
			if ( client.isSelf() ) _myBatteryMoveTo = position;
			if ( batteryList[client.getClientID()] == undefined ) { // 未設置
				var battery:Battery = new Battery(isMySide, ClientUtil.getBaseLevel(client), ClientUtil.getColor(client), ClientUtil.getBaseColor(client));
				battery.updateCombo( "attackCombo", String(client.getAttribute("", "attackCombo")) == "true" );
				battery.updateCombo( "defenceCombo", String(client.getAttribute("", "defenceCombo")) == "true" );
//				battery.draw(ClientUtil.getBaseLevel(client), ClientUtil.getColor(client), ClientUtil.getBaseColor(client));
				battery.setName( ClientUtil.getUserName(client) );
				batteryList[client.getClientID()] = battery;
				(isMySide?operationBG:enemyBG).addChild( battery );
				battery.x = (isMySide?position:(465-position));
				battery.y = Constants.BATTERY_DEFAULT_Y;
				battery.setMode( client.getAttribute("", Constants.PLAY_MODE));
			} else {
				battery = batteryList[client.getClientID()];
				(isMySide?operationBG:enemyBG).addChild( battery );
				battery.setSide(isMySide);
				battery.x = (isMySide?position:(465 - position));
			}
			// 設置完了
		}
		
		public function setPlayMode(client:IClient, playMode:String):void {
			if ( client.isSelf() ) {
				operationUI.setMode(playMode);
				moveUI.setMode(playMode);
			}
			if ( batteryList[client.getClientID()] != undefined ) {
				Battery(batteryList[client.getClientID()]).setMode(playMode);
			}
			// 防御UI表示/非表示
		}
		
		protected function drawBG():void {
			bgGrid.graphics.lineStyle(0, 0x00FF00, 0.5);
			for ( var i:int = 0; i < 16; i++ ) {
				bgGrid.graphics.moveTo( 7 + 30 * i, 0 );
				bgGrid.graphics.lineTo( 7 + 30 * i, 465 );
				bgGrid.graphics.moveTo( 0, 7 + 30 * i );
				bgGrid.graphics.lineTo( 465, 7 + 30 * i );
			}
			setup( Constants.TEAM_NONE );
		}
		protected function drawOperationBG(color:uint):void {
			operationBG.graphics.clear();
			operationBG.graphics.beginGradientFill("linear", [color, color], [1.0, 0.0], [0, 255], new Matrix(0, -0.1, 0.1, 0, 0, 50));
			operationBG.graphics.drawRect( 0, 0, 465, 125);
			operationBG.graphics.lineStyle(2, 0xFFFFFF);
			operationBG.graphics.moveTo(0, 0);
			operationBG.graphics.lineTo(465, 0);
			operationBG.graphics.moveTo(0, 40);
			operationBG.graphics.lineTo(465, 40);
		}
		public function setDamage(mySide:int, enemySide:int):void {
			operationBG.graphics.lineStyle(2, 0xFF0000 | (0x101 * int(255 - 255 * (mySide / Constants.VICTORY_POINT))));
			enemyBG.graphics.lineStyle(2, 0xFF0000 | (0x101 * int(255 - 255 * (enemySide / Constants.VICTORY_POINT))));
			operationBG.graphics.moveTo(0, 0);
			operationBG.graphics.lineTo(465, 0);
			enemyBG.graphics.moveTo(0, 40);
			enemyBG.graphics.lineTo(465, 40);
		}
		protected function drawEnemyBG(color:uint):void {
			enemyBG.graphics.clear();
			enemyBG.graphics.beginGradientFill("linear", [color, color], [1.0, 0.0], [0, 255], new Matrix(0, 0.1, -0.1, 0, 0, -10));
			enemyBG.graphics.drawRect(0, 0, 465, 40);
			enemyBG.graphics.lineStyle(2, 0xFFFFFF);
			enemyBG.graphics.moveTo(0, 40);
			enemyBG.graphics.lineTo(465, 40);
		}
		
		protected var _chatInputMode:Boolean = false;
		protected function onChatKeyUp(e:KeyboardEvent):void {
			if ( e.keyCode == Keyboard.ENTER ) {
				var arr:Array = chatInputBar.text.split("\r");
				if ( arr.length >= 2 ) {
/*					
					if ( chatInputBar.text == "\r" ) {
						chatInputMode = false;
						operationUI.setFocus();
						return;
					}
*/
					chatInputMode = false;
					operationUI.setFocus();
					dispatchEvent(new GameEvent(GameEvent.CHAT, false, false, arr.join("")));
					chatInputBar.text = "";
				}
			}
		}
		protected function onChatKeyDown(e:KeyboardEvent):void {
			if ( e.keyCode == Keyboard.LEFT || e.keyCode == Keyboard.RIGHT ) {
				e.stopPropagation();
			}
		}
		// チームに入ってゲーム開始
		public function setup(team:String):void {
			var operationBGColor:uint;
			var enemyBGColor:uint;
			operationUI.reset();
			if ( team == Constants.TEAM_BLUE ) {
				operationUI.setup();
				operationBGColor = 0x0000AA;
				enemyBGColor = 0x990000;
//				operationUI.setMode(Constants.PLAY_MODE_DEFENDER); // デフォルトで防御モード
				this.stage.addEventListener(KeyboardEvent.KEY_DOWN, onShortcutKeyDown);
				this.addEventListener( Event.ENTER_FRAME, onEnterFrame );
			} else if ( team == Constants.TEAM_RED ) {
				operationUI.setup();
				operationBGColor = 0x990000;
				enemyBGColor = 0x0000AA;
//				operationUI.setMode(Constants.PLAY_MODE_ATTACKER); // デフォルトで攻撃モード
				this.stage.addEventListener(KeyboardEvent.KEY_DOWN, onShortcutKeyDown);
				this.addEventListener( Event.ENTER_FRAME, onEnterFrame );
			} else if ( team == Constants.TEAM_NONE ) {
				operationBGColor = 0x666666;
				enemyBGColor = 0x666666;
				operationUI.setMode("");
				if ( this.stage != null ) {
					this.stage.removeEventListener( KeyboardEvent.KEY_DOWN, onShortcutKeyDown );
				}
			}
			drawOperationBG(operationBGColor);
			drawEnemyBG(enemyBGColor);
			enable = false;
		}
		protected var _enable:Boolean = false;
		public function set enable(value:Boolean ):void {
			if ( !_enable && value ) {
				bg.setFilter(1);
				var temp:TextField = new TextField();
				temp.defaultTextFormat = new TextFormat("Arial", 60, 0xFFFF00, true);
				temp.text = "START";
				temp.autoSize = "left";
				bg.bitmapData.draw(temp, new Matrix(1, 0, 0, 1, 232 - 0.5 * temp.textWidth, 130));
				if( cpu != null ){
					cpu.start();
					cpu.addEventListener(GameEvent.ATTACK, onCPUAttack);
					cpu.addEventListener(GameEvent.SHOOT, onCPUShoot);
				}
			} else if ( _enable && !value ) {
				if( cpu != null ){
					cpu.disable();
					cpu.removeEventListener(GameEvent.ATTACK, onCPUAttack);
					cpu.removeEventListener(GameEvent.SHOOT, onCPUShoot);
				}
			}
			_enable = value;
			trace( "enable!!", value );
			this.mouseChildren = value;
			this.operationUI.visible = value;
			if( value ){
				this.addEventListener( GameEvent.MISSILE_DAMAGE, onMissileDamage );
			} else {
				this.removeEventListener( GameEvent.MISSILE_DAMAGE, onMissileDamage );
			}
		}
		// 一人用
		
		protected function onCPUAttack(e:GameEvent):void {
			SoundManager.AttackSound.play();
			batteryList["0"].setMode(Constants.PLAY_MODE_ATTACKER);
			
			addAttack(cpu, e.data, false, cpu.attackid, 1, ServerClockUtil.getTime()+Constants.MISSILE_ARRIVE_TIME, -40+80*Math.random()+Number(cpu.getAttribute("",Constants.BATTERY_X)));
		}
		protected function onCPUShoot(e:GameEvent):void {
			batteryList["0"].setMode(Constants.PLAY_MODE_DEFENDER);
			addBullet(cpu, cpu.self, cpu.focus.id, ServerClockUtil.getTime(), Constants.BULLET_SPEED, false, (e.data=="1"));
		}
		
		public function get myBatteryMoveTo():Number { return _myBatteryMoveTo; }
		
		public function set myBatteryMoveTo(value:Number):void 
		{
			if ( value < 22 ) value = 22;
			if ( value > 442 ) value = 442;
			_myBatteryMoveTo = value;
			dispatchEvent( new GameEvent(GameEvent.MOVE_BATTERY) );
		}
		public function get chatInputMode():Boolean { return _chatInputMode; }
		
		public function set chatInputMode(value:Boolean):void 
		{
			if ( _chatInputMode == false && value == true ) {
				chatInputBar.visible = true;
			} else if( !value ) {
				chatInputBar.visible = false;
			}
			if ( value ) {
				stage.focus = chatInputBar;
				if ( Capabilities.hasIME ) {
					try {
						IME.enabled = true;
					} catch(e:*){}
				}
			}

			_chatInputMode = value;
		}
	}
	class ColorSettingPanel extends Sprite {
		protected var colorTable:Array = [
			0x888888, 0xAA8888, 0x88AA88, 0x8888AA, 0x88AAAA, 0xAA88AA, 0xAAAA88, 0x666666, 0xAA6666, 0x66AA66,
			0x6666AA, 0x226666, 0x662266, 0x666622, 0xCC2222, 0x22CC22, 0x2222CC, 0x22CCCC, 0xCC22CC, 0xCCCC22
		];
		protected var bg:Sprite = new Sprite;
		protected var colors:Sprite = new Sprite;
		public function ColorSettingPanel() {
			bg.mouseEnabled = false;
			this.addChild( colors );
			this.addChild( bg );
			colors.addEventListener(MouseEvent.CLICK, onClick);
			colors.buttonMode = true;
		}
		public function setLevel(level:int):void {
			if ( level > colorTable.length ) level = colorTable.length;
			bg.graphics.clear();
			colors.graphics.clear();
			for ( var i:int = 0; i <level; i++ ) {
				bg.graphics.lineStyle( 0, 0xFFFFFF );
				bg.graphics.drawRect( 27 * (i % 10), 28 * int(i / 10), 16, 16);
				colors.graphics.beginFill( colorTable[i] );
				colors.graphics.drawRect( 27 * (i % 10), 28 * int(i / 10), 16, 16);
			}
			for ( ; i < 20; i++ ) { // 20: max level
				bg.graphics.lineStyle(0, 0xFFFFFF, 0.25);
				bg.graphics.drawRect( 27 * (i % 10), 28 * int(i / 10), 16, 16);
			}
		}
		protected function onClick(e:MouseEvent):void {
			var index:int = 10 * int(e.localY / 28) + int(e.localX / 27);
			dispatchEvent(new GameEvent("color", false, false, index + "-" + colorTable[index]) );
		}
	}
	
	// 設定ウィンドウ
	class OptionWindow extends Sprite {
		protected var attackTF:TextField = new TextField();
		protected var defenceTF:TextField = new TextField();
		protected var titleTF:TextField = new TextField();
		protected var baseColorTF:TextField = new TextField();
		protected var colorTF:TextField = new TextField();
		protected var handicapTitleTF:TextField = new TextField();
		protected var handicapTF:TextField = new TextField();
		protected var nameTF:TextField = new TextField();
		protected var nameChangeTF:TextField = new TextField();
		protected var languageTF:TextField = new TextField();
		protected var attackBattery:Battery = new Battery( true, 0, 0, 0);
		protected var defenceBattery:Battery = new Battery( true, 0, 0, 0);
		protected var baseColorPanel:ColorSettingPanel = new ColorSettingPanel();
		protected var colorPanel:ColorSettingPanel = new ColorSettingPanel();
		protected var closeBtn:Button = new Button(100, 20, "CLOSE", true, false, 0x888888);
		protected var normalBtn:Button = new Button(100, 20, "NO HANDICAP", true, true, 0x0);
		protected var hardBtn:Button = new Button(100, 20, "DOUBLE WORD", true, false, 0x0);
		protected var veryHardBtn:Button = new Button(100, 20, "TRIPLE WORD", true, false, 0x0);
		protected var EngBtn:Button = new Button(90, 20, "ONLY ENGLISH", true, false, 0x0);
		protected var BothBtn:Button = new Button(80, 20, "ALL WORDS", true, false, 0x0);
		protected var JpnBtn:Button = new Button(80, 20, "日本語のみ", true, false, 0x0);
		
		public function OptionWindow() {
			this.graphics.beginFill(0x0, 0.5);
			this.graphics.drawRect( -20, -40, 465, 465); // magic number...
			this.graphics.endFill();
			this.graphics.beginFill(0x888888, 0.90);
			this.graphics.lineStyle(2, 0xFFFFFF);
			this.graphics.drawRoundRect(0, 0, 425, 330, 10);
			this.graphics.lineStyle(1, 0xFFFFFF);
			this.graphics.beginFill(0x0);
			this.graphics.drawRoundRect(15, 80, 275, 33, 10);
			this.graphics.drawRoundRect(15, 140, 275, 58, 10);
			this.graphics.drawRoundRect(15, 225, 275, 58, 10);
//			this.graphics.drawRoundRect(310, 170, 100, 113, 10);
			this.graphics.moveTo(300, 10);
			this.graphics.lineTo(300, 50);
			this.graphics.moveTo(15, 55);
			this.graphics.lineTo(410, 55);
//			this.graphics.moveTo(300, 60);
//			this.graphics.lineTo(300, 283);
			
			baseColorPanel.x = 22;
			baseColorPanel.y = 147;
			colorPanel.x = 22;
			colorPanel.y = 232;
			
			TextFieldUtil.setupNoBorder(attackTF, false);
			TextFieldUtil.setupNoBorder(defenceTF, false);
			TextFieldUtil.setupNoBorder(titleTF, false);
			TextFieldUtil.setupNoBorder(baseColorTF, false);
			TextFieldUtil.setupNoBorder(colorTF, false);
			TextFieldUtil.setupNoBorder(handicapTitleTF, false);
			TextFieldUtil.setupNoBorder(handicapTF, false);
			TextFieldUtil.setupNoBorder(nameTF, false);
			TextFieldUtil.setupNoBorder(languageTF, false);
			TextFieldUtil.setupBorder(nameChangeTF);
			
			attackTF.text = " ATTACK MODE";
			defenceTF.text = "DEFENCE MODE";
			titleTF.text = "COLOR SETTING";
			baseColorTF.text = "COLOR 1";
			colorTF.text = "COLOR 2";
			handicapTitleTF.text = "HANDICAP SETTING";
			nameTF.text = "YOUR NAME";
			languageTF.text = "ATTACK LANGUAGE SELECT";
			nameChangeTF.type = "input";
			nameChangeTF.text = String(LocalData.read(Constants.LD_FIELD, Constants.USER_NAME));
			
			this.addChild(titleTF);
			this.addChild(baseColorTF);
			this.addChild(colorTF);
			
			this.addChild(nameTF);
			this.addChild(nameChangeTF);
			
			this.addChild(languageTF);
			this.addChild(EngBtn);
			this.addChild(BothBtn);
			this.addChild(JpnBtn);
			
//			this.addChild(handicapTF);
//			this.addChild(handicapTitleTF);

			this.addChild(attackTF);
			this.addChild(defenceTF);
			this.addChild(attackBattery);
			this.addChild(defenceBattery);
			this.addChild(closeBtn);
//			this.addChild(normalBtn);
//			this.addChild(hardBtn);
//			this.addChild(veryHardBtn);
			
			nameTF.x = 310;
			nameTF.y = 7;
			nameChangeTF.x = 310;
			nameChangeTF.y = 27;
			nameChangeTF.width = 100;
			nameChangeTF.height = 20;
			
			languageTF.x = 15;
			languageTF.y = 7;
			EngBtn.x = 15;
			BothBtn.x = 115;
			JpnBtn.x =  205;
			EngBtn.y = BothBtn.y = JpnBtn.y = 27;
			
//			handicapTitleTF.x = 310;
//			handicapTitleTF.y = 60;
//			handicapTF.x = 315;
//			handicapTF.y = 175;
//			handicapTF.width = 90;
//			handicapTF.wordWrap = true;
//			normalBtn.x = 310;
//			normalBtn.y = 80;
//			hardBtn.x = 310;
//			hardBtn.y = 110;
//			veryHardBtn.x = 310;
//			veryHardBtn.y = 140;
			titleTF.x = 15;
			titleTF.y = 60;
			baseColorTF.x = 15;
			baseColorTF.y = 120;
			colorTF.x = 15;
			colorTF.y = 205;
			attackTF.x = 20;
			attackTF.y = 90;
			defenceTF.x = 175;
			defenceTF.y = 90;
			attackBattery.x = 110;
			attackBattery.y = 95;
			defenceBattery.x = 265;
			defenceBattery.y = 98;
			closeBtn.x = 170;
			closeBtn.y = 300;
			this.addChild( baseColorPanel );
			this.addChild( colorPanel );
			
			attackBattery.setMode(Constants.PLAY_MODE_ATTACKER);
			defenceBattery.setMode(Constants.PLAY_MODE_DEFENDER);
			closeBtn.addEventListener(MouseEvent.CLICK, onClick);
			
			EngBtn.addEventListener(MouseEvent.CLICK, onEngClick);
			BothBtn.addEventListener(MouseEvent.CLICK, onBothClick);
			JpnBtn.addEventListener(MouseEvent.CLICK, onJpnClick);
			
//			normalBtn.addEventListener(MouseEvent.CLICK, onNormalClick);
//			hardBtn.addEventListener(MouseEvent.CLICK, onHardClick);
//			veryHardBtn.addEventListener(MouseEvent.CLICK, onVeryHardClick);
			baseColorPanel.addEventListener("color", onBaseColor);
			colorPanel.addEventListener("color", onColor);
		}
		protected function onEngClick(e:MouseEvent):void {
			Score.japaneseRate = 0;
			setLanguage();
		}
		protected function onBothClick(e:MouseEvent):void {
			Score.japaneseRate = 0.5;
			setLanguage();
		}
		protected function onJpnClick(e:MouseEvent):void {
			Score.japaneseRate = 1.0;
			setLanguage();
		}
		protected function onNormalClick(e:MouseEvent):void {
			dispatchEvent( new GameEvent(GameEvent.HANDICAP_SELECT, true, false, "1") );
		}
		protected function onHardClick(e:MouseEvent):void {
			dispatchEvent( new GameEvent(GameEvent.HANDICAP_SELECT, true, false, "2") );
		}
		protected function onVeryHardClick(e:MouseEvent):void {
			dispatchEvent( new GameEvent(GameEvent.HANDICAP_SELECT, true, false, "3") );
		}
		protected function onBaseColor(e:GameEvent):void {
			dispatchEvent( new GameEvent(GameEvent.COLOR_SELECT, true, false, Constants.USER_BATTERY_BASE_COLOR + "-" + e.data) );
		}
		public function setLanguage():void {
			EngBtn.selected = BothBtn.selected = JpnBtn.selected = false;
			switch( Score.japaneseRate ) {
				case 0:
					EngBtn.selected = true;
					break;
				case 1:
					JpnBtn.selected = true;
					break;
				default:
					BothBtn.selected = true;
					break;
			}
		}
		public function setHandicap():void {
			normalBtn.selected = hardBtn.selected = veryHardBtn.selected = false;
			var str:String = "";
			switch( Score.handicap ) {
				case 1:
					normalBtn.selected = true;
					str= "NO HANDICAP.\n";
					break;
				case 2:
					hardBtn.selected = true;
					str = "YOU HAVE TO TYPE WORDS TWICE TO ATTACK OR DEFENCE.\n";
					break;
				case 3:
					veryHardBtn.selected = true;
					str= "YOU HAVE TO TYPE WORDS THREE TIMES TO ATTACK OR DEFENCE.\n";
					break;
			}
			
			str +="\nATK:" +int(Score.attackPower / 1000) + "->" +int(Score.attackPower / 1000 / Score.handicap);
			str += "\nDEF:" +int(Score.defencePower / 1000) + "->" +int(Score.defencePower / 1000 / Score.handicap);
			
			handicapTF.text = str;
		}
		public function setLevel():void {
			baseColorPanel.setLevel(Score.getLevel());
			colorPanel.setLevel(Score.getLevel());
		}
		protected function onColor(e:GameEvent):void {
			dispatchEvent( new GameEvent(GameEvent.COLOR_SELECT, true, false, Constants.USER_BATTERY_COLOR + "-" + e.data) );
		}
		
		// 砲台の色設定
		public function setColors(baseLevel:int, color:int, baseColor:int):void {
			attackBattery.draw(baseLevel, color, baseColor);
			defenceBattery.draw(baseLevel, color, baseColor);
		}
		protected function onClick(e:MouseEvent):void {
			var oldName:String = String(LocalData.read(Constants.LD_FIELD, Constants.USER_NAME));
			if ( nameChangeTF.text != oldName && nameChangeTF.text != "" ) {
				dispatchEvent(new GameEvent(GameEvent.NAME_CHANGE, true, false, nameChangeTF.text));
				LocalData.write(Constants.LD_FIELD, Constants.USER_NAME, nameChangeTF.text);
			}
			Score.save();
			dispatchEvent(new Event("close"));
		}
	}
	
	// 情報ウィンドウ
	class InfoUI extends Sprite {
		protected var lobbyUserNumText:TextField = new TextField();
		protected var blueTeamNumText:TextField = new TextField();
		protected var redTeamNumText:TextField = new TextField();
		protected var defaultTextFormat:TextFormat = new TextFormat(TextFieldUtil.getFont(), null, 0xFFFFFF, true);
		
		protected var blueBtn:Button = new Button(40, 18, "BLUE", true, false, 0x0000FF);
		protected var redBtn:Button = new Button(40, 18, "RED", true, false, 0xFF0000);
		protected var autoJoinBtn:Button = new Button(120, 40, "AUTO JOIN", true, false, 0x999999);
		protected var optionBtn:Button = new Button(80, 20, "SETTINGS", true, false, 0x333333);
		protected var tweetBtn:Button = new Button(60, 20, "TWEET", true, false, 0x333333);
		protected var gameRoomTF:TextField = new TextField();
		protected var gameRoomStatusTF:TextField = new TextField();
		protected var normalRoomPanel:Sprite = new Sprite();
		
		protected var optionWindow:OptionWindow = new OptionWindow();
		protected var chatWindow:ChatWindow;
		
		
		protected var soloRoomPanel:Sprite = new Sprite();
		protected var soloBtn:Button = new Button(120, 20, "VS COM", true, false, 0x999999);
		protected var soloTitle:TextField = new TextField();
		protected var soloInfo:TextField = new TextField();
		
		protected var blueNum:int;
		protected var redNum:int;
		
		
		public function InfoUI() {
			this.graphics.beginFill(0x0, 0.5);
			this.graphics.drawRect( -30, -30, 465, 465);
			this.graphics.endFill();
			this.graphics.beginFill(0x888888, 0.75);
			this.graphics.lineStyle(2, 0xFFFFFF);
			this.graphics.drawRoundRect(0, 0, 405, 415, 10);
			lobbyUserNumText.defaultTextFormat = blueTeamNumText.defaultTextFormat = redTeamNumText.defaultTextFormat = defaultTextFormat;
			lobbyUserNumText.autoSize = blueTeamNumText.autoSize = redTeamNumText.autoSize = "left";
			this.addChild( lobbyUserNumText );
			
			normalRoomPanel.addChild( blueTeamNumText );
			normalRoomPanel.addChild( redTeamNumText );
			normalRoomPanel.addChild( autoJoinBtn );
			normalRoomPanel.addChild( blueBtn );
			normalRoomPanel.addChild( redBtn );
			normalRoomPanel.addChild( gameRoomTF );
			normalRoomPanel.addChild( gameRoomStatusTF );
			this.addChild( normalRoomPanel );
			
			soloRoomPanel.addChild( soloTitle );
			soloRoomPanel.addChild( soloBtn );
			soloRoomPanel.addChild( soloInfo );
			
			this.addChild( soloRoomPanel );
			this.addChild( optionBtn );
			this.addChild( tweetBtn );
			blueBtn.addEventListener(MouseEvent.CLICK, onBlueClick);
			redBtn.addEventListener(MouseEvent.CLICK, onRedClick);
			autoJoinBtn.addEventListener(MouseEvent.CLICK, autoJoinFunc);
			optionBtn.addEventListener(MouseEvent.CLICK, onOptionClick);
			tweetBtn.addEventListener(MouseEvent.CLICK, onTweet);
			soloBtn.addEventListener(MouseEvent.CLICK, onSolo);
			
			TextFieldUtil.setupNoBorder( gameRoomTF, false );
			TextFieldUtil.setupNoBorder( gameRoomStatusTF, false );
			TextFieldUtil.setupNoBorder( soloTitle, false );
			TextFieldUtil.setupNoBorder( soloInfo, false );
			
			gameRoomTF.text = "NORMAL ROOM";
			gameRoomStatusTF.y = 20;
			lobbyUserNumText.x = 10; lobbyUserNumText.y = 10;
			tweetBtn.x = 245;
			tweetBtn.y = 10;
			optionBtn.x = 315;
			optionBtn.y = 10;
			optionWindow.x = -10;
			optionWindow.y = 10;
			normalRoomPanel.x = 10;
			normalRoomPanel.y = 335;
			
			soloRoomPanel.x = 10; soloRoomPanel.y = 385;
			soloTitle.text = "SOLO ROOM";
			soloBtn.x = 120;
			soloInfo.x = 245;
			
			redTeamNumText.x = 290; redTeamNumText.y = 22;
			blueTeamNumText.x = 290; blueTeamNumText.y = 0;
			autoJoinBtn.x = 120; autoJoinBtn.y = 0;
			blueBtn.x = 245; blueBtn.y = 0;
			redBtn.x = 245; redBtn.y = 22;
			
			optionWindow.addEventListener("close", onClose);
		}
		public function updateSoloInfo(msg:String):void {
			soloInfo.text = msg;
		}
		public function updateNormalRoomStatus(msg:String):void {
			gameRoomStatusTF.text = msg;
		}
		public function onTweet(e:MouseEvent):void {
			navigateToURL(new URLRequest("http://twitter.com/home/?status=" 
				+ escapeMultiByte("Please join TypeShoot! http://wonderfl.net/code/bc91e1ddbdf13b313832b95f8eca60c3758a5228")));
		}
		public function updateHandicap():void {
			optionWindow.setHandicap();
		}
		protected function onClose(e:Event):void {
			removeChild(optionWindow);
		}
		protected function onOptionClick(e:MouseEvent):void {
			optionWindow.setLevel();
			optionWindow.setHandicap();
			optionWindow.setLanguage();
			addChild(optionWindow);
		}
		
		public function fadeOut():void {
			this.mouseEnabled = false;
			this.mouseChildren = false;
			this.addEventListener(Event.ENTER_FRAME, onFadeOutEnterFrame);
		}
		protected function onFadeOutEnterFrame(e:Event):void {
			this.alpha *= 0.9;
			if ( this.alpha < 0.1 ) {
				this.removeEventListener(Event.ENTER_FRAME, onFadeOutEnterFrame);
				this.alpha = 0;
				this.visible = false;
			}
		}
		public function setInfoMessage(msg:String):void {
			chatWindow.setInfoMessage(msg);
		}
		public function setChatRoom(lobby:Room):void {
			chatWindow = new ChatWindow(lobby);
			this.addChild( chatWindow );
			chatWindow.y = 40;
			chatWindow.x = 10;
		}
		public function display():void {
			this.mouseEnabled = true;
			this.mouseChildren = true;
			this.alpha = 1.0;
			this.visible = true;
			if ( this.stage != null ) {
				chatWindow.setFocus(this.stage);
			}
			if ( Score.lastLevel < Score.getLevel() ) {
				chatWindow.setInfoMessage("LV UP! 機体色が増えました!");
			}
			Score.lastLevel = Score.getLevel();
		}
		public function setBatteryColors(baseLevel:int, color:int, baseColor:int):void {
			LocalData.write(Constants.LD_FIELD, Constants.USER_BATTERY_COLOR, String(color));
			LocalData.write(Constants.LD_FIELD, Constants.USER_BATTERY_BASE_COLOR, String(baseColor));
			optionWindow.setColors(baseLevel, color, baseColor );
		}
		public function autoJoinFunc(e:MouseEvent):void {
			this.mouseChildren = false;
			if ( blueNum < redNum ) {
				dispatchEvent(new GameEvent(GameEvent.JOIN_BLUE));
			} else if ( blueNum > redNum ) {
				dispatchEvent(new GameEvent(GameEvent.JOIN_RED));
			} else {
				if ( Math.random() < 0.5 ) {
					dispatchEvent(new GameEvent(GameEvent.JOIN_BLUE));
				} else {
					dispatchEvent(new GameEvent(GameEvent.JOIN_RED));
				}
			}
		}
		public function onSolo(e:MouseEvent):void {
			this.mouseChildren = false;
			dispatchEvent(new GameEvent(GameEvent.JOIN_SOLO));
		}
		public function onBlueClick(e:MouseEvent):void {
//			blueBtn.selected = true;
			this.mouseChildren = false;
			dispatchEvent(new GameEvent(GameEvent.JOIN_BLUE));
		}
		public function onRedClick(e:MouseEvent):void {
//			redBtn.selected = true;
			this.mouseChildren = false;
			dispatchEvent(new GameEvent(GameEvent.JOIN_RED));
		}
		public function setLobbyUserNum(num:int ):void {
			lobbyUserNumText.text = "" + num + (num>1?" users are":" user is") + " connecting. Enjoy!";
		}
		public function setBlueTeamInfo(num:int, attack:Number, defence:Number):void {
			blueNum = num;
			blueTeamNumText.text = num + (num>1?" users":" user") + " ("+int(attack/1000)+"/"+int(defence/1000)+")";
		}
		public function setRedTeamInfo(num:int, attack:Number, defence:Number):void {
			redNum = num;
			redTeamNumText.text = num + (num>1?" users":" user") + " ("+int(attack/1000)+"/"+int(defence/1000)+")";
		}
	}
	
	// チャットウィンドウ
	class ChatWindow extends Sprite {
		protected var input:TextField = new TextField();
		protected var coming:TextField = new TextField();
		protected var users:TextField = new TextField();
		protected var lobby:Room;
		
		public function ChatWindow(lobby:Room) {
			
			this.lobby = lobby;
			users.border = input.border = coming.border = true;
			users.background = input.background = coming.background = true;
			users.borderColor = input.borderColor = coming.borderColor = 0xFFFFFF;
			users.backgroundColor = input.backgroundColor = coming.backgroundColor = 0x0;
			TextFieldUtil.setupNoBorder(users);
			TextFieldUtil.setupNoBorder(input);
			TextFieldUtil.setupNoBorder(coming);
//			users.defaultTextFormat = input.defaultTextFormat = coming.defaultTextFormat = new TextFormat(null, null, 0xFFFFFF);
			coming.width = 280;
			coming.height = 260;
			coming.wordWrap = true;
			coming.textColor = 0xFFFFFF;
			input.type = "input";
			input.width = 385;
			input.height = 20;
			input.y = 265;
			users.width = 100;
			users.height = 260;
			users.x = 285;
			
			this.addChild( coming );
			this.addChild( input );
			this.addChild( users );
			
			var defaultText:String = "";
			defaultText += getNowTimeFormat() + " *** TypeShoot へようこそ! ***\n";
			defaultText += getNowTimeFormat() + " * これはタイピングゲームです。\n";
			defaultText += getNowTimeFormat() + " * 青と赤のチームにわかれ、敵側に\n";
			defaultText += getNowTimeFormat() + " * ミサイルを撃ち込みます。先に\n";
			defaultText += getNowTimeFormat() + " * 敵のライフを0にすると勝利です。\n";
			defaultText += getNowTimeFormat() + " * \n";
			defaultText += getNowTimeFormat() + " * ATTACK MODEを選択し、ワードを\n";
			defaultText += getNowTimeFormat() + " * 打ちこむと攻撃できます。選択中の\n";
			defaultText += getNowTimeFormat() + " * ワード入力にはボーナスがあります。\n";
			defaultText += getNowTimeFormat() + " * 防御はDEFENCE MODEを選択し、\n";
			defaultText += getNowTimeFormat() + " * 敵ミサイルのワードを撃ちます。\n";
			defaultText += getNowTimeFormat() + " * \n";
			defaultText += getNowTimeFormat() + " * ゲーム中はF1を押すことで\n";
			defaultText += getNowTimeFormat() + " * チームチャットが可能です。\n";
			defaultText += getNowTimeFormat() + " * \n";
			defaultText += getNowTimeFormat() + " * それでは、お楽しみください!\n";
			coming.text = defaultText;
			
			input.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
			lobby.addEventListener(RoomEvent.CLIENT_COUNT, onClientCount);
			lobby.addEventListener(RoomEvent.ADD_CLIENT, onJoin);
			lobby.addEventListener(RoomEvent.REMOVE_CLIENT, onLeave);
			lobby.addEventListener(RoomEvent.UPDATE_CLIENT_ATTRIBUTE, onUpdateClientAttribute);
			lobby.addMessageListener("CHAT_MESSAGE", chatMessageAction);
		}
		public function setFocus(stage:Stage):void {
			stage.focus = input;
		}
		protected function onJoin(e:RoomEvent):void {
			setInfoMessage(ClientUtil.getUserName(e.getClient()) + " joined");
		}
		protected function onLeave(e:RoomEvent):void {
			setInfoMessage(ClientUtil.getUserName(e.getClient()) + " left");
		}
		protected function onUpdateClientAttribute(e:RoomEvent):void {
			switch( e.getChangedAttr().name ) {
				case "team":
						updateUserList();
						break;
				case Constants.USER_NAME:
						if( e.getChangedAttr().oldValue != null ) setInfoMessage(e.getChangedAttr().oldValue + " is now " + e.getChangedAttr().value );
						updateUserList();
						break;
				case Constants.ATTACK_POWER:
						if ( e.getClient().isSelf() ) {
							setInfoMessage("Your attack power: " + int(Number(e.getChangedAttr().oldValue)/100)/10 + " -> " + int(Number(e.getChangedAttr().value)/100)/10);
						}
						updateUserList();
						break;
				case Constants.DEFENCE_POWER:
						if ( e.getClient().isSelf() ) {
							setInfoMessage("Your defence power: " + int(Number(e.getChangedAttr().oldValue)/100)/10 + " -> " + int(Number(e.getChangedAttr().value)/100)/10);
						}
						updateUserList();
						break;
				case Constants.HANDICAP:
						updateUserList();
						break;
					break;
			}
		}
		protected function onClientCount(e:RoomEvent):void {
			
			updateUserList();
		}
		protected function updateUserList():void {
			var list:Array = lobby.getClients();
			var listStr:String = "";
			for ( var i:int = 0; i < list.length; i++ ) {
				var userStr:String = ClientUtil.getUserName(list[i]);
				var attackPower:Number = ClientUtil.getAttackPower(list[i]);
				var defencePower:Number = ClientUtil.getDefencePower(list[i]);
				var handicap:Number = ClientUtil.getHandicap(list[i]);
				var colorStr:String = "#FFFFFF";
				var powerColor:String = "#FFFFFF";
				if ( ClientUtil.isSolo( list[i] ) ) {
					colorStr = "#666666";
				} else {
					if ( ClientUtil.isTeamBlue( list[i] ) ) colorStr = "#6666FF";
					if ( ClientUtil.isTeamRed( list[i] ) ) colorStr = "#FF6666";
				}
				if ( (list[i] as IClient).isSelf() ) colorStr = "#66FF66";
				if ( handicap == 2 ) powerColor = "#FFFF66";
				if ( handicap == 3 ) powerColor = "#FF6666";
				listStr += "<font color='" + colorStr +"'>" + userStr + "</font>" +
				"(<font color='"+powerColor+"'>"+int(int(attackPower)/1000/handicap)+"</font>/<font color='"+powerColor+"'>"+int(int(defencePower)/1000/handicap)+"</font>)\n";
			}
			users.htmlText = "<font color='#FFFFFF' face='_等幅'>" + listStr +"</font>";
		}
		protected function getNowTimeFormat():String {
			var date:Date = new Date();
			var timeStr:String = 
				String("00" + date.getHours()).substr( -2, 2) + ":" + 
				String("00" + date.getMinutes()).substr( -2, 2) + ":" +
				String("00" + date.getSeconds()).substr( -2, 2);
			return timeStr;
		}
		protected function chatMessageAction(from:IClient, msg:String):void {
			if( from.isSelf() )
				coming.appendText( getNowTimeFormat() + " )" + ClientUtil.getUserName(from) + "( " + String(decodeURI(msg)) + "\n" );
			else
				coming.appendText( getNowTimeFormat() + " (" + ClientUtil.getUserName(from) + ") " + String(decodeURI(msg)) + "\n" );
			coming.scrollV = coming.maxScrollV;
		}
		public function setInfoMessage(msg:String):void {
			coming.appendText( getNowTimeFormat() + " *** " + msg + "\n" );
			coming.scrollV = coming.maxScrollV;
		}
		protected function onKeyDown(e:KeyboardEvent):void {
			if ( (e.keyCode == Keyboard.ENTER) && ( input.text != "" ) ) {
				lobby.sendMessage("CHAT_MESSAGE", true, null, String(encodeURI(input.text)));
				input.text = "";
			}
		}
	}
	
	class ClientUtil {
		public static function isCPU(client:IClient):Boolean {
			return ( client.getClientID() == "0" ) 
		}
		public static function isSolo(client:IClient):Boolean {
			return ( client.getAttribute("", "isSolo") == "true" );
		}
		public static function isTeamBlue(client:IClient):Boolean {
			return ( client.getAttribute("", "team") == Constants.TEAM_BLUE );
		}
		public static function isTeamRed(client:IClient):Boolean {
			return ( client.getAttribute("", "team") == Constants.TEAM_RED );
		}
		public static function getUserName(client:IClient):String {
			return decodeURIComponent(String(client.getAttribute("", Constants.USER_NAME)));
		}
		public static function getHandicap(client:IClient):Number {
			return Number(client.getAttribute("", Constants.HANDICAP));
		}
		public static function getAttackPower(client:IClient):Number {
			return Number(client.getAttribute("", Constants.ATTACK_POWER));
		}
		public static function getDefencePower(client:IClient):Number {
			return Number(client.getAttribute("", Constants.DEFENCE_POWER));
		}
		public static function getBaseLevel(client:IClient):int {
			return int(client.getAttribute("", Constants.USER_BATTERY_BASE_LEVEL));
		}
		public static function getColor(client:IClient):int {
			return int(client.getAttribute("", Constants.USER_BATTERY_COLOR));
		}
		public static function getBaseColor(client:IClient):int {
			return int(client.getAttribute("", Constants.USER_BATTERY_BASE_COLOR));
		}
		public static function setColor(client:IClient, color:int):void {
			client.setAttribute(Constants.USER_BATTERY_COLOR, String(color), "");
		}
		public static function setBaseColor(client:IClient, baseColor:int):void {
			client.setAttribute(Constants.USER_BATTERY_BASE_COLOR, String(baseColor), "");
		}
		public static function getStatus(client:IClient):String {
			return String(client.getAttribute("", "status") );
		}
	}
	
	// 
	class GameEvent extends Event {
//		public static const INFO_START:String = "infoStart";
//		public static const GAME_START:String = "gameStart";
//		public static const LOBBY_USER_NUM_UPDATE:String = "lobbyUserNumUpdate";
//		public static const GAME_USER_NUM_UPDATE:String = "gameUserNumUpdate";
		public static const JOIN_BLUE:String = "joinBlue";
		public static const JOIN_RED:String = "joinRed";
		public static const JOIN_SOLO:String = "joinSolo";
		public static const AUTO_JOIN:String = "autoJoin";
		public static const MOVE_BATTERY:String = "moveBattery";
		public static const MODE_ATTACK:String = "modeAttack";
		public static const MODE_DEFENCE:String = "modeDefender";
		public static const HIT:String = "hit";
		public static const ATTACK:String = "attack";
		public static const DEFENCE_TYPE:String = "defenceType";
		public static const DESTROY:String = "destroy";
		public static const SHOOT:String = "shoot";
		public static const MISSILE_DESTROY:String = "missileDestroy";
		public static const MISSILE_DAMAGE:String = "missileDamage";
		public static const CHAT:String = "chat";
		public static const END_GAME:String = "endGame";
		public static const COLOR_SELECT:String = "colorSelect";
		public static const HANDICAP_SELECT:String = "handicapSelect";
		public static const ATTACK_REFLECT:String = "attackReflect";
		public static const ATTACK_DOUBLE:String = "attackDouble";
		public static const NAME_CHANGE:String = "nameChange";
		public static const ATTACK_COMBO_END:String = "attackComboEnd";
		public static const ATTACK_COMBO_START:String = "attackComboStart";
		public static const DEFENCE_COMBO_END:String = "defenceComboEnd";
		public static const DEFENCE_COMBO_START:String = "defenceComboStart";
		
		public var data:String;
		
		public function GameEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false, data:String=null) {
			super(type, bubbles, cancelable);
			this.data = data;
		}
		override public function clone():Event 
		{
			return new GameEvent(type, bubbles, cancelable, data);
		}
	}
	
	// 入室時のインターフェース
	class EnterUI extends Sprite {
		protected var tf:TextField = new TextField();
		protected var btn:Button = new Button(100, 20, "ENTER", false);
		public function EnterUI () {
			draw();
		}
		protected function draw():void {
			tf.width = 100;
			tf.height = 18;
			TextFieldUtil.setupBorder(tf);
			tf.type = "input";
			btn.y = 30;
			var userNameObj:Object = LocalData.read(Constants.LD_FIELD, Constants.USER_NAME);
			if ( userNameObj == null ) {
				tf.text = "Input your name";
				tf.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
			} else {
				tf.text = String(userNameObj);
				enterEnable();
			}
			tf.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
			this.addChild( tf );
			this.addChild( btn );
			btn.addEventListener(MouseEvent.CLICK, onClick);
		}
		protected function onClick(e:MouseEvent = null):void {
			LocalData.write(Constants.LD_FIELD, Constants.USER_NAME, tf.text);
			LocalData.flush(Constants.LD_FIELD);
			btn.selected = true;
			this.dispatchEvent(new Event("enter"))
		}
		protected function onMouseDown(e:MouseEvent):void {
			tf.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
			tf.text = "";
		}
		protected function onKeyUp(e:KeyboardEvent):void {
			if ( tf.text.length > 0 ) {
				enterEnable();
				if ( e.keyCode == Keyboard.ENTER ) {
					onClick(); // ENTER押したのと同じにする
				}
			} else {
				enterDisable();
			}
		}
		protected function enterEnable():void {
			btn.enable = true;
		}
		protected function enterDisable():void {
			btn.enable = false;
		}
	}
	
	// テキストとグラフィックで作るボタン
	class Button extends Sprite {
		protected var bgBlack:Sprite = new Sprite();
		protected var bgGray:Sprite = new Sprite();
		protected var highlight:Sprite = new Sprite();
		protected var tf:TextField = new TextField();
		public function Button(width:int, height:int, text:String, isEnabled:Boolean = true, isSelected:Boolean = false, bgColor:int = 0x0) {
			bgBlack.graphics.lineStyle(0, 0xFFFFFF);
			bgBlack.graphics.beginFill(bgColor, 0.75);
			bgBlack.graphics.drawRoundRect(0, 0, width, height, 5);
			bgGray.graphics.beginFill(0xFFFFFF, 0.5);
			bgGray.graphics.drawRoundRect(0, 0, width, height, 5);
			highlight.graphics.beginFill(0x88FF88, 0.5);
			highlight.graphics.drawRoundRect(0, 0, width, height, 5);
			this.addChild( bgBlack );
			this.addChild( bgGray );
			this.addChild( highlight );
			highlight.mouseEnabled = highlight.mouseChildren = false;
			this.selected = isSelected;
			this.addEventListener(MouseEvent.ROLL_OVER, function():void { bgGray.alpha = 1.0 } );
			this.addEventListener(MouseEvent.ROLL_OUT, function():void { bgGray.alpha = 0 } );
			bgGray.alpha = 0;
			tf.autoSize = "left";
			TextFieldUtil.setupNoBorder(tf);
			tf.text = text;
			tf.x = 0.5 * (width - tf.width);
			tf.y = 0.5 * (height - tf.height);
			this.addChild( tf );
			this.mouseChildren = false;
			this.enable = isEnabled;
			this.addEventListener(MouseEvent.CLICK, onClick);
		}
		protected function onClick(e:MouseEvent):void {
			SoundManager.ClickSound.play();
		}
		public function get selected():Boolean {
			return highlight.visible;
		}
		public function set selected(value:Boolean):void {
			highlight.visible = value;
		}
		public function set enable(value:Boolean):void {
			this.mouseEnabled = value;
//			this.buttonMode = value;
			this.alpha = (value?1.0:0.5);
		}
	}
	
	// 砲台移動用ユーザーインターフェース
	class MoveBatteryUI extends Sprite {
		private var batteryShadow:Battery = new Battery(true, 0,0,0);
		public function MoveBatteryUI() {
			batteryShadow.filters =[ new ColorMatrixFilter([0, 0, 0, 0.4, 0, 0, 0, 0, 0.4, 0, 0, 0, 0, 0.4, 0, 0, 0, 0, 1.0, 0])];
			this.addChild( batteryShadow );
			batteryShadow.y = Constants.BATTERY_DEFAULT_Y;
			batteryShadow.visible = false;
			this.graphics.beginFill(0x0, 0);
			this.graphics.drawRect(0, 0, 480, 40);
			
			this.addEventListener(MouseEvent.ROLL_OVER, onRollOver);
			this.addEventListener(MouseEvent.ROLL_OUT, onRollOut);
			this.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
		}
		protected function onRollOver(e:MouseEvent):void {
			batteryShadow.visible = true;
		}
		protected function onRollOut(e:MouseEvent):void {
			batteryShadow.visible = false;
		}
		protected function onMouseMove(e:MouseEvent):void {
			batteryShadow.x = this.mouseX;
		}
		// 砲台シャドウの形態変更
		public function setMode(playMode:String):void {
			batteryShadow.setMode(playMode);
		}
	}
	
	// ゲーム設定値とか定数とかとにかく放り込んだ
	class Constants {
		public static const GAME_START_WAITTIME:Number = 10000;
		public static const GAME_END_NEXT_WAITTIME:Number = 20000;
		public static const ANGLE:String = "angle";
		public static const LD_FIELD:String = "wonderflGame1";
		public static const USER_NAME:String = "userName";
		public static const STATUS_CHATING:String = "statusChating"; // lobby
		public static const STATUS_WAITING:String = "statusWaiting"; // 入室まち
		public static const STATUS_PLAYING:String = "statusPlaying"; // プレイ中
		public static const STATUS_FINISHING:String = "statusFinishing"; // プレイ中
		public static const CPU_ROOM_DEFAULT:String = "wonderfl.keno.missileGame.game.cpu";
		public static const GAME_ROOM:String = "wonderfl.keno.missileGame.game.room2";
		public static const LOBBY_ROOM:String = "wonderfl.keno.missileGame.lobby2";
		public static const TEAM_NONE:String = "teamNone";
		public static const TEAM_BLUE:String = "teamBlue";
		public static const TEAM_RED:String = "teamRed";
		public static const BATTERY_DEFAULT_Y:Number = 12;
		public static const BATTERY_X:String = "batteryX";
		public static const PLAY_MODE:String = "playMode";
		public static const PLAY_MODE_ATTACKER:String = "playModeAttacker";
		public static const PLAY_MODE_DEFENDER:String = "playModeDefender";
		public static const USER_BATTERY_BASE_LEVEL:String = "userBatteryBaseLevel";
		public static const USER_BATTERY_COLOR:String = "UserBatteryColor";
		public static const USER_BATTERY_BASE_COLOR:String = "UserBatteryBaseColor";
		public static const USER_BATTERY_EFFECT_LEVEL:String = "UserBatteryEffectLevel";
		public static const DEFAULT_USER_BATTERY_BASE_LEVEL:int = 0;
		public static const DEFAULT_USER_BATTERY_COLOR:int = 0x888888
		public static const DEFAULT_USER_BATTERY_BASE_COLOR:int = 0x888888;
		public static const ATTACK:String = "attack";
		public static const ATTACK_LOG:String = "attackLog";
		public static const ATTACK_REFLECT:String = "attackReflect";
		public static const REMOVE_MISSILE:String = "attackLog";
		public static const RESULT_SUCCESS:int = 1;
		public static const NO_VALUE:int = -1;
		public static const RESULT_FAULT:int = 0;
		public static const RESULT_DESTROY:int = 3;
		public static const SEND_ME_LOG:String = "sendMeLog";
		public static const SHOOT:String = "shoot";
		public static const DESTROY:String = "destroy";
		public static const HIT:String = "hit";
		public static const TEAM_CHAT_DURATION:Number = 5000; // 5000 msec
		public static const GAME_CHAT:String = "gameChat";
		public static const VICTORY_POINT:Number = 10;
		public static const MISSILE_ARRIVE_TIME:Number = 8000; // 8000 msec
		public static const BULLET_SPEED:Number = 170 / 1000; // 170pixel per 1000msec
		public static const DISTANCE:Number = 300; // 300pixel
		public static const BASE_LEVEL_0:int = 0; // 円
		public static const BASE_LEVEL_1:int = 1; // 三角
		public static const BASE_LEVEL_2:int = 2; // A字
		public static const BASE_LEVEL_3:int = 3; // スター
		public static const CHAT_KEY:uint = Keyboard.F1;
		public static const ATTACK_POWER:String = "attackPower";
		public static const DEFENCE_POWER:String = "defencePower";
		public static const HANDICAP:String = "handicap";
		public static const ATTACK_COMBO_START:int = 15;
		public static const DEFENCE_COMBO_START:int = 15;
	}
	
	// テキストフィールドでよく使う設定
	class TextFieldUtil {
		public static function getFont():String {
//			return "_sans";
			return "_等幅";
//			return "MS ゴシック";
		}
		public static function setupBorder(tf:TextField):void {
			tf.defaultTextFormat = new TextFormat(getFont(), null, 0xFFFFFF);
			tf.border = true;
			tf.borderColor = 0xFFFFFF;
			tf.background = true;
			tf.backgroundColor = 0x0;
		}
		public static function setupNoBorder(tf:TextField, selectable:Boolean = true):void {
			tf.defaultTextFormat = new TextFormat(getFont(), null, 0xFFFFFF);
			tf.selectable = selectable;
		}
	}
	
	// 自分用データ(共有しない)
	class Score {
		public static var type:Number = 0; // SO保存
		public static var correct:Number = 0; // SO保存
		public static var attack:Number = 0; // SO保存
		public static var defence:Number = 0; // SO保存
		public static var hit:Number = 0; // SO保存
		public static var damage:Number = 0; // SO保存
		public static var win:Number = 0; // SO保存
		public static var lose:Number = 0; // SO保存
		public static var score:Number = 0;
		public static var defencePower:Number = 0; // SO保存
		public static var attackPower:Number = 0; // SO保存
		public static var handicap:Number = 1;
		public static var attackCombo:Number = 0;
		public static var defenceCombo:Number = 0;
		public static var japaneseRate:Number = 0.5; // SO保存
		public static var lastLevel:int = 0;
		public static var playMode:String = null;
		public static var myGameRoom:String = "";
		public static var isSolo:Boolean = false;
		public static var cpuLV:Number = -1;
		protected static var expTable:Array = [0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 12000, 14000, 16000, 18000, 20000, 25000, 30000, 35000, 40000, 45000];
		public static function initScore():void {
			type = Number(LocalData.read(Constants.LD_FIELD, "totalType"));
			correct = Number(LocalData.read(Constants.LD_FIELD, "totalCorrect"));
			attack = Number(LocalData.read(Constants.LD_FIELD, "attack"));
			defence = Number(LocalData.read(Constants.LD_FIELD, "defence"));
			hit = Number(LocalData.read(Constants.LD_FIELD, "hit"));
			damage = Number(LocalData.read(Constants.LD_FIELD, "damage"));
			win = Number(LocalData.read(Constants.LD_FIELD, "win"));
			lose = Number(LocalData.read(Constants.LD_FIELD, "lose"));
			attackPower = Number(LocalData.read(Constants.LD_FIELD, "attackPower"));
			defencePower = Number(LocalData.read(Constants.LD_FIELD, "defencePower"));
//			handicap = Number(LocalData.read(Constants.LD_FIELD, "handicap"));
//			if ( handicap == 0 ) handicap = 1;
			japaneseRate = Number(LocalData.read(Constants.LD_FIELD, "japaneseRate"));
			if ( japaneseRate == 0 ) japaneseRate = 0;
			if ( cpuLV == -1 ) cpuLV = Math.max( int(attackPower/1000), int(defencePower/1000) );
		}
		public static function save():void {
			LocalData.write(Constants.LD_FIELD, "totalType", type);
			LocalData.write(Constants.LD_FIELD, "totalCorrect", correct);
			LocalData.write(Constants.LD_FIELD, "attack", attack);
			LocalData.write(Constants.LD_FIELD, "defence", defence);
			LocalData.write(Constants.LD_FIELD, "hit", hit);
			LocalData.write(Constants.LD_FIELD, "damage", damage);
			LocalData.write(Constants.LD_FIELD, "win", win);
			LocalData.write(Constants.LD_FIELD, "lose", lose);
			LocalData.write(Constants.LD_FIELD, "defencePower", defencePower);
			LocalData.write(Constants.LD_FIELD, "attackPower", attackPower);
//			LocalData.write(Constants.LD_FIELD, "handicap", handicap);
			LocalData.write(Constants.LD_FIELD, "japaneseRate", japaneseRate);
		}
		public static function resetCombo():void {
			attackCombo = defenceCombo = 0;
		}
		public static function getLevel():int {
			var exp:Number = win * 500 + lose * 100 + correct * 5 + hit * 100;
			for ( var i:int = 0; i < 20; i++ ) {
				if ( exp < expTable[i] ) {
					break;
				}
			}
			return i;
		}
		public static function getAttackBonusRate():Number {
			var rate:Number = (attackCombo - Constants.ATTACK_COMBO_START) * 0.5 / handicap;
			if ( rate > 80 ) rate = 80;
			if ( rate < 0 ) rate = 0;
			return int(rate);
		}
		public static function getDefenceBonusRate():Number {
			var rate:Number = (defenceCombo - Constants.DEFENCE_COMBO_START) * 0.5 / handicap;
			if ( rate > 80 ) rate = 80;
			if ( rate < 0 ) rate = 0;
			return int(rate);
		}
		public static function isJapanese():Boolean {
			return ( japaneseRate > Math.random() );
		}
	}
	
	// 時間の同期クラス
	class ServerClockUtil {
		public static var timeDiff:Number = 0;
		public static function init(server:Server):void {
			server.addEventListener(ServerEvent.TIME_SYNC, function():void {
				timeDiff = server.getServerTime() - Number(getTimer());
			});
			server.syncTime();
//			timeDiff = serverTime - Number(getTimer());
		}
		public static function getTime():Number {
			return Number(getTimer()) + timeDiff;
		}
	}
	
	// ローマ字変換クラス てきとうですが
	class Roma {
		public static function isJapaneseWord(word:String):Boolean {
			return ( word.charCodeAt(0) > 127 );
		}
		public static function getHiragana(roma:String):String {
			roma = roma.toUpperCase();
			var index:int = 0;
			var ret:String = "";
			while ( index < roma.length ) {
				var char:String = roma.charAt(index);
				if ( roma.length > (index + 1) ) {
					if ( (char != "A") && (char != "I") && (char != "U") && (char != "E") && (char != "O") &&
					(char != "N") && (char != "-") && (char == roma.charAt(index + 1)) ) {
						ret += "っ";
						index++;
						continue;
					}
				}
				for ( var i:int = 3; i > 0; i-- ) {
					var test:String = roma.substr(index, i);
					var kana:String = hiraganaTable[test];
					if ( kana ) {
						ret += kana;
						index += test.length;
						break;
					}
				}
				if ( i == 0 ) {
					ret += roma.substr(index);
					break;
				}
			}
			return ret;
		}
		public static function init():void {
			hiraganaTable = makeReverseTable( romaTable );
		}
		
		protected static var hiraganaTable:Object;
		
		protected static function makeReverseTable(table:Object):Object {
			var ret:Object = { };
			for ( var str:String in table ) {
				if ( table[str] is Array ) {
					for each( var roma:String in table[str] ) {
						ret[roma] = str;
					}
				} else {
					var temp:Object = makeReverseTable( table[str] );
					for ( var str2:String in temp ) {
						ret[str2] = str + temp[str2];
					}
				}
			}
			return ret;
		}
		public static function getRomaToType(hiragana:String, typed:String = ""):String {
			var default_ltu_alone:Boolean = false;
			var index:int = 0;
			var ret:String = "";
			var char:String = "";
			var addChar:String = "";
			var nextChar:String = "";
			var nn:Boolean = false;
			var hatsuon:Boolean = false;
			
			var typedIndex:int = 0;
			var typedLength:int = typed.length;
			var targetChar:String;
			var wordLength:int;
			var isOk:Boolean;
			var i:int;
			var j:int;
			var test:String = "";
			
			var oldTypedIndex:int = 0;
			
			var targetArray:Array;
			var nextTargetArray:Array;
			var addIndex:int = 1;
			
			while ( index < hiragana.length) {
				char = hiragana.charAt(index);
				if ( char == "ん" && (index + 1 < hiragana.length ) ) {
					nn = true;
					index++;
					continue;
				}
				if ( char == "っ" ) {
					//
					if ( (typedIndex < typedLength) && 
					( typed.charAt(typedIndex) == "L" || typed.charAt(typedIndex) == "X" ) ) { // 単独文字として扱う
						default_ltu_alone = true; // 実装未定・・
						hatsuon = false;
					} else 
					//
					{
						hatsuon = true; // 判断先送り
						index++;
						continue;
					}
				}
				
				addIndex = 1;
				if ( (romaTable[char] is Array) ) { // 一文字決まり
					targetArray = romaTable[char];
				} else {
					if( ((index + 1) < hiragana.length) ){ // 二文字目あり
						nextChar = hiragana.charAt(index + 1);
					} else { // 二文字目なし
						nextChar = "";
					}
					if ( romaTable[char][nextChar] == null ) { // 二文字目はあるけど候補なし
						targetArray = romaTable[char][""];
					} else { // 二文字目まで候補あり
						targetArray = romaTable[char][nextChar];
						nextTargetArray = romaTable[char][""];
						addIndex = 2;
					}
				}
				//
				if ( typedIndex < typedLength ) {
					if ( nn ) {
						if ( typed.charAt(typedIndex) != "N" ) return null;
						typedIndex++;
						if ( typedIndex < typedLength ) {
							if ( typed.charAt(typedIndex) == "N" ) {
								typedIndex++;
							} else {
								switch( typed.charAt(typedIndex) ) {
									case "A":
									case "I":
									case "U":
									case "E":
									case "O":
									case "Y":
										return null;
										break;
									default:
									break;
								}
							}
						}
					}
					if ( hatsuon ) {
						test = getRomaToType(hiragana.substr(index, 2), typed.substr(typedIndex, 1));
						if ( !test ) return null;
						typedIndex++;
						if ( (typedIndex < typedLength) && (typed.charAt(typedIndex) != test.charAt(0)) ) return null;
					}
					for ( i = 0; i < targetArray.length; i++ ) {
						targetChar = targetArray[i];
						wordLength = targetChar.length;
						isOk = true;
						for ( j = typedIndex; j < typedIndex + wordLength; j++ ) {
							if ( j == typedLength ) break;
							if ( typed.charAt(j) != targetChar.charAt(j - typedIndex) ) {
								isOk = false;
								break;
							}
						}
						if ( isOk ) break;
					}
					if ( !isOk && (addIndex == 2) ) { // 二文字あるけど一文字ずつ区切って打とうとしている場合
						addIndex = 1;
						targetArray = nextTargetArray;
						for ( i = 0; i < targetArray.length; i++ ) {
							targetChar = targetArray[i];
							wordLength = targetChar.length;
							isOk = true;
							for ( j = typedIndex; j < typedIndex + wordLength; j++ ) {
								if ( j == typedLength ) break;
								if ( typed.charAt(j) != targetChar.charAt(j - typedIndex) ) {
									isOk = false;
									break;
								}
							}
							if ( isOk ) break;
						}
					}
					if ( !isOk ) return null;
					typedIndex += j - typedIndex;
					if( i > 0 ){
						targetArray.splice(i, 1);
						targetArray.unshift(targetChar);
					}
				}
				//
				addChar = targetArray[0];
				index += addIndex;
					
				if ( hatsuon ) {
					ret += addChar.substr(0, 1);
					hatsuon = false;
				}
				if ( nn ) {
					switch( addChar.substr(0, 1) ) {
						case "A":
						case "I":
						case "U":
						case "E":
						case "O":
						case "N":
						case "Y":
							ret += "NN";
							break;
						default:
							ret += "N";
							break;
					}
					nn = false;
				}
				ret += addChar;
				addChar = "";
			}
			if ( nn ) {
				ret += "NN";
			}
			return ret;
		}
		protected static const romaTable:Object = {
			"あ": ["A"],
			"い": {
				"ぇ": ["YE"],
				"": ["I", "YI"]
			},
			"う": {
				"ぃ": ["WI"],
				"ぇ": ["WE"],
				"": ["U", "WU"]
			},
			"え": ["E"],
			"お": ["O"],
			"か": ["KA", "CA"],
			"き": {
				"ゃ": ["KYA"],
				"ぃ": ["KYI"],
				"ゅ": ["KYU"],
				"ぇ": ["KYE"],
				"ょ": ["KYO"],
				"": ["KI"]
			},
			"く": {
				"ぁ": ["KWA"],
				"": ["KU", "CU"]
			},
			"け": ["KE"],
			"こ": ["KO", "CO"],
			"が": ["GA"],
			"ぎ": {
				"ゃ": ["GYA"],
				"ぃ": ["GYI"],
				"ゅ": ["GYU"],
				"ぇ": ["GYE"],
				"ょ": ["GYO"],
				"": ["GI"]
			},
			"ぐ": {
				"ぁ": ["GWA"],
				"": ["GU"]
			},
			"げ": ["GE"],
			"ご": ["GO"],
			"さ": ["SA"],
			"し": {
				"ゃ": ["SYA", "SHA"],
				"ぃ": ["SYI"],
				"ゅ": ["SYU", "SHU"],
				"ぇ": ["SYE", "SHE"],
				"ょ": ["SYO", "SHO"],
				"": ["SI", "SHI", "CI"]
			},
			"す": {
				"ぁ": ["SWA"],
				"ぃ": ["SWI"],
				"ぅ": ["SWU"],
				"ぇ": ["SWE"],
				"ぉ": ["SWO"],
				"": ["SU"]
			},
			"せ": ["SE", "CE"],
			"そ": ["SO"],
			"ざ": ["ZA"],
			"じ": {
				"ゃ": ["ZYA", "JYA", "JA"],
				"ぃ": ["ZYI", "JYI"],
				"ゅ": ["ZYU", "JYU", "JU"],
				"ぇ": ["ZYE", "JYE"],
				"ょ": ["ZYO", "JYO", "JO"],
				"": ["ZI", "JI"]
			},
			"ず": ["ZU"],
			"ぜ": ["ZE"],
			"ぞ": ["ZO"],
			"た": ["TA"],
			"ち": {
				"ゃ": ["TYA","CHA", "CYA"],
				"ぃ": ["TYI","CYI"],
				"ゅ": ["TYU","CHU", "CYU"],
				"ぇ": ["TYE", "CHE", "CYE"],
				"ょ": ["TYO", "CHO", "CYO"],
				"": ["TI", "CHI"]
			},
			"つ": {
				"ぁ": ["TSA"],
				"ぃ": ["TSI"],
				"ぅ": ["TSU"],
				"ぇ": ["TSE"],
				"ぉ": ["TSO"],
				"": ["TU", "TSU"]
			},
			"て": {
				"ゃ": ["THA"],
				"ぃ": ["THI"],
				"ゅ": ["THU"],
				"ぇ": ["THE"],
				"ょ": ["THO"],
				"": ["TE"]
			},
			"と": {
				"ぁ": ["TWA"],
				"ぃ": ["TWI"],
				"ぅ": ["TWU"],
				"ぇ": ["TWE"],
				"ぉ": ["TWO"],
				"": ["TO"]
			},
			"だ": ["DA"],
			"ぢ": {
				"ゃ": ["DYA"],
				"ぃ": ["DYI"],
				"ゅ": ["DYU"],
				"ぇ": ["DYE"],
				"ょ": ["DYO"],
				"": ["DI"]
			},
			"づ": ["DU"],
			"で": {
				"ゃ": ["DHA"],
				"ぃ": ["DHI"],
				"ゅ": ["DHU"],
				"ぇ": ["DHE"],
				"ょ": ["DHO"],
				"": ["DE"]
			},
			"ど": {
				"ぁ": ["DWA"],
				"ぃ": ["DWI"],
				"ぅ": ["DWU"],
				"ぇ": ["DWE"],
				"ぉ": ["DWO"],
				"": ["DO"]
			},
			"な": ["NA"],
			"に": {
				"ゃ": ["NYA"],
				"ぃ": ["NYI"],
				"ゅ": ["NYU"],
				"ぇ": ["NYE"],
				"ょ": ["NYO"],
				"": ["NI"]
			},
			"ぬ": ["NU"],
			"ね": ["NE"],
			"の": ["NO"],
			"は": ["HA"],
			"ひ": {
				"ゃ": ["HYA"],
				"ぃ": ["HYI"],
				"ゅ": ["HYU"],
				"ぇ": ["HYE"],
				"ょ": ["HYO"],
				"": ["HI"]
			},
			"ふ": {
				"ぁ": ["FA"],
				"ぃ": ["FI"],
				"ぇ": ["FE"],
				"ぉ": ["FO"],
				"": ["FU", "HU"]
			},
			"へ": ["HE"],
			"ほ": ["HO"],
			"ば": ["BA"],
			"び": {
				"ゃ": ["BYA"],
				"ぃ": ["BYI"],
				"ゅ": ["BYU"],
				"ぇ": ["BYE"],
				"ょ": ["BYO"],
				"": ["BI"]
			},
			"ぶ": ["BU"],
			"べ": ["BE"],
			"ぼ": ["BO"],
			"ぱ": ["PA"],
			"ぴ": {
				"ゃ": ["PYA"],
				"ぃ": ["PYI"],
				"ゅ": ["PYU"],
				"ぇ": ["PYE"],
				"ょ": ["PYO"],
				"": ["PI"]
			},
			"ぷ": ["PU"],
			"ぺ": ["PE"],
			"ぽ": ["PO"],
			"ま": ["MA"],
			"み": {
				"ゃ": ["MYA"],
				"ぃ": ["MYI"],
				"ゅ": ["MYU"],
				"ぇ": ["MYE"],
				"ょ": ["MYO"],
				"": ["MI"]
			},
			"む": ["MU"],
			"め": ["ME"],
			"も": ["MO"],
			"や": ["YA"],
			"ゆ": ["YU"],
			"よ": ["YO"],
			"ら": ["RA"],
			"り": {
				"ゃ": ["RYA"],
				"ぃ": ["RYI"],
				"ゅ": ["RYU"],
				"ぇ": ["RYE"],
				"ょ": ["RYO"],
				"": ["RI"]
			},
			"る": ["RU"],
			"れ": ["RE"],
			"ろ": ["RO"],
			"わ": ["WA"],
			"を": ["WO"],
			"ゎ": ["LWA", "XWA"],
			"ぁ": ["LA", "XA"],
			"ぃ": ["LI", "XI"],
			"ぅ": ["LU", "XU"],
			"ぇ": ["LE", "XE"],
			"ぉ": ["LO", "XO"],
			"ゃ": ["LYA", "XYA"],
			"ゅ": ["LYU", "XYU"],
			"ょ": ["LYO", "XYO"],
			"っ": ["LTU", "XTU", "LTSU"],
			"ん": ["N", "NN"],
			"ー": ["-"],
			" ": [" "]
			// ん, っ については別ルール適用
		}
	}
	
	// 単語管理クラス
	class Words {
		public static const jKanjiList:Array = [
			"会う", "青い", "赤い", "明い", "秋", "開く", "開ける",
			"上げる", "朝", "朝御飯", "あさって", "足", "あした", "あそこ",
			"遊ぶ", "暖かい", "頭", "新しい", "あちら", "暑い", "熱い",
			"厚い", "後", "あなた", "兄", "姉", "あの", "アパート", "あびる",
			"危ない", "甘い", "あまり", "雨", "洗う", "ある", "歩く", "あれ",
			"いい", "よい", "いいえ", "言う", "家", "行く", "行く", "いくつ",
			"いくら", "池", "医者", "いす", "忙しい", "痛い", "一",
			"一日", "いちばん", "いつ", "五日", "一緒", "五つ", "いつも",
			"今", "意味", "妹", "嫌", "入口", "居る", "要る", "入れる",
			"色", "いろいろ", "上", "後", "薄い", "歌", "歌う", "うち",
			"生まれる", "海", "売る", "上着", "映画", "映画館", "英語",
			"ええ", "駅", "エレベーター", "鉛筆", "おいしい", "大きい",
			"大勢", "お母さん", "お菓子", "お金", "起きる", "置く", "奥さん",
			"お酒", "お皿", "伯父", "叔父", "おじいさん", "教える", "押す",
			"遅い", "お茶", "お手洗い", "お父さん", "弟", "男",
			"男の子", "おととい", "おととし", "大人", "おなか", "同じ",
			"お兄さん", "お姉さん", "伯母", "叔母", "おばあさん", "お弁当",
			"覚える", "重い", "おもしろい", "泳ぐ", "降りる", "終る",
			"音楽", "女", "女の子", "外国", "外国人", "会社",
			"階段", "買い物", "買う", "返す", "帰る", "顔", "かぎ",
			"書く", "学生", "かける", "傘", "貸す", "風", "家族",
			"片仮名", "学校", "角", "家内", "かばん", "花瓶", "かぶる",
			"紙", "カメラ", "火曜日", "辛い", "体", "借りる", "軽い",
			"カレンダー", "川", "河", "かわいい", "漢字", "黄色い", "消える",
			"聞く", "北", "ギター", "汚い", "喫茶店", "切手", "切符",
			"昨日", "九", "牛肉", "今日", "教室", "兄弟", "去年",
			"嫌い", "切る", "着る", "きれい", "キロ", "銀行", "金曜日",
			"薬", "ください", "果物", "口", "靴", "靴下", "国", "曇る",
			"暗い", "くらい", "ぐらい", "クラス", "グラム", "来る", "車",
			"黒い", "今朝", "消す", "結構", "結婚", "月曜日",
			"玄関", "元気", "五", "公園", "交番", "声", "コート",
			"ここ", "午後", "九日", "九つ", "御主人", "午前", "答える",
			"こちら", "コップ", "今年", "言葉", "子供", "この", "御飯",
			"困る", "これ", "今月", "今週", "こんな", "今晩", "さあ",
			"魚", "先", "咲く", "作文", "さす", "雑誌", "砂糖",
			"寒い", "再来年", "三", "四", "散歩", "塩", "しかし",
			"時間", "仕事", "辞書", "静か", "下", "七", "質問", "自転車",
			"自動車", "死ぬ", "字引", "自分", "閉まる", "閉める", "締める",
			"じゃあ", "写真", "シャツ", "十", "授業", "宿題", "上手",
			"丈夫", "しょうゆ", "食堂", "知る", "白い", "新聞", "水曜日",
			"吸う", "スカート", "好き", "すぐに", "少し", "涼しい", "ストーブ",
			"スプーン", "スポーツ", "ズボン", "住む", "スリッパ", "する", "座る",
			"背", "生徒", "せっけん", "セーター", "背広", "狭い", "ゼロ",
			"千", "先月", "先週", "先生", "洗濯", "全部", "掃除",
			"そうして", "そして", "そこ", "そちら", "外", "その", "そば", "空",
			"それ", "それから", "それでは", "大学", "大使館", "大丈夫",
			"大好き", "大切", "たいてい", "台所", "たいへん", "高い",
			"たくさん", "タクシー", "出す", "立つ", "建物", "楽しい", "頼む",
			"たばこ", "たぶん", "食べ物", "食べる", "卵", "だれ", "誕生日",
			"だんだん", "小さい", "近い", "違う", "近く", "地下鉄",
			"地図", "父", "茶色", "ちゃわん", "ちょうど", "ちょっと", "一日",
			"使う", "疲れる", "次", "着く", "机", "作る", "つける", "勤める",
			"つまらない", "冷たい", "強い", "手", "テープ", "テーブル", "出かける",
			"手紙", "できる", "出口", "テスト", "では", "デパート", "でも",
			"出る", "テレビ", "天気", "電気", "電車", "電話", "戸", "ドア",
			"トイレ", "どうして", "どうぞ", "動物", "どうも", "十", "遠い", "十日",
			"時々", "時計", "どこ", "所", "図書館", "どちら", "とても", "どなた",
			"隣", "どの", "飛ぶ", "止まる", "友達", "土曜日", "鳥", "鳥肉",
			"取る", "撮る", "どれ", "どんな", "ない", "ナイフ", "中", "長い",
			"鳴く", "夏", "夏休み", "七つ", "何", "七日", "名前", "習う",
			"並ぶ", "並べる", "二", "にぎやか", "肉", "西", "日曜日",
			"荷物", "ニュース", "庭", "脱ぐ", "ネクタイ", "寝る", "ノート", "登る",
			"飲み物", "飲む", "乗る", "歯", "パーティー", "はい", "灰皿",
			"入る", "葉書", "はく", "箱", "橋", "はし", "始まる", "初めに",
			"初めて", "走る", "バス", "バター", "二十歳", "働く", "八", "二十日",
			"花", "鼻", "話", "話す", "母", "早い", "速い", "春",
			"はる", "晴れる", "半", "晩", "パン", "ハンカチ", "番号",
			"晩御飯", "半分", "東", "引く", "弾く", "低い", "飛行機",
			"左", "人", "一つ", "一月", "一人", "暇", "百", "病院",
			"病気", "平仮名", "昼", "昼御飯", "広い", "フィルム", "封筒",
			"プール", "フォーク", "吹く", "服", "二つ", "豚肉", "二人", "二日",
			"太い", "冬", "降る", "古い", "ふろ", "パージ", "下手", "ベッド",
			"部屋", "辺", "ペン", "勉強", "便利", "ほう", "帽子", "ボールペン",
			"外", "ポケット", "欲しい", "細い", "ボタン", "ホテル", "本",
			"本棚", "ほんとうに", "毎朝", "毎月", "毎月", "毎週", "毎日",
			"毎年", "毎年", "毎晩", "前", "曲る", "まずい", "また", "まだ",
			"町", "待つ", "まっすぐ", "マッチ", "窓", "丸い", "円い", "万",
			"万年筆", "磨く", "右", "短い", "水", "店", "見せる", "道",
			"三日", "三つ", "皆さん", "南", "耳", "見る", "みんな", "六日",
			"向こう", "難しい", "六つ", "目", "メートル", "眼鏡", "もう",
			"木曜日", "もしもし", "もちろん", "持つ", "もっと", "物", "門",
			"問題", "八百屋", "野菜", "易しい", "安い", "休み", "休む",
			"八つ", "山", "やる", "夕方", "郵便局", "ゆうべ", "有名",
			"雪", "ゆっくり", "八日", "洋服", "よく", "横", "四日", "四つ",
			"呼ぶ", "読む", "夜", "来月", "来週", "来年", "ラジオ",
			"りっぱ", "留学生", "両親", "料理", "旅行", "零", "冷蔵庫",
			"レコード", "レストラン", "練習", "六", "ワイシャツ", "若い",
			"分る","忘れる","私","わたし","渡す","渡る","悪い"
		];
		public static const jList:Array = [
			"あう","あおい","あかい","あかるい","あき","あく","あける", // JLPT lv 4
			"あげる","あさ","あさごはん","あさって","あし","あした","あそこ",
			"あそぶ","あたたかい","あたま","あたらしい","あちら","あつい","あつい",
			"あつい","あと","あなた","あに","あね","あの","あぱーと","あびる",
			"あぶない","あまい","あまり","あめ","あらう","ある","あるく","あれ",
			"いい","よい","いいえ","いう","いえ","いく","ゆく","いくつ",
			"いくら","いけ","いしゃ","いす","いそがしい","いたい","いち",
			"いちにち","いちばん","いつ","いつか","いっしょ","いつつ","いつも",
			"いま","いみ","いもうと","いや","いりぐち","いる","いる","いれる",
			"いろ","いろいろ","うえ","うしろ","うすい","うた","うたう","うち",
			"うまれる","うみ","うる","うわぎ","えいが","えいがかん","えいご",
			"ええ","えき","えれべーたー","えんぴつ","おいしい","おおきい",
			"おおぜい","おかあさん","おかし","おかね","おきる","おく","おくさん",
			"おさけ","おさら","おじ","おじ","おじいさん","おしえる","おす",
			"おそい","おちゃ","おてあらい","おとうさん","おとうと","おとこ",
			"おとこのこ","おととい","おととし","おとな","おなか","おなじ",
			"おにいさん","おねえさん","おば","おば","おばあさん","おべんとう",
			"おぼえる","おもい","おもしろい","およぐ","おりる","おわる",
			"おんがく","おんな","おんなのこ","がいこく","がいこくじん","かいしゃ",
			"かいだん","かいもの","かう","かえす","かえる","かお","かぎ",
			"かく","がくせい","かける","かさ","かす","かぜ","かぞく",
			"かたかな","がっこう","かど","かない","かばん","かびん","かぶる",
			"かみ","かめら","かようび","からい","からだ","かりる","かるい",
			"かれんだー","かわ","かわ","かわいい","かんじ","きいろい","きえる",
			"きく","きた","ぎたー","きたない","きっさてん","きって","きっぷ",
			"きのう","きゅう","ぎゅうにく","きょう","きょうしつ","きょうだい","きょねん",
			"きらい","きる","きる","きれい","きろ","ぎんこう","きんようび",
			"くすり","ください","くだもの","くち","くつ","くつした","くに","くもる",
			"くらい","くらい","ぐらい","くらす","ぐらむ","くる","くるま",
			"くろい","けさ","けす","けっこう","けっこん","げつようび",
			"げんかん","げんき","ご","こうえん","こうばん","こえ","こーと",
			"ここ","ごご","ここのか","ここのつ","ごしゅじん","ごぜん","こたえる",
			"こちら","こっぷ","ことし","ことば","こども","この","ごはん",
			"こまる","これ","こんげつ","こんしゅう","こんな","こんばん","さあ",
			"さかな","さき","さく","さくぶん","さす","ざっし","さとう",
			"さむい","さらいねん","さん","し","さんぽ","しお","しかし",
			"じかん","しごと","じしょ","しずか","した","しち","しつもん","じてんしゃ",
			"じどうしゃ","しぬ","じびき","じぶん","しまる","しめる","しめる",
			"じゃあ","しゃしん","しゃつ","じゅう","じゅぎょう","しゅくだい","じょうず",
			"じょうぶ","しょうゆ","しょくどう","しる","しろい","しんぶん","すいようび",
			"すう","すかーと","すき","すぐに","すこし","すずしい","すとーぶ",
			"すぷーん","すぽーつ","ずぼん","すむ","すりっぱ","する","すわる",
			"せい","せいと","せっけん","せーたー","せびろ","せまい","ぜろ",
			"せん","せんげつ","せんしゅう","せんせい","せんたく","ぜんぶ","そうじ",
			"そうして","そして","そこ","そちら","そと","その","そば","そら",
			"それ","それから","それでは","だいがく","たいしかん","だいじょうぶ",
			"だいすき","たいせつ","たいてい","だいどころ","たいへん","たかい",
			"たくさん","たくしー","だす","たつ","たてもの","たのしい","たのむ",
			"たばこ","たぶん","たべもの","たべる","たまご","だれ","たんじょうび",
			"だんだん","ちいさい","ちかい","ちがう","ちかく","ちかてつ",
			"ちず","ちち","ちゃいろ","ちゃわん","ちょうど","ちょっと","ついたち",
			"つかう","つかれる","つぎ","つく","つくえ","つくる","つける","つとめる",
			"つまらない","つめたい","つよい","て","てーぷ","てーぶる","でかける",
			"てがみ","できる","でぐち","てすと","では","でぱーと","でも",
			"でる","てれび","てんき","でんき","でんしゃ","でんわ","と","どあ",
			"といれ","どうして","どうぞ","どうぶつ","どうも","とお","とおい","とおか",
			"ときどき","とけい","どこ","ところ","としょかん","どちら","とても","どなた",
			"となり","どの","とぶ","とまる","ともだち","どようび","とり","とりにく",
			"とる","とる","どれ","どんな","ない","ないふ","なか","ながい",
			"なく","なつ","なつやすみ","ななつ","なに","なのか","なまえ","ならう",
			"ならぶ","ならべる","に","にぎやか","にく","にし","にちようび",
			"にもつ","にゅーす","にわ","ぬぐ","ねくたい","ねる","のーと","のぼる",
			"のみもの","のむ","のる","は","ぱーてぃー","はい","はいざら",
			"はいる","はがき","はく","はこ","はし","はし","はじまる","はじめに",
			"はじめて","はしる","ばす","ばたー","はたち","はたらく","はち","はつか",
			"はな","はな","はなし","はなす","はは","はやい","はやい","はる",
			"はる","はれる","はん","ばん","ぱん","はんかち","ばんごう",
			"ばんごはん","はんぶん","ひがし","ひく","ひく","ひくい","ひこうき",
			"ひだり","ひと","ひとつ","ひとつき","ひとり","ひま","ひゃく","びょういん",
			"びょうき","ひらがな","ひる","ひるごはん","ひろい","ふぃるむ","ふうとう",
			"ぷーる","ふぉーく","ふく","ふく","ふたつ","ぶたにく","ふたり","ふつか",
			"ふとい","ふゆ","ふる","ふるい","ふろ","ぱーじ","へた","べっど",
			"へや","へん","ぺん","べんきょう","べんり","ほう","ぼうし","ぼーるぺん",
			"ほか","ぽけっと","ほしい","ほそい","ぼたん","ほてる","ほん",
			"ほんだな","ほんとうに","まいあさ","まいげつ","まいつき","まいしゅう","まいにち",
			"まいねん","まいとし","まいばん","まえ","まがる","まずい","また","まだ",
			"まち","まつ","まっすぐ","まっち","まど","まるい","まるい","まん",
			"まんねんひつ","みがく","みぎ","みじかい","みず","みせ","みせる","みち",
			"みっか","みっつ","みなさん","みなみ","みみ","みる","みんな","むいか",
			"むこう","むずかしい","むっつ","め","めーとる","めがね","もう",
			"もくようび","もしもし","もちろん","もつ","もっと","もの","もん",
			"もんだい","やおや","やさい","やさしい","やすい","やすみ","やすむ",
			"やっつ","やま","やる","ゆうがた","ゆうびんきょく","ゆうべ","ゆうめい",
			"ゆき","ゆっくり","ようか","ようふく","よく","よこ","よっか","よっつ",
			"よぶ","よむ","よる","らいげつ","らいしゅう","らいねん","らじお",
			"りっぱ","りゅうがくせい","りょうしん","りょうり","りょこう","れい","れいぞうこ",
			"れこーど","れすとらん","れんしゅう","ろく","わいしゃつ","わかい",
			"わかる","わすれる","わたくし","わたし","わたす","わたる","わるい",
			"ああ","あいさつ","あいだ","あう","あかちゃん","あがる","あかんぼう","あく","あくせさりー","あげる","あさい","あじ","あじあ","あす","あそび", // JLPT lv 3
			"あつまる","あつめる","あなうんさー","あふりか","あめりか","あやまる","あるこーる","あるばいと","あんしん","あんぜん","あんな","あんない",
			"いか","いがい","いがく","いきる","いけん","いし","いじめる","いじょう","いそぐ","いたす","いただく","いちど","いっしょうけんめい","いっぱい",
			"いと","いない","いなか","いのる","いらっしゃる","うえる","うかがう","うかがう","うけつけ","うける","うごく","うそ","うち","うつ","うつくしい",
			"うつす","うつる","うで","うまい","うら","うりば","うれしい","うん","うんてんしゅ","うんてん","うんどう","えすかれーたー","えだ","えらぶ","えんりょ",
			"おいでになる","おいわい","おーとばい","おーばー","おかげ","おかしい","おく","おくじょう","おくりもの","おくる","おくれる","おこさん","おこす",
			"おこなう","おこる","おしいれ","おじょうさん","おたく","おちる","おっしゃる","おっと","おつり","おと","おとす","おどり","おどる","おどろく",
			"おまつり","おみまい","おみやげ","おもいだす","おもう","おもちゃ","おもて","おや","おりる","おる","おる","おれい","おれる","おわり","かーてん",
			"かいがん","かいぎ","かいぎしつ","かいじょう","かいわ","かえり","かえる","かがく","かがみ","かける","かける","かける","かざる","かじ","がす",
			"がそりん","がそりんすたんど","かた","かたい","かたち","かたづける","かちょう","かつ","かっこう","かない","かなしい","かならず","かねもち",
			"おかねもち","かのじょ","かべ","かまう","かみ","かむ","かよう","がらす","かれ","かれら","かわく","かわり","かわる","かんがえる","かんけい",
			"かんごふ","かんたん","がんばる","き","きかい","きけん","きこえる","きしゃ","ぎじゅつ","きせつ","きそく","きっと","きぬ","きびしい","きぶん",
			"きまる","きみ","きめる","きもち","きもの","きゃく","きゅう","きゅうこう","きょういく","きょうかい","きょうそう","きょうみ","きんじょ","ぐあい",
			"くうき","くうこう","くさ","くださる","くび","くも","くらべる","くれる","くれる","け","け","けいかく","けいけん","けいざい","けいさつ","けが",
			"けーき","けしき","けしごむ","げしゅく","けっして","けれど","けれども","げんいん","けんか","げんかん","けんきゅう","けんきゅうしつ","けんぶつ",
			"こ","こう","こうがい","こうぎ","こうぎょう","こうこう","こうとうがっこう","こうこうせい","こうじょう","こうちょう","こうつう","こうどう",
			"こうむいん","こくさい","こころ","ごしゅじん","こしょう","ごぞんじ","こたえ","ごちそう","こっち","こと","ことり","このあいだ","このごろ",
			"こまかい","ごみ","こむ","こめ","ごらんになる","これから","こわい","こわす","こわれる","こんさーと","こんど","こんや","さいきん","さいご",
			"さいしょ","さか","さがす","さがる","さかん","さげる","さしあげる","さっき","さびしい","さらいげつ","さらいしゅう","さらだ","さわぐ","さわる",
			"さんぎょう","さんだる","さんどいっち","ざんねん","し","じ","しあい","しかた","しかる","しけん","じこ","じしん","じだい","したぎ","したくする",
			"しっかり","しっぱい","しつれい","じてん","しなもの","しばらく","しま","しみん","じむしょ","しゃかい","しゃちょう","じゃま","じゃむ","じゆう",
			"しゅうかん","じゅうしょ","じゅうどう","じゅうぶん","しゅっせきする","しゅっぱつする","しゅみ","じゅんびする","しょうかい","しょうがつ",
			"しょうがっこう","しょうせつ","しょうたいする","しょうちする","しょうらい","しょくじする","しょくしゅ","しょくりょうひん","じょせい","しらせる",
			"しらべる","じんこう","じんじゃ","しんせつ","しんぱいする","しんぶんしゃ","すいえい","すいどう","ずいぶん","すうがく","すーつ","すーつけーす",
			"すーぱー","すぎる","すく","すく","すくりーん","すごい","すすむ","すっかり","ずっと","すてーき","すてれお","すてる","すな","すばらしい",
			"すべる","すみ","すむ","すり","すると","せいかつする","せいさんする","せいじ","せいよう","せかい","せき","せつめい","せなか","ぜひ","せわする",
			"せん","ぜんぜん","せんそう","せんぱい","せんもん","そう","そうだんする","そだてる","そつぎょう","そふ","そふと","そぼ","それで","それに",
			"それほど","そろそろ","そんな","そんなに","たいいんする","だいがくせい","だいじ","だいたい","たいてい","たいぷ","だいぶ","たいふう",
			"たおれる","だから","たしか","たす","たずねる","たずねる","ただしい","たたみ","たてる","たてる","たとえば","たな","たのしむ","たのしみ",
			"たまに","ため","だめ","たりる","だんせい","だんぼう","ち","ちぇっく","ちから","ちっとも","ちゅうい","ちゅうがっこう","ちゅうしゃ",
			"ちゅうしゃじょう","ちり","つかまえる","つき","つく","つける","つける","つごう","つたえる","つづく","つづける","つつむ","つま","つもり",
			"つる","つれる","ていねい","てきすと","てきとう","できる","できるだけ","てつだう","てにす","てぶくろ","てら","てん","てんいん",
			"てんきよほう","でんとう","でんぽう","てんらんかい","と","どうぐ","とうとう","どうぶつえん","とおく","とおり","とおる","とくに","とくべつ",
			"とこや","とちゅう","とっきゅう","とどける","とまる","とめる","とりかえる","どろぼう","どんどん","なおす","なおる","なおる","なかなか",
			"なく","なくなる","なくなる","なげる","なさる","なる","なるべく","なるほど","なれる","におい","にがい","にげる","にっき","にゅういん",
			"にゅうがく","にる","にんぎょう","ぬすむ","ぬる","ぬれる","ねだん","ねつ","ねっしん","ねぼう","ねむい","ねむる","のこる","のど","のりかえる",
			"のりもの","は","ばあい","ぱーと","ばい","はいけん","はいしゃ","はこぶ","はじめる","ばしょ","はず","はずかしい","ぱそこん","はつおん",
			"はっきり","はなみ","ぱぱ","はやし","はらう","ばんぐみ","はんたい","はんばーぐ","ひ","ぴあの","ひえる","ひかる","ひかり","ひきだし","ひげ",
			"ひこうじょう","ひさしぶり","びじゅつかん","ひじょうに","びっくりする","ひっこす","ひつよう","ひどい","ひらく","びる","ひるま","ひるやすみ",
			"ひろう","ふぁっくす","ふえる","ふかい","ふくざつ","ふくしゅう","ぶちょう","ふつう","ぶどう","ふとる","ふとん","ふね","ふべん","ふむ",
			"ぷれぜんと","ぶんか","ぶんがく","ぶんぽう","べつ","べる","へん","へんじ","ぼうえき","ほうそう","ほうりつ","ぼく","ほし","ほど","ほとんど",
			"ほめる","ほんやく","まいる","まける","まじめ","まず","または","まちがえる","まにあう","まわり","まわる","まんが","まんなか","みえる",
			"みずうみ","みそ","みつかる","みつける","みな","みなと","むかえる","むかう","むかし","むし","むすこ","むすめ","むり","めしあがる","めずらしい",
			"もうしあげる","もうす","もうすぐ","もし","もちろん","もっとも","もどる","もめん","もらう","もり","やく","やくにたつ","やくそく","やける",
			"やさしい","やせる","やっと","やはり","やっぱり","やむ","やめる","やる","やわらかい","ゆ","ゆうはん","ゆしゅつ","ゆにゅう","ゆび","ゆびわ",
			"ゆめ","ゆれる","よう","よう","ようい","ようじ","よごれる","よしゅう","よてい","よやく","よる","よろこぶ","よろしい","りゆう","りよう",
			"りょうほう","りょかん","るす","れいぼう","れきし","れじ","れぽーと","れんらく","わーぷろ","わかす","わかれる","わく","わけ","わすれもの","わらう","わりあい","われる"
		];
		public static const list:Array = [
			"able","about","account","acid","across","act","addition","adjustment","advertisement","after","again","against","agreement","air","all","almost","among","amount","amusement","and","angle","angry","animal","answer","ant","any","apparatus","apple","approval","arch","argument","arm","army","art","as","at","attack","attempt","attention","attraction","authority","automatic","awake",
			"baby","back","bad","bag","balance","ball","band","base","basin","basket","bath","be","beautiful","because","bed","bee","before","behaviour","belief","bell","bent","berry","between","bird","birth","bit","bite","bitter","black","blade","blood","blow","blue","board","boat","body","boiling","bone","book","boot","bottle","box","boy","brain","brake","branch","brass","bread","breath","brick","bridge","bright","broken","brother","brown","brush","bucket","building","bulb","burn","burst","business","but","butter","button","by",
			"cake","camera","canvas","card","care","carriage","cart","cat","cause","certain","chain","chalk","chance","change","cheap","cheese","chemical","chest","chief","chin","church","circle","clean","clear","clock","cloth","cloud","coal","coat","cold","collar","colour","comb","come","comfort","committee","common","company","comparison","competition","complete","complex","condition","connection","conscious","control","cook","copper","copy","cord","cork","cotton","cough","country","cover","cow","crack","credit","crime","cruel","crush","cry","cup","cup","current","curtain","curve","cushion",
			"damage","danger","dark","daughter","day","dead","dear","death","debt","decision","deep","degree","delicate","dependent","design","desire","destruction","detail","development","different","digestion","direction","dirty","discovery","discussion","disease","disgust","distance","distribution","division","do","dog","door","doubt","down","drain","drawer","dress","drink","driving","drop","dry","dust",
			"ear","early","earth","east","edge","education","effect","egg","elastic","electric","end","engine","enough","equal","error","even","event","ever","every","example","exchange","existence","expansion","experience","expert","eye",
			"face","fact","fall","false","family","far","farm","fat","father","fear","feather","feeble","feeling","female","fertile","fiction","field","fight","finger","fire","first","fish","fixed","flag","flame","flat","flight","floor","flower","fly","fold","food","foolish","foot","for","force","fork","form","forward","fowl","frame","free","frequent","friend","from","front","fruit","full","future",
			"garden","general","get","girl","give","glass","glove","go","goat","gold","good","government","grain","grass","great","green","grey","grip","group","growth","guide","gun",
			"hair","hammer","hand","hanging","happy","harbour","hard","harmony","hat","hate","have","he","head","healthy","hear","hearing","heart","heat","help","high","history","hole","hollow","hook","hope","horn","horse","hospital","hour","house","how","humour",
			"ice","idea","if","ill","important","impulse","in","increase","industry","ink","insect","instrument","insurance","interest","invention","iron","island",
			"jelly","jewel","join","journey","judge","jump",
			"keep","kettle","key","kick","kind","kiss","knee","knife","knot","knowledge",
			"land","language","last","late","laugh","law","lead","leaf","learning","leather","left","leg","let","letter","level","library","lift","light","like","limit","line","linen","lip","liquid","list","little","living","lock","long","look","loose","loss","loud","love","low",
			"machine","make","male","man","manager","map","mark","market","married","mass","match","material","may","meal","measure","meat","medical","meeting","memory","metal","middle","military","milk","mind","mine","minute","mist","mixed","money","monkey","month","moon","morning","mother","motion","mountain","mouth","move","much","muscle","music",
			"nail","name","narrow","nation","natural","near","necessary","neck","need","needle","nerve","net","new","news","night","no","noise","normal","north","nose","not","note","now","number","nut",
			"observation","of","off","offer","office","oil","old","on","only","open","operation","opinion","opposite","or","orange","order","organization","ornament","other","out","oven","over","owner",
			"page","pain","paint","paper","parallel","parcel","part","past","paste","payment","peace","pen","pencil","person","physical","picture","pig","pin","pipe","place","plane","plant","plate","play","please","pleasure","plough","pocket","point","poison","polish","political","poor","porter","position","possible","pot","potato","powder","power","present","price","print","prison","private","probable","process","produce","profit","property","prose","protest","public","pull","pump","punishment","purpose","push","put",
			"quality","question","quick","quiet","quite",
			"rail","rain","range","rat","rate","ray","reaction","reading","ready","reason","receipt","record","red","regret","regular","relation","religion","representative","request","respect","responsible","rest","reward","rhythm","rice","right","ring","river","road","rod","roll","roof","room","root","rough","round","rub","rule","run",
			"sad","safe","sail","salt","same","sand","say","scale","school","science","scissors","screw","sea","seat","second","secret","secretary","see","seed","seem","selection","self","send","sense","separate","serious","servant","sex","shade","shake","shame","sharp","sheep","shelf","ship","shirt","shock","shoe","short","shut","side","sign","silk","silver","simple","sister","size","skin","skirt","sky","sleep","slip","slope","slow","small","smash","smell","smile","smoke","smooth","snake","sneeze","snow","so","soap","society","sock","soft","solid","some","son","song","sort","sound","soup","south","space","spade","special","sponge","spoon","spring","square","stage","stamp","star","start","statement","station","steam","steel","stem","step","stick","sticky","stiff","still","stitch","stocking","stomach","stone","stop","store","story","straight","strange","street","stretch","strong","structure","substance","such","sudden","sugar","suggestion","summer","sun","support","surprise","sweet","swim","system",
			"table","tail","take","talk","tall","taste","tax","teaching","tendency","test","than","that","the","then","theory","there","thick","thin","thing","this","thought","thread","throat","through","through","thumb","thunder","ticket","tight","till","time","tin","tired","to","toe","together","tomorrow","tongue","tooth","top","touch","town","trade","train","transport","tray","tree","trick","trouble","trousers","true","turn","twist",
			"umbrella","under","unit","up","use",
			"value","verse","very","vessel","view","violent","voice",
			"waiting","walk","wall","war","warm","wash","waste","watch","water","wave","wax","way","weather","week","weight","well","west","wet","wheel","when","where","while","whip","whistle","white","who","why","wide","will","wind","window","wine","wing","winter","wire","wise","with","woman","wood","wool","word","work","worm","wound","writing","wrong",
			"year", "yellow", "yes", "yesterday", "you", "young"
		];
		public static function getRandom():String {
			return list[ int(Math.random() * list.length) ];
		}
		public static function getJRandom():Array {
			var index:int = int(Math.random() * jList.length);
			//return [jKanjiList[index], jList[index]];
			return ["", jList[index]];
		}
		public static function isExist(word:String):Boolean {
			return( (list.indexOf(word) >= 0) );
		}
		public static function isJExist(word:String):Boolean {
			if ( word.charCodeAt(0) < 128 ) {
				word = Roma.getHiragana(word);
			}
			return( (jList.indexOf(word) >= 0 ) );
		}
	}
	
	// 音管理クラス
	class SoundManager {
		public static var ClickSound:Sound;
		public static var ComboSound:Sound;
		public static var ShootSound:Sound;
		public static var AttackSound:Sound;
		public static var HitSound:Sound;
		public static var DamageSound:Sound;
		public static var DangerSound:Sound;
		public static var TypeSound:Sound;
		public static var GameBGM:Sound;
		public static var ReflectSound:Sound;
		public static var count:Number = 0;
		public static const countMax:Number = 10;
		public static var initChecker:EventDispatcher = new EventDispatcher();
		public static function init():void {
			ClickSound = new Sound();
			ClickSound.addEventListener(Event.COMPLETE, onComplete);
			ClickSound.load(new URLRequest("http://keno.serio.jp/sound/click.mp3"));
			ComboSound = new Sound();
			ComboSound.addEventListener(Event.COMPLETE, onComplete);
			ComboSound.load(new URLRequest("http://keno.serio.jp/sound/combo.mp3"));
			ShootSound = new Sound();
			ShootSound.addEventListener(Event.COMPLETE, onComplete);
			ShootSound.load(new URLRequest("http://keno.serio.jp/sound/shoot.mp3"));
			AttackSound = new Sound();
			AttackSound.addEventListener(Event.COMPLETE, onComplete);
			AttackSound.load(new URLRequest("http://keno.serio.jp/sound/attack.mp3"));
			HitSound = new Sound();
			HitSound.addEventListener(Event.COMPLETE, onComplete);
			HitSound.load(new URLRequest("http://keno.serio.jp/sound/hit.mp3"));
			DamageSound = new Sound();
			DamageSound.addEventListener(Event.COMPLETE, onComplete);
			DamageSound.load(new URLRequest("http://keno.serio.jp/sound/damage.mp3"));
			DangerSound = new Sound();
			DangerSound.addEventListener(Event.COMPLETE, onComplete);
			DangerSound.load(new URLRequest("http://keno.serio.jp/sound/danger.mp3"));
			TypeSound = new Sound();
			TypeSound.addEventListener(Event.COMPLETE, onComplete);
			TypeSound.load(new URLRequest("http://keno.serio.jp/sound/type.mp3"));
			GameBGM = new Sound();
			GameBGM.addEventListener(Event.COMPLETE, onComplete);
			GameBGM.load(new URLRequest("http://keno.serio.jp/sound/gameBgm.mp3"));
			ReflectSound = new Sound();
			ReflectSound.addEventListener(Event.COMPLETE, onComplete);
			ReflectSound.load(new URLRequest("http://keno.serio.jp/sound/reflect.mp3"));
		}
		private static var gameBgmCh:SoundChannel;
		public static function playGameBgm():void {
			timer.stop();
			if( gameBgmCh == null ) gameBgmCh = GameBGM.play(0, 99999);
		}
		public static function stopGameBgm():void {
			if ( gameBgmCh != null ) {
				timer.start();
				timer.addEventListener(TimerEvent.TIMER, onTimer);
			}
		}
		private static var timer:Timer = new Timer(100, 10);
		private static var volume:Number = 1.0;
		private static function onTimer(e:TimerEvent):void {
			gameBgmCh.soundTransform = new SoundTransform(volume);
			volume-= 0.1;
			if ( volume < 0.1 ) {
				e.target.removeEventListener(TimerEvent.TIMER, onTimer);
				gameBgmCh.stop();
				gameBgmCh = null;
			}
		}
		private static function onComplete(e:Event):void {
			count++;
			e.target.removeEventListener(Event.COMPLETE, onComplete);
			initChecker.dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS, false, false, count, countMax));
		}
		
	}
	
	class Log
	{
		private static var _tf:TextField;
		
		public static function init(stageRef:Stage):void
		{
			_tf = new TextField();
			_tf.height = stageRef.stageHeight;
			_tf.background = true;
			_tf.border = true;
			_tf.type = TextFieldType.INPUT;
			stageRef.addChild(_tf);
		}
		
		public static function get tf():TextField { return _tf; }
		
		public static function set tf(value:TextField):void 
		{
			_tf = value;
		}
		
		public static function out(value:String):void
		{
			_tf.text = value + "\n" + _tf.text;
		}
		
		public static function clear():void
		{
			_tf.text = "";
		}
	}