module kyojintati4d.taskending3;

private import y4d;
private import y4d_aux.filesys;
private import y4d_draw.texturebase;

private import kyojintati4d.myapp;
private import kyojintati4d.gameinfo;

private import yamalib.draw.camerawork;
private import yamalib.draw.filmnoise;
private import yamalib.draw.drawassist;
private import yamalib.counterfsp;
private import yamalib.log.log;

/// メインパートのエンディング
class TaskEnding3 : GameTaskBase {
	
	/// タスク名を返却する
	/**
		このタスク名称を使って、コントローラ部でタスク判別を行う可能性があるので
		ユニークな名前で、実装しておくべきである
	*/	
	override char[] getTaskName() {
		with (kyojintati4d.val.kyojinconst) {
			return cast(char[]) KyojinConst.TASK_NAME[KyojinConst.Task.Task_Ending3];
		}
	}

	/// 移動の処理を行います
	override int onMove(Object o) {
		info = cast(GameInfo) o;

		if (!m_init) {
			onInit();
			info.screen.blendSrcAlpha();
			m_init = true;
		}
		
		if (m_loading) { 
			if (m_bgm.isLoading()) {
				return 0;
			} else {
				m_loading = false;
				m_allScene[m_currentScene].resetSceneTimer();
			}
		}

		// 現在のシーンの終了チェック
		if ( m_allScene[m_currentScene].isFinish() ) {
			if (m_currentScene < m_allScene.length-1) {
				// オブジェクト破棄
				m_allScene[m_currentScene].destroy();
				++m_currentScene;
				// タイマーをリセット
				m_allScene[m_currentScene].resetSceneTimer();
				Log.print("%s#onMove : NEXT SCENE%s. [%s]ms" , 
					super.toString(), m_currentScene, m_timer.get());
			} else {
				info.mouse.show();
				m_finish = true;
				// エンディング終了
				// すべてのスタックを破棄		
				info.gameSceneTransiter.exitScene();

				info.gameSceneTransiter.setTransitType( 19, 2, true );
				info.gameSceneTransiter.jumpScene(KyojinConst.Task.Task_Title);
				Log.print("%s#onMove : ALL SCENE FINISHED." , super.toString());
				return -1;
			}
		}
		
		if (!m_finish) {
			m_allScene[m_currentScene].onMove(info.screen);
		}

		return 0;
	}

	/// 描画の処理を行います
	override int onDraw(Object o) {
		info = cast(GameInfo) o;
		if (m_loading) {
			return 0;
		}
		if (!m_finish) {
			m_allScene[m_currentScene].onDraw(info.screen);
		}
		return 0;
	}

	/// 古いヤツ
	int task(Object o) {
		onMove(o);
		onDraw(o);
		return 0;
	}
	
	/// 占有しているメモリを解放する
	override void destroy() {
	}

	this() {
	}
	
private:

	private SoundLoader bgmloader;

	/// 初期化
	void onInit() {
		
		// まずマウスけしく
		info.mouse.hide();
		
		// 現在のシーン
		m_currentScene = 0;	// DEFAULT
//		m_currentScene = 1;	// VOICE 03
//		m_currentScene = 2;	// VOCAL 04
//		m_currentScene = 3;	// ボーカル間奏 05
//		m_currentScene = 4;	// ボーカルさび 06
//		m_currentScene = 5;	// VOICE 07
//		m_currentScene = 6;	// VOICE 08
//		m_currentScene = 7;	// VOICE 09
//		m_currentScene = 8;	// VOICE 10
//		m_currentScene = 9;	// VOICE 11

		// BGM 読み込み
		m_bgm = new Sound();
//		m_bgm.load( FileSys.read("snd/song/COZ_I_Want_You_To_(Ye-He).ogg"), 0);
		
		switch (m_currentScene) {
		case 0:
			m_bgm.load(cast(char[]) "snd\\song\\COZ_I_Want_You_To_(Ye-He).ogg",-1);
			break;
		case 1:
			m_bgm.load(cast(char[]) "snd/song/COZ_I_Want_You_To_(Ye-He)03.wav", 0);
			break;
		case 2:
			m_bgm.load(cast(char[]) "snd/song/COZ_I_Want_You_To_(Ye-He)04.wav", 0);
			break;
		case 3:
			m_bgm.load(cast(char[]) "snd/song/COZ_I_Want_You_To_(Ye-He)05.wav", 0);
			break;
		case 4:
			m_bgm.load(cast(char[]) "snd/song/COZ_I_Want_You_To_(Ye-He)06.wav", 0);
			break;
		case 5:
			m_bgm.load(cast(char[]) "snd/song/COZ_I_Want_You_To_(Ye-He)07.wav", 0);
			break;
		case 6:
			m_bgm.load(cast(char[]) "snd/song/COZ_I_Want_You_To_(Ye-He)08.wav", 0);
			break;
		case 7:
			m_bgm.load(cast(char[]) "snd/song/COZ_I_Want_You_To_(Ye-He)09.wav", 0);
			break;
		case 8:
			m_bgm.load(cast(char[]) "snd/song/COZ_I_Want_You_To_(Ye-He)10.wav", 0);
			break;
		case 9:
			m_bgm.load(cast(char[]) "snd/song/COZ_I_Want_You_To_(Ye-He)11.wav", 0);
			break;
		default:
			assert(false);
		}

//		m_currentScene = 0;	// 現在のシーン

		// フィルムノイズ用背景描画クラスの生成
		m_filmNoiseBg = new FilmNoiseBgDraw(80u, 6u, 3u);
		// フィルムノイズフィルターの生成
		createFilmNoise();

		
		// 使用するシーンの生成
		createScene();
		
		// 全シーン初期化(loadOnMemory)
		foreach (inout SceneBase scene; m_allScene) {
			scene.onInit(info.screen);
		}

		// BGMのボリュームはオプション画面のボリュームにしとっかー
		m_bgm.setVolume( info.optionInfo.getBgmVol() );
		m_bgm.play();
		m_loading = true;
		m_timer.reset();
	}

	/// 使用するシーンを構築します	
	void createScene() {
		m_timer = new FixTimer();
		m_allScene ~= new Scene01(m_timer, m_filmNoise, m_filmNoiseBg);
		m_allScene ~= new Scene03(m_timer, m_filmNoise, m_filmNoiseBg);
		m_allScene ~= new Scene04(m_timer, m_filmNoise, m_filmNoiseBg);
		m_allScene ~= new Scene05(m_timer, m_filmNoise, m_filmNoiseBg);
		m_allScene ~= new Scene06(m_timer, m_filmNoise, m_filmNoiseBg);
		m_allScene ~= new Scene07(m_timer, m_filmNoise, m_filmNoiseBg);
		m_allScene ~= new Scene08(m_timer, m_filmNoise, m_filmNoiseBg);
		m_allScene ~= new Scene09(m_timer, m_filmNoise, m_filmNoiseBg);
		m_allScene ~= new Scene10(m_timer, m_filmNoise, m_filmNoiseBg);
		m_allScene ~= new Scene11(m_timer, m_filmNoise, m_filmNoiseBg, m_bgm);
	}

	/// フィルムノイズクラスのセットアップを行う	
	void createFilmNoise() {

		FilmNoiseEssenceCreator.setRange(20, 600);
		FilmNoiseEssenceCreator.setMaxWidth(200u, 150u);
		FilmNoiseEssenceCreator.setMaxSpeed(5.0f, 1.5f);
		FilmNoiseEssenceCreator.setMaxAlpha(128, 16);
		FilmNoiseEssenceCreator.setReverseDefault(false, false);
		
		// ノイズテクスチャのロード
		auto texture = new Texture();
		texture.load(cast(char[]) "img/main_ending/film_noise.png");
		//
		auto tl = new TextureLoader();
		tl.setCacheSize(-1);	// 無限キャッシュ
		tl.loadDefFile(cast(char[]) "img/main_ending/noise/noise.lst");
		
		auto filmNoiseMgr = new FilmNoise.FilmNoiseEssenceManager();
		filmNoiseMgr.setMaxShowNum(5u);
		filmNoiseMgr.setCreateInterval(25);
		filmNoiseMgr.setNoiseTextureLoader(tl);
		filmNoiseMgr.setDrawNoiseNum(5u, 20u);
		for (int i = 0; i < 128; ++i) {
			auto essence = FilmNoiseEssenceCreator.create();
			essence.texture = texture;
			filmNoiseMgr.addEssence( essence );
		}
		
		m_filmNoise = new FilmNoise( filmNoiseMgr );
	}
	
	/// このエンディングで使用する背景移動用のカウンタ
	private static class BgMoveCounter {
		
		/// 増分
		void inc() {
			m_move.inc();
		}
		
		/// 初期状態に戻す
		void reset() {
			m_slipping = 0.0f;
			m_move.reset();
		}
		
		/// 現在の値を取得する
		int get() {
			if (m_move.isEnd()) {
				if (m_move.getStart() > m_move.getEnd() ){
					m_slipping -= m_slipSize;
					return cast(int) (m_move.getEnd() - m_slipping);
				} else {
					m_slipping += m_slipSize;
					return cast(int) (m_move.getEnd() + m_slipping);
				}
			}
			return m_move.get();
		}
		
		/// 移動範囲を設定する
		void set(int start_, int end_, int step_) {
			m_move.set(start_, end_, step_);
		}
		
		/// 終了しても滑るサイズ
		void setSlipSize(float slip_) {
			this.m_slipSize = slip_;
		}
		float getSlipSize() {
			return this.m_slipSize;
		}
		
		/// コンストラクタ
		this() {
			m_move = new RootCounterS();
		}

	private:
		RootCounterS m_move;	//!< 移動の基本となるカウンタ
		float m_slipSize = 0.0f;	// 移動サイズ
		float m_slipping = 0.0f;	// 累積移動量
	}

	
	/// 時間の範囲を表すクラス
	private static class TimeRange {
		this(FixTimer timer, uint start, uint end) 
		in
		{
			assert( !(timer is null) );
			assert(start < end);
		}
		body
		{
			m_timer = timer;
			m_start = start;
			m_end = end;
		}
		
		/// 残り時間の取得
		int getRemainEndTime() {
			return m_end - get();
		}
		
		/// 開始されまでの時間
		int getRemainStartTime() {
			return m_start - get();
		}
		
		/// 時間の取得
		uint getRange() {
			return m_end - m_start;
		}

		/// 時間の前
		bool isBefore() {
			return isBefore(m_start);
		}
		
		/// 時間内
		bool isDuring() {	
			return m_now >= m_start && m_now <= m_end;
			 
		}
		
		/// 時間の後
		bool isAfter() {
			return isAfter(m_end);
		}

		//-------- おまけ		
		bool isBefore(uint time) {
			return get() < time;
		}
		bool isAfter(uint time) {
			return get() >= time;
		}
		
		/// 現在時間の取得
		uint get() {
			return m_now;
		}
		
		/// 更新
		void update() {
			this.m_timer.update();
			m_now = m_timer.get();
		}
		
		
	private:
		FixTimer m_timer;
		uint m_start;
		uint m_end;
		
		uint m_now;
	}
	
	/** シーンを表現する基底クラス */
	private static class SceneBase {  	
	public:
		/// 移動処理
		abstract int onInit(Screen screen);
		/// 移動処理
		abstract int onMove(Screen screen);
		/// 描画処理
		abstract int onDraw(Screen screen);
		/// 終了したか
		bool isFinish() {
			return this.m_finish;
		}
		/// オブジェクトを解放する
		void destroy() {
		}
		
		/// 静的コンストラクタ
		static this() {
			RAND = new Rand();
		}

		/// コンストラクタ
		this(FixTimer grobalTimer) {
			m_timer = grobalTimer;
			m_innerTimer = new FixTimer();
			m_innerTimer.reset();
			m_finish = false;
		}
		
		/// シーン内タイマーをリセットする
		void resetSceneTimer() {
			m_innerTimer.reset();
		}
		
		/// TextureLoader にリストされている画像をすべてキャッシュに読み込む
		static void loadCacheAll(TextureLoader tl) {
			tl.setCacheSize(-1);	// 無限キャッシュ
			// キャッシュに読み込んでおく
			for (int i = 0; i < tl.getInfoList().size(); ++i) {
				Texture t = tl.get(i);
			}
		}
		
		/// ネガポジ変換で描画する 
		/** 
		 * これは、完全に白の背景に、変換したい画像を、subColor で描画することで
		 * 実現してある。
		 * したがって、これは一度、真っ白にクリアする。
		 * そしてその後に対象のテクスチャを描画する。
		 */
		static void drawNegaPosi(Screen screen, ITextureBase texture, int x, int y) {
			if(screen is null || texture is null) {
				return;
			}
			
			Color4ub colorOrg = screen.getColor4ub();
			int w = screen.getWidth();
			int h = screen.getHeight();
			
			// 白で塗りつぶし
			screen.setColor(255,255,255);
			screen.drawPolygon(
				0, 0,
				w, 0,
				w, h,
				0, h
			);
			
			screen.blendSubColor();
			screen.blt(texture, x, y);
		}

		/// ネガポジ変換で描画する 
		/** 
		 * これは、完全に白の背景に、変換したい画像を、subColor で描画することで
		 * 実現してある。
		 * したがって、これは一度、真っ白にクリアする。
		 * そしてその後に対象のテクスチャを描画する。
		 * (DrawAssist で描画するバージョン）
		 */
		static void drawNegaPosi(Screen screen, DrawAssist.Context context,
		 ITextureBase texture, int x, int y) {
			if(screen is null || texture is null || context is null) {
				return;
			}
			
			Color4ub colorOrg = screen.getColor4ub();
			int w = screen.getWidth();
			int h = screen.getHeight();
			
			// 白で塗りつぶし
			screen.setColor(255,255,255);
			screen.drawPolygon(
				0, 0,
				w, 0,
				w, h,
				0, h
			);
			
			screen.blendSubColor();
			DrawAssist.blt( screen, texture, x, y, context);
		}
		
	protected:
		/// ランダムで１、または-1を取得する
		static int getSignRand() {
			return RAND.get(2) == 0 ? 1 : -1;
		}

	protected:
		static Rand RAND;
		FixTimer m_timer;
		FixTimer m_innerTimer;
		bool m_finish;
	}

	/** シーン01 */
	private static class Scene01 : SceneBase {
	public :
		/// コンストラクタ
		/// 必要なものをすべて強制
		this(FixTimer grobalTimer, FilmNoise filmNoise, 
				FilmNoiseBgDraw filmNoiseBg) {
			super(grobalTimer);
			m_timeRange ~= new TimeRange(m_innerTimer, 0u, 5200u);	// 空白
			m_timeRange ~= new TimeRange(m_innerTimer, 5000u, 15000u); // 去人たち
			m_timeRange ~= new TimeRange(m_innerTimer, 15000u, 25000u);	// プレゼント
			m_timeRange ~= new TimeRange(m_innerTimer, 25000u, 30000u);	// スタッフ
			m_timeRange ~= new TimeRange(m_innerTimer, 30000u, 35000u);	// スタッフ
			m_timeRange ~= new TimeRange(m_innerTimer, 35000u, 40000u);	// スタッフ
			m_timeRange ~= new TimeRange(m_innerTimer, 40000u, 45000u);	// スタッフ
			m_timeRange ~= new TimeRange(m_innerTimer, 45000u, 50000u);	// スタッフ
			m_timeRange ~= new TimeRange(m_innerTimer, 50000u, 55000u);	// スタッフ
			m_timeRange ~= new TimeRange(m_innerTimer, 55000u, 60000u);	// スタッフ
			m_timeRange ~= new TimeRange(m_innerTimer, 60000u, 65000u);	// スタッフ
			m_timeRange ~= new TimeRange(m_innerTimer, 65000u, 70000u);	// スタッフ
			m_timeRange ~= new TimeRange(m_innerTimer, 70000u, 75000u);	// スタッフ

			m_timeRange ~= new TimeRange(m_innerTimer, 75000u, 80000u);	// セリフ
			m_timeRange ~= new TimeRange(m_innerTimer, 80000u, 85000u);	// セリフ
			m_timeRange ~= new TimeRange(m_innerTimer, 85000u, 87500u);	// セリフ
			m_timeRange ~= new TimeRange(m_innerTimer, 87500u, 90000u);	// セリフ
			m_timeRange ~= new TimeRange(m_innerTimer, 90000u, 92500u);	// セリフ

			m_timeRange ~= new TimeRange(m_innerTimer, 92600u, 93000u);	// キャラ１
			m_timeRange ~= new TimeRange(m_innerTimer, 93000u, 93400u);	// キャラ２
			m_timeRange ~= new TimeRange(m_innerTimer, 93400u, 93900u);	// セリフ３
			m_timeRange ~= new TimeRange(m_innerTimer, 93800u, 94400u);	// セリフ４
			m_timeRange ~= new TimeRange(m_innerTimer, 94300u, 95000u);	// セリフ５
			
			// フィルムノイズ用背景描画クラス
			m_filmNoiseBg = filmNoiseBg;
			// 使用する画像
			m_textureLoader = new TextureLoader();
			
			m_currentSection = SECTION.EMPTY;
			m_filmNoise = filmNoise;
			
			
		}

		/// 初期化処理
		int onInit(Screen screen) {
			try {
				m_textureLoader.loadDefFile(cast(char[]) "img/main_ending/s01/bg.lst");
				m_drawAlpha = new RootCounterS();
				m_drawAlpha.set(0, 255, 1);
				Log.print("%s#onInit : FINISH." , super.toString());
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onInit : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
		/// オブジェクトの解放
		override void destroy() {
			m_textureLoader.releaseAll();
			m_textureLoader.releaseFileList();
			m_finish = false;
			Log.print("%s#destroy : FINISH." , super.toString());
		}

		/// 移動処理	
		int onMove(Screen screen) {
			try {
				if (m_finish) {
					return 0;
				}
				switch (m_currentSection) {
				case SECTION.EMPTY:
					screen.setClearColor(255,255,255);
					m_drawAlpha.inc();
					m_timeRange[SECTION.EMPTY].update();
					if ( m_timeRange[SECTION.EMPTY].isAfter() ) {
						m_currentSection = SECTION.KYOJIN;
						m_imgIndex = 1;
						
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
						screen.setClearColor(0,0,0);
						Log.print("TO KYOJIN [%s ms]", m_timeRange[SECTION.EMPTY].get());
					}
					break;
				case SECTION.KYOJIN:
					m_timeRange[SECTION.KYOJIN].update();
					if ( m_timeRange[SECTION.KYOJIN].isAfter() ) {
						m_currentSection = SECTION.K2C;
						m_imgIndex = 2;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),9);
						Log.print("TO K2C [%s ms]", m_timeRange[SECTION.KYOJIN].get());
					}
					break;
				case SECTION.K2C:
					m_timeRange[SECTION.K2C].update();
					if ( m_timeRange[SECTION.K2C].isAfter() ) {
						m_currentSection = SECTION.STAFF;
						m_imgIndex = 3;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO STAFF [%s ms]", m_timeRange[SECTION.K2C].get());
					}
					break;
				case SECTION.STAFF:
					m_timeRange[SECTION.STAFF + m_innerIndex].update();
					if ( m_timeRange[SECTION.STAFF + m_innerIndex].isAfter() ) {
						++m_innerIndex;
						if (m_innerIndex >= 10) {
							++m_imgIndex;
							m_innerIndex = 0;
							m_currentSection = SECTION.REMARK;
							m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
							Log.print("TO REMARK [%s ms]", m_timeRange[SECTION.K2C].get());
						} else {
							++m_imgIndex;
							m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						}
					}
					break;
				case SECTION.REMARK:
					m_timeRange[SECTION.REMARK + m_innerIndex].update();
					if ( m_timeRange[SECTION.REMARK + m_innerIndex].isAfter() ) {
						++m_innerIndex;
						if (m_innerIndex >= 5) {
							++m_imgIndex;
							m_innerIndex = 0;
							m_currentSection = SECTION.CHARA;
							Log.print("TO CHARA [%s ms]", m_timeRange[SECTION.K2C].get());
							m_filmNoiseBg.noiseDrawNow(10 * getSignRand(), 5);
						} else {
							++m_imgIndex;
							m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						}
					}
					break;
				case SECTION.CHARA:
					m_timeRange[SECTION.CHARA + m_innerIndex].update();
					if ( m_timeRange[SECTION.CHARA + m_innerIndex].isAfter() ) {
						++m_innerIndex;
						if (m_innerIndex >= 5) {
							m_finish = true;
							m_innerIndex = 0;
							m_currentSection = SECTION.CHARA;
						} else {
							++m_imgIndex;
							m_filmNoiseBg.noiseDrawNow(10 * getSignRand(), 5);
						}
					}
					break;
				default:
					assert(false);
				}

				m_filmNoiseBg.onMove(screen);
				m_filmNoise.onMove(screen);
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onMove : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}

		/// 描画処理	
		int onDraw(Screen screen) {
			try {
				screen.clear();
				Color4ub colorOrg = screen.getColor4ub();
				switch (m_currentSection) {
				case SECTION.EMPTY:
					screen.setColor(colorOrg.r, colorOrg.g, colorOrg.b, m_drawAlpha.get());
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.KYOJIN:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.K2C:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.STAFF:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.REMARK:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.CHARA:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				default:
					Log.printError("m_currentSection = %s", cast(int) m_currentSection);
					assert(false);
				}

				m_filmNoise.onDraw(screen);
				
				screen.setColor(colorOrg);
				
				return 0;
			} catch (Object e) {
				Log.printError("Exception %s#onDraw : [%s] [%s]", 
					super.toString(), e.toString());
				throw e;
			}
		}
		
	private:
		enum SECTION : int {EMPTY,KYOJIN,K2C, STAFF, REMARK=13, CHARA=18};
		static const uint BASE_RANGE = 5000u;
		
		SECTION m_currentSection;
		TimeRange[] m_timeRange;
		TextureLoader m_textureLoader;
		int m_imgIndex;
		RootCounterS m_drawAlpha;
		FilmNoiseBgDraw m_filmNoiseBg;
		FilmNoise m_filmNoise;
		
		int m_innerIndex;
		
	}

	/** シーン03 声優クレジット */
	private static final class Scene03 : SceneBase {
	public :
		/// コンストラクタ
		/// 必要なものをすべて強制
		this(FixTimer grobalTimer, FilmNoise filmNoise, 
				FilmNoiseBgDraw filmNoiseBg) {
			super(grobalTimer);
			m_timeRange ~= new TimeRange(m_innerTimer, 0u, 5000u);		// ありす
			m_timeRange ~= new TimeRange(m_innerTimer, 5000u, 10000u);  // 翠子
			m_timeRange ~= new TimeRange(m_innerTimer, 10400u, 15000u);	// 富絵
			m_timeRange ~= new TimeRange(m_innerTimer, 15600u, 20000u);	// 安西
			m_timeRange ~= new TimeRange(m_innerTimer, 20600u, 25000u);	// ルビ
			m_timeRange ~= new TimeRange(m_innerTimer, 25600u, 29400u);	// アウト
			m_timeRange ~= new TimeRange(m_innerTimer, 29000u, 30000u);	// アウト
				
			// フィルムノイズ用背景描画クラス
			m_filmNoiseBg = filmNoiseBg;
			// 使用する画像
			m_textureLoader = new TextureLoader();
			
			m_currentSection = SECTION.ALICE;
			m_filmNoise = filmNoise;
		}

		/// 初期化処理
		int onInit(Screen screen) {
			try {
				m_textureLoader.loadDefFile(cast(char[]) "img/main_ending/s03/bg.lst");
				m_drawAlpha = new RootCounterS();
				m_drawAlpha.set(0, 255, 1);
				m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
				Log.print("%s#onInit : FINISH." , super.toString());
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onInit : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
		/// オブジェクトの解放
		override void destroy() {
			m_textureLoader.releaseAll();
			m_textureLoader.releaseFileList();
			m_finish = false;
			Log.print("%s#destroy : FINISH." , super.toString());
		}

		/// 移動処理	
		int onMove(Screen screen) {
			try {
				if (m_finish) {
					return 0;
				}
				switch (m_currentSection) {
				case SECTION.ALICE:
					m_timeRange[SECTION.ALICE].update();
					if ( m_timeRange[SECTION.ALICE].isAfter() ) {
						m_currentSection = SECTION.MIDORIKO;
						m_imgIndex = 1;
						
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
						screen.setClearColor(0,0,0);
						Log.print("TO MIDORIKO [%s ms]", m_timeRange[SECTION.ALICE].get());
					}
					break;
				case SECTION.MIDORIKO:
					m_timeRange[SECTION.MIDORIKO].update();
					if ( m_timeRange[SECTION.MIDORIKO].isAfter() ) {
						m_currentSection = SECTION.TOMIE;
						m_imgIndex = 2;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),9);
						Log.print("TO TOMIE [%s ms]", m_timeRange[SECTION.MIDORIKO].get());
					}
					break;
				case SECTION.TOMIE:
					m_timeRange[SECTION.TOMIE].update();
					if ( m_timeRange[SECTION.TOMIE].isAfter() ) {
						m_currentSection = SECTION.ANZAI;
						m_imgIndex = 3;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO ANZAI [%s ms]", m_timeRange[SECTION.TOMIE].get());
					}
					break;
				case SECTION.ANZAI:
					m_timeRange[SECTION.ANZAI].update();
					if ( m_timeRange[SECTION.ANZAI].isAfter() ) {
						++m_imgIndex;
						m_innerIndex = 0;
						m_currentSection = SECTION.RUBI;
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						Log.print("TO RUBI [%s ms]", m_timeRange[SECTION.ANZAI].get());
					}
					break;
				case SECTION.RUBI:
					m_timeRange[SECTION.RUBI].update();
					if ( m_timeRange[SECTION.RUBI].isAfter() ) {
						m_currentSection = SECTION.K2C_STYLE;
						++m_imgIndex;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO FADE_OUT [%s ms]", m_timeRange[SECTION.TOMIE].get());
					}
					break;
				case SECTION.K2C_STYLE:
					m_timeRange[SECTION.K2C_STYLE].update();
					if ( m_timeRange[SECTION.K2C_STYLE].isAfter() ) {
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_currentSection = SECTION.FADE_OUT;
						m_innerIndex = 0;
					}
					break;
				case SECTION.FADE_OUT:
					m_timeRange[SECTION.FADE_OUT].update();
					if ( m_timeRange[SECTION.FADE_OUT].isAfter() ) {
						m_finish = true;
						m_innerIndex = 0;
					} else {
						// フルノイズ
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
					}
					break;
				default:
					assert(false);
				}

				m_filmNoiseBg.onMove(screen);
				m_filmNoise.onMove(screen);
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onMove : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}

		/// 描画処理	
		int onDraw(Screen screen) {
			try {
				screen.clear();
				Color4ub colorOrg = screen.getColor4ub();
				switch (m_currentSection) {
				case SECTION.ALICE:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.MIDORIKO:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.TOMIE:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.ANZAI:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.RUBI:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.K2C_STYLE:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.FADE_OUT:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				default:
					Log.printError("m_currentSection = %s", cast(int) m_currentSection);
					assert(false);
				}

				m_filmNoise.onDraw(screen);
				
				screen.setColor(colorOrg);
				
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onDraw : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
	private:
		enum SECTION : int {ALICE, MIDORIKO, TOMIE, ANZAI, RUBI, K2C_STYLE,FADE_OUT};
		static const uint BASE_RANGE = 5000u;
		
		SECTION m_currentSection;
		TimeRange[] m_timeRange;
		TextureLoader m_textureLoader;
		int m_imgIndex;
		RootCounterS m_drawAlpha;
		FilmNoiseBgDraw m_filmNoiseBg;
		FilmNoise m_filmNoise;
		
		int m_innerIndex;
	}
	

	/** シーン04 ボーカル歌詞 */
	private static final class Scene04 : SceneBase {
	public :
		/// コンストラクタ
		/// 必要なものをすべて強制
		this(FixTimer grobalTimer, FilmNoise filmNoise, 
				FilmNoiseBgDraw filmNoiseBg) {
			super(grobalTimer);
			m_timeRange ~= new TimeRange(m_innerTimer, 0u, 5000u);		// cos I was not Able to help you, Yet.
			m_timeRange ~= new TimeRange(m_innerTimer, 5000u, 10000u);  // but you Also, you did Not heip me, too.
			m_timeRange ~= new TimeRange(m_innerTimer, 10000u, 15000u);	// Cos I am FOOLish, I will Be satisfied...
			m_timeRange ~= new TimeRange(m_innerTimer, 15000u, 20000u);	// repeat
				
			// フィルムノイズ用背景描画クラス
			m_filmNoiseBg = filmNoiseBg;
			// 使用する画像
			m_textureLoader = new TextureLoader();
			
			m_currentSection = SECTION.CUT01;
			m_filmNoise = filmNoise;
		}

		/// 初期化処理
		int onInit(Screen screen) {
			try {
				m_textureLoader.loadDefFile(cast(char[]) "img/main_ending/s04/bg.lst");
				m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
				Log.print("%s#onInit : FINISH." , super.toString());
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onInit : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
		/// オブジェクトの解放
		override void destroy() {
			m_textureLoader.releaseAll();
			m_textureLoader.releaseFileList();
			m_finish = false;
			Log.print("%s#destroy : FINISH." , super.toString());
		}

		/// 移動処理	
		int onMove(Screen screen) {
			try {
				if (m_finish) {
					return 0;
				}
				switch (m_currentSection) {
				case SECTION.CUT01:
					m_timeRange[SECTION.CUT01].update();
					if ( m_timeRange[SECTION.CUT01].isAfter() ) {
						m_currentSection = SECTION.CUT02;
						m_imgIndex = 1;
						
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
						screen.setClearColor(0,0,0);
						Log.print("TO CUT02 [%s ms]", m_timeRange[SECTION.CUT01].get());
					}
					break;
				case SECTION.CUT02:
					m_timeRange[SECTION.CUT02].update();
					if ( m_timeRange[SECTION.CUT02].isAfter() ) {
						m_currentSection = SECTION.CUT03;
						m_imgIndex = 2;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),9);
						Log.print("TO CUT03 [%s ms]", m_timeRange[SECTION.CUT04].get());
					}
					break;
				case SECTION.CUT03:
					m_timeRange[SECTION.CUT03].update();
					if ( m_timeRange[SECTION.CUT03].isAfter() ) {
						m_currentSection = SECTION.CUT04;
						m_imgIndex = 3;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO CUT04 [%s ms]", m_timeRange[SECTION.CUT03].get());
					}
					break;
				case SECTION.CUT04:
					m_timeRange[SECTION.CUT04].update();
					if ( m_timeRange[SECTION.CUT04].isAfter() ) {
						m_innerIndex = 0;
						m_finish = true;
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						Log.print("SCENE04 END [%s ms]", m_timeRange[SECTION.CUT04].get());
					}
					break;
				default:
					assert(false);
				}

				m_filmNoiseBg.onMove(screen);
				m_filmNoise.onMove(screen);
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onMove : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}

		/// 描画処理	
		int onDraw(Screen screen) {
			try {
				screen.clear();
				Color4ub colorOrg = screen.getColor4ub();
				switch (m_currentSection) {
				case SECTION.CUT01:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.CUT02:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.CUT03:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.CUT04:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				default:
					Log.printError("m_currentSection = %s", cast(int) m_currentSection);
					assert(false);
				}

				m_filmNoise.onDraw(screen);
				
				screen.setColor(colorOrg);
				
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onDraw : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
	private:
		enum SECTION : int {CUT01,CUT02,CUT03,CUT04};
		static const uint BASE_RANGE = 5000u;
		
		SECTION m_currentSection;
		TimeRange[] m_timeRange;
		TextureLoader m_textureLoader;
		int m_imgIndex;
		FilmNoiseBgDraw m_filmNoiseBg;
		FilmNoise m_filmNoise;
		
		int m_innerIndex;
		
	}
	

	/** シーン05 ボーカル間奏 */
	private static final class Scene05 : SceneBase {
	public :
		/// コンストラクタ
		/// 必要なものをすべて強制
		this(FixTimer grobalTimer, FilmNoise filmNoise, 
				FilmNoiseBgDraw filmNoiseBg) {
			super(grobalTimer);
			m_timeRange ~= new TimeRange(m_innerTimer, 0u, 2500u);		// cos I was not Able to help you, Yet.
			m_timeRange ~= new TimeRange(m_innerTimer, 2500u, 5000u);  // but you Also, you did Not heip me, too.
			m_timeRange ~= new TimeRange(m_innerTimer, 5000u, 7500u);	// Cos I am FOOLish, I will Be satisfied...
			m_timeRange ~= new TimeRange(m_innerTimer, 7500u, 10000u);	// repeat
				
			// フィルムノイズ用背景描画クラス
			m_filmNoiseBg = filmNoiseBg;
			// 使用する画像
			m_textureLoader = new TextureLoader();
			
			m_currentSection = SECTION.CUT01;
			m_filmNoise = filmNoise;
		}

		/// 初期化処理
		int onInit(Screen screen) {
			try {
				m_textureLoader.loadDefFile(cast(char[]) "img/main_ending/s05/bg.lst");
				m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
				Log.print("%s#onInit : FINISH." , super.toString());
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onInit : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
		/// オブジェクトの解放
		override void destroy() {
			m_textureLoader.releaseAll();
			m_textureLoader.releaseFileList();
			m_finish = false;
			Log.print("%s#destroy : FINISH." , super.toString());
		}

		/// 移動処理	
		int onMove(Screen screen) {
			try {
				if (m_finish) {
					return 0;
				}
				switch (m_currentSection) {
				case SECTION.CUT01:
					m_timeRange[SECTION.CUT01].update();
					if ( m_timeRange[SECTION.CUT01].isAfter() ) {
						m_currentSection = SECTION.CUT02;
						m_imgIndex = 1;
						
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
						screen.setClearColor(0,0,0);
						Log.print("TO CUT02 [%s ms]", m_timeRange[SECTION.CUT01].get());
					}
					break;
				case SECTION.CUT02:
					m_timeRange[SECTION.CUT02].update();
					if ( m_timeRange[SECTION.CUT02].isAfter() ) {
						m_currentSection = SECTION.CUT03;
						m_imgIndex = 2;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),9);
						Log.print("TO CUT03 [%s ms]", m_timeRange[SECTION.CUT04].get());
					}
					break;
				case SECTION.CUT03:
					m_timeRange[SECTION.CUT03].update();
					if ( m_timeRange[SECTION.CUT03].isAfter() ) {
						m_currentSection = SECTION.CUT04;
						m_imgIndex = 3;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO CUT04 [%s ms]", m_timeRange[SECTION.CUT03].get());
					}
					break;
				case SECTION.CUT04:
					m_timeRange[SECTION.CUT04].update();
					if ( m_timeRange[SECTION.CUT04].isAfter() ) {
						m_innerIndex = 0;
						m_finish = true;
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						Log.print("SCENE04 END [%s ms]", m_timeRange[SECTION.CUT04].get());
					}
					break;
				default:
					assert(false);
				}

				m_filmNoiseBg.onMove(screen);
				m_filmNoise.onMove(screen);
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onMove : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}

		/// 描画処理	
		int onDraw(Screen screen) {
			try {
				screen.clear();
				Color4ub colorOrg = screen.getColor4ub();
				switch (m_currentSection) {
				case SECTION.CUT01:
				case SECTION.CUT02:
				case SECTION.CUT03:
				case SECTION.CUT04:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				default:
					Log.printError("m_currentSection = %s", cast(int) m_currentSection);
					assert(false);
				}

				m_filmNoise.onDraw(screen);
				
				screen.setColor(colorOrg);
				
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onDraw : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
	private:
		enum SECTION : int {CUT01,CUT02,CUT03,CUT04};
		static const uint BASE_RANGE = 5000u;
		
		SECTION m_currentSection;
		TimeRange[] m_timeRange;
		TextureLoader m_textureLoader;
		int m_imgIndex;
		FilmNoiseBgDraw m_filmNoiseBg;
		FilmNoise m_filmNoise;
		
		int m_innerIndex;
		
	}
	
	/** シーン06 ボーカルサビ KILL ME! */
	private static final class Scene06 : SceneBase {
	public :
		/// コンストラクタ
		/// 必要なものをすべて強制
		this(FixTimer grobalTimer, FilmNoise filmNoise, 
				FilmNoiseBgDraw filmNoiseBg) {
			super(grobalTimer);
			m_timeRange ~= new TimeRange(m_innerTimer, 0u, 2500u);		// KILL ME!
			m_timeRange ~= new TimeRange(m_innerTimer, 2500u, 5000u);  // KILL ME!
			m_timeRange ~= new TimeRange(m_innerTimer, 5000u, 7500u);	// I WANT YOU TO!
			m_timeRange ~= new TimeRange(m_innerTimer, 7500u, 10000u);	// KILL ME!
				
			// フィルムノイズ用背景描画クラス
			m_filmNoiseBg = filmNoiseBg;
			// 使用する画像
			m_textureLoader = new TextureLoader();
			
			m_currentSection = SECTION.CUT01;
			m_filmNoise = filmNoise;
		}

		/// 初期化処理
		int onInit(Screen screen) {
			try {
				m_textureLoader.loadDefFile(cast(char[]) "img/main_ending/s06/bg.lst");
				m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
				Log.print("%s#onInit : FINISH." , super.toString());
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onInit : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
		/// オブジェクトの解放
		override void destroy() {
			m_textureLoader.releaseAll();
			m_textureLoader.releaseFileList();
			m_finish = false;
			Log.print("%s#destroy : FINISH." , super.toString());
		}

		/// 移動処理	
		int onMove(Screen screen) {
			try {
				if (m_finish) {
					return 0;
				}
				switch (m_currentSection) {
				case SECTION.CUT01:
					m_timeRange[SECTION.CUT01].update();
					if ( m_timeRange[SECTION.CUT01].isAfter() ) {
						m_currentSection = SECTION.CUT02;
						m_imgIndex = 1;
						
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
						screen.setClearColor(0,0,0);
						Log.print("TO CUT02 [%s ms]", m_timeRange[SECTION.CUT01].get());
					}
					break;
				case SECTION.CUT02:
					m_timeRange[SECTION.CUT02].update();
					if ( m_timeRange[SECTION.CUT02].isAfter() ) {
						m_currentSection = SECTION.CUT03;
						m_imgIndex = 2;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),9);
						Log.print("TO CUT03 [%s ms]", m_timeRange[SECTION.CUT04].get());
					}
					break;
				case SECTION.CUT03:
					m_timeRange[SECTION.CUT03].update();
					if ( m_timeRange[SECTION.CUT03].isAfter() ) {
						m_currentSection = SECTION.CUT04;
						m_imgIndex = 3;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO CUT04 [%s ms]", m_timeRange[SECTION.CUT03].get());
					}
					break;
				case SECTION.CUT04:
					m_timeRange[SECTION.CUT04].update();
					if ( m_timeRange[SECTION.CUT04].isAfter() ) {
						m_innerIndex = 0;
						m_finish = true;
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						Log.print("SCENE04 END [%s ms]", m_timeRange[SECTION.CUT04].get());
					}
					break;
				default:
					assert(false);
				}

				m_filmNoiseBg.onMove(screen);
				m_filmNoise.onMove(screen);
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onMove : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}

		/// 描画処理	
		int onDraw(Screen screen) {
			try {
				screen.clear();
				Color4ub colorOrg = screen.getColor4ub();
				switch (m_currentSection) {
				case SECTION.CUT01:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.CUT02:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.CUT03:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.CUT04:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				default:
					Log.printError("m_currentSection = %s", cast(int) m_currentSection);
					assert(false);
				}

				m_filmNoise.onDraw(screen);
				
				screen.setColor(colorOrg);
				
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onDraw : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
	private:
		enum SECTION : int {CUT01,CUT02,CUT03,CUT04};
		static const uint BASE_RANGE = 5000u;
		
		SECTION m_currentSection;
		TimeRange[] m_timeRange;
		TextureLoader m_textureLoader;
		int m_imgIndex;
		FilmNoiseBgDraw m_filmNoiseBg;
		FilmNoise m_filmNoise;
		int m_innerIndex;
	}

	/** シーン07 ボーカル 間奏 キャラクター線画 */
	private static final class Scene07 : SceneBase {
	public :
		/// コンストラクタ
		/// 必要なものをすべて強制すべし
		this(FixTimer grobalTimer, FilmNoise filmNoise, 
				FilmNoiseBgDraw filmNoiseBg) {
			super(grobalTimer);
			m_timeRange ~= new TimeRange(m_innerTimer,     0u, 2500u);		//
			m_timeRange ~= new TimeRange(m_innerTimer,  2500u, 5000u);  // KILL ME!
			m_timeRange ~= new TimeRange(m_innerTimer,  5000u, 7500u);	// I WANT YOU TO!
			m_timeRange ~= new TimeRange(m_innerTimer,  7500u, 10000u);	// KILL ME!
			m_timeRange ~= new TimeRange(m_innerTimer, 10000u, 12500u);		//
			m_timeRange ~= new TimeRange(m_innerTimer, 12500u,  15000u);  // KILL ME!
			m_timeRange ~= new TimeRange(m_innerTimer, 15000u,  17500u);	// I WANT YOU TO!
			m_timeRange ~= new TimeRange(m_innerTimer, 17500u,  20000u);	// KILL ME!
				
			// フィルムノイズ用背景描画クラス
			m_filmNoiseBg = filmNoiseBg;
			// 使用する画像
			m_textureLoader = new TextureLoader();
			
			m_currentSection = SECTION.CUT01;
			m_filmNoise = filmNoise;
			
			for (int i = 0; i < IMG_STOCK - 1; ++i) {
				m_imgNumbers ~= i;
			}
			
			Rand myRand = new Rand();
			myRand.randomize();
			// シャッフル
			for (int i = 0; i < 128; ++i) {
				int src =  myRand.get(m_imgNumbers.length);
				int dst = myRand.get(m_imgNumbers.length);
				if (src == dst) {
					continue;
				}
				int tmp = m_imgNumbers[src];
				m_imgNumbers[src] = m_imgNumbers[dst];
				m_imgNumbers[dst] = tmp;
			}
			
			// 最後は固定
			m_imgNumbers[SECTION.CUT08] = IMG_STOCK - 1;
		}
		
		/// 初期化処理
		int onInit(Screen screen) {
			try {
				m_textureLoader.loadDefFile(cast(char[]) "img/main_ending/s07/bg.lst");
				m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
				m_imgIndex = m_imgNumbers[m_imgCounter];
				Log.print("%s#onInit : FINISH." , super.toString());
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onInit : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
		/// オブジェクトの解放
		override void destroy() {
			m_textureLoader.releaseAll();
			m_textureLoader.releaseFileList();
			m_finish = false;
			Log.print("%s#destroy : FINISH." , super.toString());
		}

		/// 移動処理	
		int onMove(Screen screen) {
			try {
				if (m_finish) {
					return 0;
				}
				switch (m_currentSection) {
				case SECTION.CUT01:
					m_timeRange[SECTION.CUT01].update();
					if ( m_timeRange[SECTION.CUT01].isAfter() ) {
						m_currentSection = SECTION.CUT02;
						m_imgIndex = m_imgNumbers[++m_imgCounter];
						
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
						screen.setClearColor(0,0,0);
						Log.print("TO CUT02 [%s ms]", m_timeRange[SECTION.CUT01].get());
					}
					break;
				case SECTION.CUT02:
					m_timeRange[SECTION.CUT02].update();
					if ( m_timeRange[SECTION.CUT02].isAfter() ) {
						m_currentSection = SECTION.CUT03;
						m_imgIndex = m_imgNumbers[++m_imgCounter];
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),9);
						Log.print("TO CUT03 [%s ms]", m_timeRange[SECTION.CUT04].get());
					}
					break;
				case SECTION.CUT03:
					m_timeRange[SECTION.CUT03].update();
					if ( m_timeRange[SECTION.CUT03].isAfter() ) {
						m_currentSection = SECTION.CUT04;
						m_imgIndex = m_imgNumbers[++m_imgCounter];
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO CUT04 [%s ms]", m_timeRange[SECTION.CUT03].get());
					}
					break;
				case SECTION.CUT04:
					m_timeRange[SECTION.CUT04].update();
					if ( m_timeRange[SECTION.CUT04].isAfter() ) {
						m_currentSection = SECTION.CUT05;
						m_imgIndex = m_imgNumbers[++m_imgCounter];
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO CUT04 [%s ms]", m_timeRange[SECTION.CUT04].get());
					}
					break;
				case SECTION.CUT05:
					m_timeRange[SECTION.CUT05].update();
					if ( m_timeRange[SECTION.CUT05].isAfter() ) {
						m_currentSection = SECTION.CUT06;
						m_imgIndex = m_imgNumbers[++m_imgCounter];
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO CUT04 [%s ms]", m_timeRange[SECTION.CUT05].get());
					}
					break;
				case SECTION.CUT06:
					m_timeRange[SECTION.CUT06].update();
					if ( m_timeRange[SECTION.CUT06].isAfter() ) {
						m_currentSection = SECTION.CUT07;
						m_imgIndex = m_imgNumbers[++m_imgCounter];
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO CUT04 [%s ms]", m_timeRange[SECTION.CUT06].get());
					}
					break;
				case SECTION.CUT07:
					m_timeRange[SECTION.CUT07].update();
					if ( m_timeRange[SECTION.CUT07].isAfter() ) {
						m_currentSection = SECTION.CUT08;
						m_imgIndex = m_imgNumbers[++m_imgCounter];
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO CUT04 [%s ms]", m_timeRange[SECTION.CUT07].get());
					}
					break;
				case SECTION.CUT08:
					m_timeRange[SECTION.CUT08].update();
					if ( m_timeRange[SECTION.CUT08].isAfter() ) {
						m_innerIndex = 0;
						m_finish = true;
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						Log.print("SCENE04 END [%s ms]", m_timeRange[SECTION.CUT08].get());
					}
					break;
				default:
					assert(false);
				}

				m_filmNoiseBg.onMove(screen);
				m_filmNoise.onMove(screen);
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onMove : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}

		/// 描画処理	
		int onDraw(Screen screen) {
			try {
				screen.clear();
				Color4ub colorOrg = screen.getColor4ub();
				switch (m_currentSection) {
				case SECTION.CUT01:
				case SECTION.CUT02:
				case SECTION.CUT03:
				case SECTION.CUT04:
				case SECTION.CUT05:
				case SECTION.CUT06:
				case SECTION.CUT07:
				case SECTION.CUT08:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				default:
					Log.printError("m_currentSection = %s", cast(int) m_currentSection);
					assert(false);
				}

				m_filmNoise.onDraw(screen);
				
				screen.setColor(colorOrg);
				
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onDraw : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
	private:
		enum SECTION : int {CUT01,CUT02,CUT03,CUT04,CUT05,CUT06,CUT07,CUT08};
		static const uint BASE_RANGE = 5000u;
		static const int IMG_STOCK = 12;
		
		SECTION m_currentSection;
		TimeRange[] m_timeRange;
		TextureLoader m_textureLoader;
		int m_imgIndex;
		FilmNoiseBgDraw m_filmNoiseBg;
		FilmNoise m_filmNoise;
		
		int[] m_imgNumbers;
		int m_imgCounter;
		int m_innerIndex;
		
	}

	/** シーン０８
	 * ありす＋廊下
	 */
	private static final class Scene08 : SceneBase {
	public :
		/// コンストラクタ
		/// 必要なものをすべて強制
		this(FixTimer grobalTimer, FilmNoise filmNoise, 
				FilmNoiseBgDraw filmNoiseBg) {
			super(grobalTimer);
			// 表示用テクスチャローダ
			m_textureLoader = new TextureLoader();
			
			// カット１（イントロ）
			m_timeRange ~= new TimeRange(m_innerTimer, 0u, 2500u);	// 壁＋ノイズ
			m_timeRange ~= new TimeRange(m_innerTimer, 2500u, 5000u);	// ノイズ（廊下）
			m_timeRange ~= new TimeRange(m_innerTimer, 5000u, 7500u);	// 壁＋ノイズ
			m_timeRange ~= new TimeRange(m_innerTimer, 7500u, 10000u);	// ノイズ（廊下＋ありす）

			// カット２(you Ask Me)
			m_timeRange ~= new TimeRange(m_innerTimer, 10000u, 12500u);	// 壁＋文字
			// カット３(so I Tell you)
			m_timeRange ~= new TimeRange(m_innerTimer, 12000u, 14800u);	// 壁＋文字
			// カット４("A Nice")
			m_timeRange ~= new TimeRange(m_innerTimer, 14500u, 15400u);	// 壁拡大
			// カット５("to")
			m_timeRange ~= new TimeRange(m_innerTimer, 14900u, 15800u);	// 壁拡大
			// カット６("Meet
			m_timeRange ~= new TimeRange(m_innerTimer, 15300u, 16100u);	// 壁拡大
			// カット７("You")
			m_timeRange ~= new TimeRange(m_innerTimer, 15700u, 17700u);	// 壁拡大
			// カット８("is it?")
			m_timeRange ~= new TimeRange(m_innerTimer, 16800u, 18200u);	// 壁＋ありす
			// カット８("is it?")
			m_timeRange ~= new TimeRange(m_innerTimer, 19000u, 19750u);	// 壁＋ありす
			
			// アルファカウンタ
			m_alphaCounter = new RootCounterS();
			m_alphaCounter.set(255, 0, 48);
			
			m_alphaFlash = new RootCounter();
			m_alphaFlash.setReverse(true);
			m_alphaFlash.set(0, 48, 8);
			
			m_filmNoiseBg = filmNoiseBg;
			m_filmNoise = filmNoise;
		}

		/// 初期化処理
		int onInit(Screen screen) {
			try {
				// 表示画像リストの読み込み
				m_textureLoader.loadDefFile(cast(char[]) "img/main_ending/s08/bg.lst");
				// 展開が速いので読み込んでおくべし
				loadCacheAll(m_textureLoader);
				
				Log.print("%s#onInit FINISH.",	super.toString());
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onInit : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
	
		/// 移動処理	
		int onMove(Screen screen) {
			try {
				// 終了してる
				if (m_finish) {
					return 0;
				}
				// タイマーリセット
				if (!m_init) {
					resetSceneTimer();
					m_init = true;
				}
				if ( m_currentRange >= m_timeRange.length) {
					// シーン終了
					m_finish = true;
					return 0;
				}
				
				
				switch (m_currentRange) {
				case 0:	// カット１壁＋ノイズ
					if ( checkAfter() ) {
						// ぶれノイズ発生
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
					}
					break;
				case 1:	// カット２廊下オーバー
					// 常にノイズ
					if (m_nextTime < m_timeRange[m_currentRange].get()) {
						m_alphaFlash.inc();
					}
					if (m_alphaFlash.isLapAroundI()) {
						m_nextTime = m_timeRange[m_currentRange].get() + 500u + RAND.get(750);
						m_alphaFlash.reset();
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
					}
				
					if ( checkAfter() ) {
						m_currentRange = 2;
						m_alphaFlash.reset();
					}
					break;
				case 2:	// カット３壁＋ノイズ
					if ( checkAfter() ) {
						// ぶれノイズ発生
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_currentRange = 3;
					}
					break;
					
				case 3:	// カット４（廊下＋alice）オーバー
					if (m_nextTime < m_timeRange[m_currentRange].get()) {
						m_alphaFlash.inc();
					}
					if (m_alphaFlash.isLapAroundI()) {
						m_nextTime = m_timeRange[m_currentRange].get() + 500u + RAND.get(750);
						m_alphaFlash.reset();
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
					}

					if ( checkAfter() ) {
						// ぶれノイズ発生
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_currentRange = 4;
					}
					break;
				case 4:	// カット５ you Ask Me
					if ( checkAfter() ) {
						// ぶれノイズ発生
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_currentRange = 5;
					}
					break;
				case 5:	// カット６ so I Tell You
					if ( checkAfter() ) {
						m_imgIndex = IMG_REMARK1;
						m_currentRange = 6;
						m_filmNoise.getManager().setRate(3.0f);
					}
					break;
				case 6:	// カット６ A Nice
					if ( checkAfter() ) {
						++m_imgIndex;
						m_currentRange = 7;
						m_alphaCounter.reset();
					}
					break;
				case 7:	// カット７ to
					m_alphaCounter.inc();
					if ( checkAfter() ) {
						++m_imgIndex;
						m_currentRange = 8;
						m_alphaCounter.reset();
					}
					break;
				case 8:	// カット８ Meet
					m_alphaCounter.inc();
					if ( checkAfter() ) {
						++m_imgIndex;
						m_currentRange = 9;
						m_alphaCounter.reset();
					}
					break;
				case 9:	// カット９ You
					m_alphaCounter.inc();
					if ( checkAfter() ) {
						++m_imgIndex;
						m_currentRange = 10;
						m_filmNoise.getManager().setRate(1.0f);
						m_alphaCounter.set(800,0,7);
					}
					break;
				case 10:	// カット１０ is it?
					if ( checkAfter() ) {
						++m_imgIndex;
						m_currentRange = 11;
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
					}
					break;
				case 11:	// カット１１ フェードアウト
					m_alphaCounter.inc();
					if ( checkAfter() ) {
						m_finish = true;
					}
					break;
				default:
					assert(false);
				}
				
				m_filmNoiseBg.onMove(screen);
				m_filmNoise.onMove(screen);

				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onMove : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}

		/// 描画処理	
		int onDraw(Screen screen) {
			try {
				Color4ub colorOrg = screen.getColor4ub();
				screen.clear();
				
				switch (m_currentRange) {
				case 0:	// カット１壁＋ノイズ
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(IMG_WALL1), 0, 0);
					m_filmNoise.onDraw(screen);
					break;
				case 1:	// カット２廊下
					// 壁
					screen.setColor(255,255,255, 255);
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(IMG_WALL1), 0, 0);
					screen.setColor(255,255,255, m_alphaFlash.get() );
					// 廊下ノイズ
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(IMG_ROUKA1), 0, 0);
					break;
				case 2:	// カット３壁＋ノイズ
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(IMG_WALL1), 0, 0);
					m_filmNoise.onDraw(screen);
					break;
				case 3:	// カット４廊下＋alice
					// 壁
					screen.setColor(255,255,255,255);
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(IMG_WALL1), 0, 0);
					screen.setColor(255,255,255, m_alphaFlash.get());
					// 廊下ノイズ
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(IMG_ROUKA2), 0, 0);
					break;
				case 4:	// カット５ you Ask Me
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(IMG_WALL2), 0, 0);
					m_filmNoise.onDraw(screen);
					break;
				case 5:	// カット６ so I Tell You
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(IMG_WALL3), 0, 0);
					m_filmNoise.onDraw(screen);
					break;
				case 6:	// カット６ A Nice
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					m_filmNoise.onDraw(screen);
					break;
				case 7:	// カット７ to
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					m_filmNoise.onDraw(screen);
					if (0 != m_alphaCounter.get()) {
						screen.setColor(255,255,255, m_alphaCounter.get());
						DrawAssist.blt(screen, m_textureLoader.get(m_imgIndex-1),0,0, 
								cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE);
						screen.setColor(255,255,255,255);
					}
					break;
				case 8:	// カット８ Meet
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					m_filmNoise.onDraw(screen);
					if (0 != m_alphaCounter.get()) {
						screen.setColor(255,255,255, m_alphaCounter.get());
						DrawAssist.blt(screen, m_textureLoader.get(m_imgIndex-1),0,0, 
								cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE);
						screen.setColor(255,255,255,255);
					}
					break;
				case 9:	// カット９ You
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					m_filmNoise.onDraw(screen);
					if (0 != m_alphaCounter.get()) {
						screen.setColor(255,255,255, m_alphaCounter.get());
						DrawAssist.blt(screen, m_textureLoader.get(m_imgIndex-1),0,0, 
								cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE);
						screen.setColor(255,255,255,255);
					}
					break;
				case 10:	// カット１０ is it?
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					m_filmNoise.onDraw(screen);
					break;
				case 11:	// カット１１ フェードアウト
					int alpha = m_alphaCounter.get();
					if (alpha > 255) {
						alpha = 255;
					}
					screen.setColor(colorOrg.r, colorOrg.g, colorOrg.b, alpha);
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE, screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					m_filmNoise.onDraw(screen);
					break;
				default:
					assert(false);
				}
				
				screen.setColor(colorOrg);
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onDraw : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
		/// オブジェクトの解放
		override void destroy() {
			try {
				m_init = false;
				m_finish = false;
				m_textureLoader.releaseAll();
				m_textureLoader.releaseFileList();
				Log.print("%s#destroy : FINISH." , super.toString());
			} catch (Exception e) {
				Log.printError("Exception %s#destroy : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
				
	private:
		static const int IMG_WALL1 = 0;
		static const int IMG_ROUKA1 = 1;
		static const int IMG_ROUKA2 = 2;
		static const int IMG_WALL2 = 3;
		static const int IMG_WALL3 = 4;
		static const int IMG_REMARK1 = 5;
	
		/// 現在のカットが終了ならばture
		bool checkAfter() {
			m_timeRange[m_currentRange].update();
			if ( m_timeRange[m_currentRange].isAfter() ) {
				if ( m_currentRange + 1 >= m_timeRange.length) {
					m_finish = true;
					return true;
				}
				++m_currentRange;
				Log.print("NEXT %s [%s ms]", m_currentRange, m_timeRange[m_currentRange-1].get());
				return true;
			}
			return false;
		}
	
		bool m_init;
		TimeRange[] m_timeRange;
		int m_currentRange;

		TextureLoader m_textureLoader;
		int m_imgIndex;
		
		FilmNoiseBgDraw m_filmNoiseBg;
		FilmNoise m_filmNoise;
		
		RootCounterS m_alphaCounter;	//!< アルファ用のカウンタ
		RootCounter m_alphaFlash;
		int m_nextTime;
	}
	
	/** シーン09 ボーカル歌詞 */
	private static final class Scene09 : SceneBase {
	public :
		/// コンストラクタ
		/// 必要なものをすべて強制
		this(FixTimer grobalTimer, FilmNoise filmNoise, 
				FilmNoiseBgDraw filmNoiseBg) {
			super(grobalTimer);
			m_timeRange ~= new TimeRange(m_innerTimer, 0u, 5000u);		// cos I was not Able to help you, Yet.
			m_timeRange ~= new TimeRange(m_innerTimer, 5000u, 10000u);  // but you Also, you did Not heip me, too.
			m_timeRange ~= new TimeRange(m_innerTimer, 10000u, 15000u);	// Cos I am FOOLish, I will Be satisfied...
			m_timeRange ~= new TimeRange(m_innerTimer, 15000u, 20000u);	// repeat
				
			// フィルムノイズ用背景描画クラス
			m_filmNoiseBg = filmNoiseBg;
			// 使用する画像
			m_textureLoader = new TextureLoader();
			
			m_currentSection = SECTION.CUT01;
			m_filmNoise = filmNoise;
		}

		/// 初期化処理
		int onInit(Screen screen) {
			try {
				m_textureLoader.loadDefFile(cast(char[]) "img/main_ending/s09/bg.lst");
				m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
				Log.print("%s#onInit : FINISH." , super.toString());
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onInit : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
		/// オブジェクトの解放
		override void destroy() {
			m_textureLoader.releaseAll();
			m_textureLoader.releaseFileList();
			m_finish = false;
			Log.print("%s#destroy : FINISH." , super.toString());
		}

		/// 移動処理	
		int onMove(Screen screen) {
			try {
				if (m_finish) {
					return 0;
				}
				switch (m_currentSection) {
				case SECTION.CUT01:
					m_timeRange[SECTION.CUT01].update();
					if ( m_timeRange[SECTION.CUT01].isAfter() ) {
						m_currentSection = SECTION.CUT02;
						m_imgIndex = 1;
						
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
						screen.setClearColor(0,0,0);
						Log.print("TO CUT02 [%s ms]", m_timeRange[SECTION.CUT01].get());
					}
					break;
				case SECTION.CUT02:
					m_timeRange[SECTION.CUT02].update();
					if ( m_timeRange[SECTION.CUT02].isAfter() ) {
						m_currentSection = SECTION.CUT03;
						m_imgIndex = 2;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),9);
						Log.print("TO CUT03 [%s ms]", m_timeRange[SECTION.CUT04].get());
					}
					break;
				case SECTION.CUT03:
					m_timeRange[SECTION.CUT03].update();
					if ( m_timeRange[SECTION.CUT03].isAfter() ) {
						m_currentSection = SECTION.CUT04;
						m_imgIndex = 3;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO CUT04 [%s ms]", m_timeRange[SECTION.CUT03].get());
					}
					break;
				case SECTION.CUT04:
					m_timeRange[SECTION.CUT04].update();
					if ( m_timeRange[SECTION.CUT04].isAfter() ) {
						m_innerIndex = 0;
						m_finish = true;
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						Log.print("SCENE04 END [%s ms]", m_timeRange[SECTION.CUT04].get());
					}
					break;
				default:
					assert(false);
				}

				m_filmNoiseBg.onMove(screen);
				m_filmNoise.onMove(screen);
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onMove : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}

		/// 描画処理	
		int onDraw(Screen screen) {
			try {
				screen.clear();
				Color4ub colorOrg = screen.getColor4ub();
				switch (m_currentSection) {
				case SECTION.CUT01:
				case SECTION.CUT02:
				case SECTION.CUT03:
				case SECTION.CUT04:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE,screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				default:
					Log.printError("m_currentSection = %s", cast(int) m_currentSection);
					assert(false);
				}

				m_filmNoise.onDraw(screen);
				
				screen.setColor(colorOrg);
				
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onDraw : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
	private:
		enum SECTION : int {CUT01,CUT02,CUT03,CUT04};
		static const uint BASE_RANGE = 5000u;
		
		SECTION m_currentSection;
		TimeRange[] m_timeRange;
		TextureLoader m_textureLoader;
		int m_imgIndex;
		FilmNoiseBgDraw m_filmNoiseBg;
		FilmNoise m_filmNoise;
		
		int m_innerIndex;
	}
	
	/** シーン10 ボーカル歌詞 */
	private static final class Scene10 : SceneBase {
	public :
		/// コンストラクタ
		/// 必要なものをすべて強制
		this(FixTimer grobalTimer, FilmNoise filmNoise, 
				FilmNoiseBgDraw filmNoiseBg) {
			super(grobalTimer);
			m_timeRange ~= new TimeRange(m_innerTimer, 0u, 2500u);		// cos I was not Able to help you, Yet.
			m_timeRange ~= new TimeRange(m_innerTimer, 2500u,  5000u);  // but you Also, you did Not heip me, too.
			m_timeRange ~= new TimeRange(m_innerTimer, 5000u,  7500u);	// Cos I am FOOLish, I will Be satisfied...
			m_timeRange ~= new TimeRange(m_innerTimer, 7500u,  10000u);	// repeat
			m_timeRange ~= new TimeRange(m_innerTimer, 10000u, 12500u);		// cos I was not Able to help you, Yet.
			m_timeRange ~= new TimeRange(m_innerTimer, 12500u, 15000u);  // but you Also, you did Not heip me, too.
			m_timeRange ~= new TimeRange(m_innerTimer, 15000u, 17500u);	// Cos I am FOOLish, I will Be satisfied...
			m_timeRange ~= new TimeRange(m_innerTimer, 17500u, 20000u);	// repeat
				
			// フィルムノイズ用背景描画クラス
			m_filmNoiseBg = filmNoiseBg;
			// 使用する画像
			m_textureLoader = new TextureLoader();
			
			m_currentSection = SECTION.CUT01;
			m_filmNoise = filmNoise;
		}

		/// 初期化処理
		int onInit(Screen screen) {
			try {
				m_textureLoader.loadDefFile(cast(char[]) "img/main_ending/s10/bg.lst");
				m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
				Log.print("%s#onInit : FINISH." , super.toString());
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onInit : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
		/// オブジェクトの解放
		override void destroy() {
			m_textureLoader.releaseAll();
			m_textureLoader.releaseFileList();
			m_finish = false;
			Log.print("%s#destroy : FINISH." , super.toString());
		}

		/// 移動処理	
		int onMove(Screen screen) {
			try {
				if (m_finish) {
					return 0;
				}
				switch (m_currentSection) {
				case SECTION.CUT01:
					m_timeRange[SECTION.CUT01].update();
					if ( m_timeRange[SECTION.CUT01].isAfter() ) {
						m_currentSection = SECTION.CUT02;
						m_imgIndex = 1;
						
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
						screen.setClearColor(0,0,0);
						Log.print("TO CUT02 [%s ms]", m_timeRange[SECTION.CUT01].get());
					}
					break;
				case SECTION.CUT02:
					m_timeRange[SECTION.CUT02].update();
					if ( m_timeRange[SECTION.CUT02].isAfter() ) {
						m_currentSection = SECTION.CUT03;
						m_imgIndex = 2;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),9);
						Log.print("TO CUT03 [%s ms]", m_timeRange[SECTION.CUT04].get());
					}
					break;
				case SECTION.CUT03:
					m_timeRange[SECTION.CUT03].update();
					if ( m_timeRange[SECTION.CUT03].isAfter() ) {
						m_currentSection = SECTION.CUT04;
						m_imgIndex = 3;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO CUT04 [%s ms]", m_timeRange[SECTION.CUT03].get());
					}
					break;
				case SECTION.CUT04:
					m_timeRange[SECTION.CUT04].update();
					if ( m_timeRange[SECTION.CUT04].isAfter() ) {
						m_currentSection = SECTION.CUT05;
						++m_imgIndex;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO CUT04 [%s ms]", m_timeRange[SECTION.CUT04].get());
					}
					break;
				case SECTION.CUT05:
					m_timeRange[SECTION.CUT05].update();
					if ( m_timeRange[SECTION.CUT05].isAfter() ) {
						m_currentSection = SECTION.CUT06;
						++m_imgIndex;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO CUT04 [%s ms]", m_timeRange[SECTION.CUT05].get());
					}
					break;
				case SECTION.CUT06:
					m_timeRange[SECTION.CUT06].update();
					if ( m_timeRange[SECTION.CUT06].isAfter() ) {
						m_currentSection = SECTION.CUT07;
						++m_imgIndex;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO CUT04 [%s ms]", m_timeRange[SECTION.CUT06].get());
					}
					break;
				case SECTION.CUT07:
					m_timeRange[SECTION.CUT07].update();
					if ( m_timeRange[SECTION.CUT07].isAfter() ) {
						m_currentSection = SECTION.CUT08;
						++m_imgIndex;
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						m_innerIndex = 0;
						Log.print("TO CUT04 [%s ms]", m_timeRange[SECTION.CUT07].get());
					}
					break;
				case SECTION.CUT08:
					m_timeRange[SECTION.CUT08].update();
					if ( m_timeRange[SECTION.CUT08].isAfter() ) {
						m_innerIndex = 0;
						m_finish = true;
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						Log.print("SCENE04 END [%s ms]", m_timeRange[SECTION.CUT04].get());
					}
					break;
				default:
					assert(false);
				}

				m_filmNoiseBg.onMove(screen);
				m_filmNoise.onMove(screen);
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onMove : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}

		/// 描画処理	
		int onDraw(Screen screen) {
			try {
				screen.clear();
				Color4ub colorOrg = screen.getColor4ub();
				switch (m_currentSection) {
				case SECTION.CUT01:
				case SECTION.CUT02:
				case SECTION.CUT03:
				case SECTION.CUT04:
				case SECTION.CUT05:
				case SECTION.CUT06:
				case SECTION.CUT07:
				case SECTION.CUT08:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE,screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				default:
					Log.printError("m_currentSection = %s", cast(int) m_currentSection);
					assert(false);
				}

				m_filmNoise.onDraw(screen);
				
				screen.setColor(colorOrg);
				
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onDraw : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
	private:
		enum SECTION : int {CUT01,CUT02,CUT03,CUT04,CUT05,CUT06,CUT07,CUT08};
		static const uint BASE_RANGE = 5000u;
		
		SECTION m_currentSection;
		TimeRange[] m_timeRange;
		TextureLoader m_textureLoader;
		int m_imgIndex;
		FilmNoiseBgDraw m_filmNoiseBg;
		FilmNoise m_filmNoise;
		
		int m_innerIndex;
	}
	
	/** シーン11 ボーカル歌詞 */
	private static final class Scene11 : SceneBase {
	public :
		/// コンストラクタ
		/// 必要なものをすべて強制
		this(FixTimer grobalTimer, FilmNoise filmNoise, 
				FilmNoiseBgDraw filmNoiseBg, Sound bgm) {
			super(grobalTimer);
			m_timeRange ~= new TimeRange(m_innerTimer, 0u,     10000u);	// Are you reciving this?
			m_timeRange ~= new TimeRange(m_innerTimer, 10000u, 25000u);	// Are you reciving this?
			m_timeRange ~= new TimeRange(m_innerTimer, 25000u, 40000u); // but you Also, you did Not heip me, too.
			m_timeRange ~= new TimeRange(m_innerTimer, 40000u, 43000u);	// Cos I am FOOLish, I will Be satisfied...
				
			// フィルムノイズ用背景描画クラス
			m_filmNoiseBg = filmNoiseBg;
			// 使用する画像
			m_textureLoader = new TextureLoader();
			
			m_currentSection = SECTION.CUT01;
			m_filmNoise = filmNoise;
			
			m_bgm = bgm;
			m_cameraWork = new CameraWork(cast(char[]) "img/main_ending/s11/cw/list.lst", 2, 2);
			m_cameraWork.loadFlow(cast(char[]) "img/main_ending/s11/cw/mouse.txt");
			
			m_alpha = new RootCounterS();
			m_alpha.set(255, 0, 4);
			m_imgIndex = 1;
		}

		/// 初期化処理
		int onInit(Screen screen) {
			try {
				m_textureLoader.loadDefFile(cast(char[]) "img/main_ending/s11/bg.lst");
				m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
				Log.print("%s#onInit : FINISH." , super.toString());
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onInit : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
		/// オブジェクトの解放
		override void destroy() {
			m_textureLoader.releaseAll();
			m_textureLoader.releaseFileList();
			m_finish = false;
			Log.print("%s#destroy : FINISH." , super.toString());
		}

		/// 移動処理	
		int onMove(Screen screen) {
			try {
				if (m_finish) {
					return 0;
				}
				switch (m_currentSection) {
				case SECTION.CUT01:
					m_timeRange[SECTION.CUT01].update();
					if ( m_timeRange[SECTION.CUT01].isAfter() ) {
						m_currentSection = SECTION.CUT02;
						m_imgIndex = 0;
						
						// ノイズを入れる
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(),8);
						screen.setClearColor(0,0,0);
						Log.print("TO CUT02 [%s ms]", m_timeRange[SECTION.CUT01].get());
					}
					break;
				case SECTION.CUT02:
					m_timeRange[SECTION.CUT02].update();
					if ( m_timeRange[SECTION.CUT02].isAfter() ) {
						m_currentSection = SECTION.CUT03;
						m_imgIndex = 2;
						Log.print("TO CUT03 [%s ms]", m_timeRange[SECTION.CUT02].get());
					}
					break;
				case SECTION.CUT03:
					m_timeRange[SECTION.CUT03].update();
					if ( m_timeRange[SECTION.CUT03].isAfter() ) {
						m_currentSection = SECTION.CUT04;
						m_bgm.stop();
						m_filmNoise.getManager().setRate(3.0f);
						Log.print("TO CUT04 [%s ms]", m_timeRange[SECTION.CUT03].get());
					}
					break;
				case SECTION.CUT04:
					m_cameraWork.onMove(screen);
					m_alpha.inc();
					m_timeRange[SECTION.CUT04].update();
					if ( m_timeRange[SECTION.CUT04].isAfter() ) {
						m_innerIndex = 0;
						m_finish = true;
						m_filmNoiseBg.noiseDrawNow(20 * getSignRand(), 10);
						Log.print("SCENE04 END [%s ms]", m_timeRange[SECTION.CUT04].get());
					}
					break;
				default:
					assert(false);
				}

				m_filmNoiseBg.onMove(screen);
				m_filmNoise.onMove(screen);
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onMove : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}

		/// 描画処理	
		int onDraw(Screen screen) {
			try {
				screen.clear();
				Color4ub colorOrg = screen.getColor4ub();
				switch (m_currentSection) {
				case SECTION.CUT01:
				case SECTION.CUT02:
				case SECTION.CUT03:
					m_filmNoiseBg.onDraw(cast(DrawAssist.Context) DrawAssist.SCREEN_SIZE,screen, 
							m_textureLoader.get(m_imgIndex), 0, 0);
					break;
				case SECTION.CUT04:
					screen.setColor(255,255,255,m_alpha.get());
					m_cameraWork.onDraw(screen);
					break;
				default:
					Log.printError("m_currentSection = %s", cast(int) m_currentSection);
					assert(false);
				}

				m_filmNoise.onDraw(screen);
				
				screen.setColor(colorOrg);
				
				return 0;
			} catch (Exception e) {
				Log.printError("Exception %s#onDraw : [%s] [%s]", 
					super.toString(), e.toString(), e.msg);
				throw e;
			}
		}
		
	private:
		enum SECTION : int {CUT01,CUT02,CUT03,CUT04};
		static const uint BASE_RANGE = 5000u;
		
		SECTION m_currentSection;
		TimeRange[] m_timeRange;
		TextureLoader m_textureLoader;
		int m_imgIndex;
		FilmNoiseBgDraw m_filmNoiseBg;
		FilmNoise m_filmNoise;
		Sound m_bgm;
		CameraWork m_cameraWork;
		
		RootCounterS m_alpha;
		
		int m_innerIndex;
		
	}	

private:
	static const uint S01_START_TIME = 0u;	//!< シーン０１ 
	static const uint S02_START_TIME = 0u; 	//!< シーン０２
	static const uint S03_START_TIME = 0u; 	//!< シーン０３
	static const uint S04_START_TIME = 0u; 	//!< シーン０４
	static const uint S05_START_TIME = 0u; 	//!< シーン０５
	static const uint S06_START_TIME = 0u; 	//!< シーン０６
	static const uint S07_START_TIME = 0u; 	//!< シーン０７
	static const uint S08_START_TIME = 0u; 	//!< シーン０８
	static const uint S09_START_TIME = 0u; 	//!< シーン０９
	static const uint S10_START_TIME = 0u; 	//!< シーン１０
	static const uint S11_START_TIME = 0u; 	//!< シーン１１


	GameInfo info;
	bool m_init;
	bool m_loading;
	bool m_finish;
	
	SceneBase[] m_allScene;
	int m_currentScene;
	
	Sound m_bgm;
	FixTimer m_timer;
	
	
	FilmNoise m_filmNoise;
	FilmNoiseBgDraw m_filmNoiseBg;
}
