module DarkHall
	
	class InterflowPhase < PartySelectPhase
		def on_enter(from)
			super
		end

		def information_text
			_("どのパーティーと合流しますか？")
		end

		
		def showing_windows
			[DUNGEON_WINDOW, PARTY_WINDOW, WARNING_WINDOW, SELECT_WINDOW]
		end

		
		def showing_cancel?
			true
		end
		
		def on_change
			super
			if SELECT_WINDOW.current_item.disabled? or SELECT_WINDOW.current_id == :cancel then
				DUNGEON_WINDOW.hide
			else
				DUNGEON_WINDOW.update.show
				SELECT_WINDOW.show
			end
		end
		
		def on_select(id)
			target = GS.dungeon_parties[id]
			GS.party.x = target.x
			GS.party.y = target.y
			GS.party.floor_id = target.floor_id
			
		end
		
		def on_cancel
			GS.party = GS.dungeon_parties[@original]
			PARTY_WINDOW.update
			DUNGEON_WINDOW.update
			Phase.change(CampPhase.new)
		end
	end




	class DungeonStartPhase < Phase
	
		def on_enter(from)
			super
			
			GS.party.floor_id = '1'
			GS.party.x = 10
			GS.party.y = 1
			GS.party.direction = DIR_S
			
			BGM.load(:Camp, GS.party.floor.bgm, :Battle)
			BGM.play(:Camp)

			INFORMATION_WINDOW.align = AL_CENTER
			INFORMATION_WINDOW.make_surface(250).update(_('Entering The DarkHall')).centering
			$windows = [PARTY_WINDOW, INFORMATION_WINDOW]
			Game.wait(5)
			

			Game.reporter.on_adventure_start(false)

			sec = Benchmark.realtime{
				DUNGEON_WINDOW.make_surface.centering
			}
			Game.save(FQ_BIG_CHANGE, 'ダンジョンに突入')
			rest = (2.0 - sec) * 1000.0
			if rest > 0 then
				SDL.delay(rest)
			end
			DUNGEON_WINDOW.update
			$section = DUNGEON
			Phase.change(CampPhase.new)
		end
	end
	
	class DungeonRestartPhase < Phase
		def on_enter(from)
			super

			BGM.load(:Camp, GS.party.floor.bgm, :Battle)
			BGM.play(:Camp)

			INFORMATION_WINDOW.align = AL_CENTER
			INFORMATION_WINDOW.make_surface(250).update(_('Returning to The DarkHall')).centering
			$windows = [PARTY_WINDOW, INFORMATION_WINDOW]
			Game.wait(5)
			


			
			Game.reporter.on_adventure_start(true)

			sec = Benchmark.realtime{
				DUNGEON_WINDOW.make_surface.centering
			}
			Game.save(FQ_BIG_CHANGE, 'ダンジョンに突入')
			rest = (2.0 - sec) * 1000.0
			if rest > 0 then
				SDL.delay(rest)
			end
			$section = DUNGEON
			Phase.change(CampPhase.new)
		end
	end

	
	
	class CampPhase < Phase
		def on_enter(from)
			super

			BGM.play(:Camp)
			
			items = SELECT_WINDOW.select_items.clear
			items << SelectItem.new(:status, _('ステータス(S)'), SDL::Key::S)
			items << SelectItem.new(:change, _('パーティー切り替え(C)'), SDL::Key::C)
			items.last.disable if GS.dungeon_parties.size <= 1
			items << SelectItem.new(:organize, _('パーティー再編成(O)'), SDL::Key::O)
			
			items << SelectItem.new(:intermit, _('冒険の中断(B)'), SDL::Key::B)
			items << SelectItem.new(:quicksave, _('クイックセーブ(Q)'), SDL::Key::Q)
			items << SelectItem.new(:cancel, _('探索に戻る'))
			items << SelectItem.new(:debug, "＊デバッグメニュー") if Game.debug_mode?

			SELECT_WINDOW.display_max = nil
			SELECT_WINDOW.make_surface(200, items.size).centering.reset_index.warp
			
			guide = _('F1～F4キーでシステムメニューを表示')
			HELP_WINDOW.make_surface(guide, 1).update([guide])
			HELP_WINDOW.dock_beside(PARTY_WINDOW, :top)
			HELP_WINDOW.right = PARTY_WINDOW.right
			
			$windows = [DUNGEON_WINDOW, PARTY_WINDOW, HELP_WINDOW, SELECT_WINDOW]
		end

		
		def on_select(id)
			case id
			when :status
				$member_index = Game.member_select(_("だれの？"))
				if $member_index then
					Phase.change(StatusPhase.new)
				else
					SELECT_WINDOW.show.warp
				end
			when :change
				Phase.change(PartyChangePhase.new)
			when :organize
				Phase.change(PartyOrganizePhase.new)
			when :intermit
				BGM.fade_out
				name = GS.party.name
				GS.change_to_new_party
				SELECT_WINDOW.hide
				Game.save(FQ_BIG_CHANGE, "#{name}の冒険を中断")
				Game.reporter.on_adventure_end(true)


				message(_('冒険を中断します……'))
				Phase.change(TownMenuPhase.new)
			when :quicksave
				Game.now_saving{
					Game.save(FQ_BIG_CHANGE, "クイックセーブ")
				}

				message(_("現在の状態をセーブしました"))
			when :debug
				Phase.change(CampDebugPhase.new)
			when :cancel
				on_cancel
			end
		end
		
		def on_cancel
			Phase.change(DungeonPhase.new)
		end
	end
	
	class CampDebugPhase < Phase
		def on_enter(from)
			super
			
			
			items = SELECT_WINDOW.select_items.clear
			items << SelectItem.new(:switch, "スイッチ操作")
			items << SelectItem.new(:buy, 'アイテム購入')
			items << SelectItem.new(:battle, "戦闘テスト")
			items << SelectItem.new(:treasure, "宝箱テスト")
			items << SelectItem.new(:encount_down, "白の香")
			items << SelectItem.new(:dungeon_reload, "ダンジョン再ロード")
			items << SelectItem.new(:dungeon_map, "ダンジョンマップ")
			items << SelectItem.new(:set_merchant_products, "デミトリの品物入れ替え")
			items << SelectItem.new(:set_troops, "部屋の敵の再配置")
			items << SelectItem.new(:delete_troops, "部屋の敵を全て消去")
			items << SelectItem.new(:tereport, "テレポート")
			items << SelectItem.new(:cancel, "キャンセル")

			SELECT_WINDOW.make_surface(200, 6)
			SELECT_WINDOW.display_max = 6
			SELECT_WINDOW.centering.reset_index.warp
			
			$windows = [DUNGEON_WINDOW, PARTY_WINDOW, SELECT_WINDOW]
		end

		
		def on_select(id)
			case id
			when :switch
				items = []
				GS.switch.keys.each do |key|
					items << SelectItem.new(key, key.to_s, nil, GS.switch[key].to_s)
				end
				key = Game.select(items){|window|
					window.make_surface(300)
				}
				unless key == :cancel then
					GS.switch[key] = (GS.switch[key] ? false : true)
				end
			when :buy
				$member_index = 0
				Phase.change(DebugBuyPhase.new)
				Game.operation_loop('Buying')
				Phase.change(CampDebugPhase.new)

				
				
			when :battle
				items = []
				DB.enemies.each do |model|
					items << SelectItem.new(model.id, model.name)
				end
				items << SelectItem.new(:start, "戦闘開始")

				items.last.disable
				win = TextWindow.new.make_surface(240, 4)
				win.set_position(8, 8)
				win.update.show
				
				id = nil
				list = []
				loop do
					
					id = Game.select(items){|w|
						w.set(id) if id
						w.dock_beside(win, :bottom)
					}
					case id
					when :cancel
						if list.empty? then
							win.hide
							break
						else
							#troop.groups.pop
							list.pop
							items.last.disable if list.empty?
						end
					when :start
						if list.include?('RuinFeather') or list.include?('LostArmor') or list.include?('MalorJin') then
							troop = BossTroop.new
						else
							troop = WanderingTroop.new
						end
						troop.add_enemies(list)
						Game.battle(troop)
						break
					else
						#troop.add_enemies(id)
						list << id
						items.last.enable
					end
					
					win.update(list.map{|x| DB.find_enemy(x).name})
				end
			when :treasure
				BGM.fade_out
				Game.challange_treasure(TreasureBox.new(rand(6) + 1, nil))
			when :dungeon_reload
				Dungeon.load_floors
				DUNGEON_WINDOW.update
				message("ダンジョンマップファイルを再読み込みしました")
			when :encount_down
				msgs = ITEM_DATA[:WhiteIncense].effect.operate(GS.party.standing_members.first, nil, nil)
				message(*msgs)

			when :dungeon_map
				Phase.change(DungeonMapEditPhase.new)
			when :set_troops
				Game.set_room_events
				message("各部屋の敵を再配置しました")
			when :delete_troops
				GS.room_troops[GS.party.floor_id].clear
				message("現在の階にいる部屋の敵を\n全て消去しました")
			when :tereport
				effect = MemberTereportEffect.new
				message(effect.operate(GS.party.members))
			when :set_merchant_products
				Game.set_merchant_products
			when :cancel
				on_cancel
			end
			
			PARTY_WINDOW.update
		end
		
		def on_cancel
			Phase.change(CampPhase.new)
		end
	end



	class PartyOrganizePhase < Phase
		include OrderChangePhase
		def on_enter(from)
			super
			GS.party.layering_parties.each do |party|
				@old_members += party.members
			end
			self.update_windows
			
			@new_order_window.show
			@old_order_window.show.warp
		end
		
		def on_leave(to)
			# 現在のパーティーに入っている冒険者が、他パーティーにも重複して登録されていないかどうかチェック
			GS.party.layering_parties.each do |other|
				other.member_ids -= GS.party.member_ids
			end
			
			GS.parties.delete_if{|x| x.members.empty?}
			
		end

		def on_cancel
			super
			if @new_members.empty? then
				SELECT_WINDOW.set(:organize).warp
			end
		end

		
		def on_select(id)
			if id == :decide then
				finish
			else
				@new_members << @old_members[id]
				
				if @new_members.size >= Party::MEMBER_MAX then
					finish
				else
					self.update_windows
				end
			end
			
		end
		
		
		def finish
			# 新パーティーメンバー確定
			GS.party.member_ids = @new_members.map{|x| GS.members.index(x)}
			
			rests = self.rest_members
			
			# 他のパーティーにすでに加入済みの冒険者は、restsに含めない
			# （新しいパーティーを作らせない）
			GS.party.layering_parties.each do |other|
				rests.each do |rest|
					rest_id = GS.members.index(rest)
					other.member_ids.delete(rest_id)
				end
			end
			
			# 余った冒険者で新しいパーティーを結成
			until rests.empty? do
				new = Party.new
				new.member_ids = rests.slice!(0..5).map{|x| GS.members.index(x)}
				new.set_position(GS.party.x, GS.party.y, GS.party.direction)
				new.floor_id = GS.party.floor_id
				
				GS.parties << new
			end
			
			Phase.change(CampPhase.new)
			Game.save(FQ_CHANGE, '隊列変更')
			SELECT_WINDOW.set(:organize).warp
		end


		
		def update_windows
			super
			@old_order_window.select_items << SelectItem.new((@new_members.empty? ? nil : :decide), _("確定(D)"), SDL::Key::D)
			@old_order_window.regulate_index.update
		end

	end
	
	
	
	
	class PartyChangePhase < PartySelectPhase
		def on_enter(from)
			@original_party_id = GS.current_party_id
			super
		end

		def information_text
			_("どのパーティーに切り替えますか？")
		end

		
		def showing_windows
			[DUNGEON_WINDOW, PARTY_WINDOW, WARNING_WINDOW, SELECT_WINDOW]
		end

		
		def showing_cancel?
			false
		end
		
		def on_change
			super
			if SELECT_WINDOW.current_item.disabled? or SELECT_WINDOW.current_id == :cancel then
				DUNGEON_WINDOW.hide
			else
				DUNGEON_WINDOW.update.show
				SELECT_WINDOW.show
			end
		end
		
		def on_select(id)
			GS.current_party_id = id

			# 選択したパーティーを1番目に順序変更（次のパーティー選択時の利便性のため）
			GS.move_party_to_first(id)
			
			Phase.change(CampPhase.new)
			SELECT_WINDOW.set(:change).warp
		end
		
		def on_cancel
			GS.current_party_id = @original_party_id
			PARTY_WINDOW.update
			DUNGEON_WINDOW.update
			Phase.change(CampPhase.new)
			SELECT_WINDOW.set(:change).warp
		end
	end
	

	

	
	
	
	class DungeonPhase < Phase
		def on_enter(from)
			$section = DUNGEON
		
			super
			unless from.kind_of?(MerchantBuyPhase) then
				BGM.play(GS.party.floor.bgm)
			end
			@other_party_window = TextWindow.new
			@encount_window = TextWindow.new.make_surface(Game.char_width*20 + PADDING*2)
			@encount_window.align = AL_CENTER
			@encount_window.dock_beside(PARTY_WINDOW, :top)
			@treasure_window = TextWindow.new.make_surface(@encount_window.width, 1, AL_CENTER)
			@treasure_window.dock_beside(@encount_window, :top)
			@treasure_window.align = AL_CENTER

			@special_action_window = SelectableWindow.new
			@special_action_window.make_surface(160, 4).dock_beside(DUNGEON_WINDOW, :right)
			@special_action_window.display_max = 4
			
			@help_window = SmallTextWindow.new
			@help_window.has_frame = false
			@help_window.text = _('[1]キーで操作説明を表示')
			@help_window.set_good_width.make_surface(nil, 1, AL_RIGHT).update
			@help_window.dock_beside(PARTY_WINDOW, :top_right, 0)
			
			#@action_help_window = SmallTextWindow.new
			#@action_help_window.has_frame = false
			#@action_help_window.align = AL_RIGHT
			#@action_help_window.make_surface(@special_action_window.width).update(_('ACTION: [A] key'))
			#@action_help_window.dock_beside(@special_action_window, :bottom, 0)
			#@action_help_window.right = @special_action_window.right


			
			$windows = [PARTY_WINDOW, @help_window, @special_action_window, DUNGEON_WINDOW]
			check_status

			DUNGEON_WINDOW.update
		end
		
		def hide_sub_windows
			@help_window.hide
			@other_party_window.hide
			@encount_window.hide
			@treasure_window.hide
			@special_action_window.hide
		end
		
		def show_sub_windows
			@special_action_window.show
			DUNGEON_WINDOW.show
		end
		
		def check_status
			self.check_other_parties
			self.check_treasure
			self.check_encount
			self.check_actions

			DUNGEON_WINDOW.show
		end
		
		def on_right_key
 			return unless movable?
			if Input.side_walk_shifted? then
				on_right_walk_key
			else
				GS.party.turn_right
				turn_sequence
			end
		end
		
		def on_left_key
 			return unless movable?
			if Input.side_walk_shifted? then
				on_left_walk_key
			else
				GS.party.turn_left
				turn_sequence
			end

		end
		
		def on_up_key
 			return unless movable?
			old_region = GS.party.region
			GS.party.forward_walk
			walk_sequence(GS.party.direction, old_region, false)
		end
		
		def on_enter_key
			if Game.current_window.kind_of?(DungeonWindow) then
	 			return unless movable?
				old_region = GS.party.region
				GS.party.forward_walk
				walk_sequence(GS.party.direction, old_region, true)
			else
				super
			end
			#@special_action_window.show.reset_index.warp
		end

		
		def on_down_key
 			return unless movable?
			unless Input.side_walk_shifted? then
				if GS.game_config.back_turn then
					GS.party.turn_back
					turn_sequence
				end
			end
		end
		
		
		def on_left_click(x, y)
			if not Game.current_window == @special_action_window and @special_action_window.mouse_on_window?(x, y) then
				SE.select
				@special_action_window.show.regulate_index.warp
			elsif on_dungeon_window? then
				if DUNGEON_WINDOW.mouse_on_window?(x, y) then
					on_up_key
				end
			else
				Game.current_window.on_enter_key
			end
		end
		
		def on_right_drag
			on_right_key
		end
		
		def on_left_drag
			on_left_key
		end
		
		def on_down_drag
			on_down_key
		end

		
		def on_left_walk_key
 			return unless movable?
			old_region = GS.party.region
			GS.party.left_walk
			walk_sequence(GS.party.left_direction, old_region)
		end
		
		def on_right_walk_key
 			return unless movable?
			old_region = GS.party.region
			GS.party.right_walk
			walk_sequence(GS.party.right_direction, old_region)
		end
		

		
		def on_shift_down
			if on_special_action_window? then
				SE.select
				DUNGEON_WINDOW.show
			elsif on_dungeon_window? then
				SE.select
				@special_action_window.show.regulate_index.warp
			end
		end
	
		def on_key_press(keycode)
			if on_dungeon_window? then
				case keycode
				when SDL::Key::K1
					SE.select
					
					Phase.change(DungeonHelpPhase.new)
					
				when SDL::Key::M
					SE.select
					Phase.change(DungeonMapEditPhase.new) if Game.debug_mode?
				else
					if (item = @special_action_window.select_items.find{|x| x.keycode == keycode}) then
						SE.select
						on_select(item.id)
					end
				end
			end
		end
		
		def walk_sequence(dir, old_region, by_decide_key = false)
			party = GS.party
			floor = party.floor
			to_x, to_y = party.next_point(dir)
			
			party.walk(dir)
		
			case floor.wall(party.x, party.y, dir)
			when Dungeon::Area::DOOR
						
				# 前に歩いてないとドアは開けられない
				if dir == party.direction and (not GS.game_config.wizardry_like_door_open? or by_decide_key) then
					lock_id = floor.get_lock_id(party.x, party.y, dir)
					stair_data = floor.get_stair_data(party.x, party.y, dir)
				
					# on_door イベントの返り値が偽なら開かない
					re = floor.on_door(party)
					unless re then
						return
					end
				
					if lock_id and not GS.unlocked_door_data[party.floor_id][lock_id] then
						# 鍵がかかっている場合
						lock_type = floor.lock_types[lock_id].to_s
					
						holder = party.find_item_holder(lock_type)
						if holder then
							SE.equip
							msg = _("この扉には特殊な鍵がかかっている\n$c[item]%{key}$cを使って扉を開ける？").evaluate(:key => DB.find_item(lock_type.to_s).name)
							if Game.ask(msg, :silent => true) then
								SE.unlock
								GS.unlocked_door_data[party.floor_id][lock_id] = true
								Game.message(_("%{holder}は鍵を使って扉を開けた").evaluate(:holder => holder.name))
							else
								SE.cancel
							end
						else
							SE.equip
							if lock_type == :KeyOfWizard then
								Game.message(_("この扉には特殊な鍵がかかっている\n開けるには$c[item]%{key}$cが必要だ\n\n（注意：%{key}はβ版では手に入りません）").evaluate(:key => DB.find_item(lock_type.to_s).name))
							else
								Game.message(_("この扉には特殊な鍵がかかっている\n開けるには$c[item]%{key}$cが必要だ").evaluate(:key => DB.find_item(lock_type.to_s).name))
							end
						end
					elsif not floor.passable_door?(party.x, party.y, dir) then
						# 一方通行のドアで、通行できない側から入ろうとした場合
						SE.equip
						Game.message(_("この扉は向こう側からしか開けられないようだ"))
					elsif stair_data then
						# 階段がある場合
						SE.door
						to_floor_id, to_x, to_y, to_dir = stair_data
						DUNGEON_WINDOW.hide
						hide_sub_windows
						
						if Game.ask(_("階段がある\n次の階層に移動しますか？"), :silent => true) then
							SE.select
							jump_to(to_floor_id, to_x, to_y, to_dir, '階段')							
							
						else
							SE.door
							DUNGEON_WINDOW.update.show
						end
						show_sub_windows
					else
						SE.door
						party.back_x = party.x
						party.back_y = party.y
						party.x = to_x
						party.y = to_y
						party.loop_sequence
						party.encount_border_up
					end
					
				else
					party.walk_to_wall
				end
			
			when Dungeon::Area::EXIT_DOOR
				if dir == party.direction and (not GS.game_config.wizardry_like_door_open? or by_decide_key) then
					SE.door
					DUNGEON_WINDOW.hide
					hide_sub_windows
					
					if Game.ask(_("迷宮を離れて街に戻ります。よろしいですか？"), :silent => true) then
						SE.select
						BGM.fade_out
						Game.reporter.on_adventure_end(false)
						
						party = GS.party
						GS.parties.delete_if{|x| x.member_ids.empty?}
						GS.current_party_id = GS.parties.index(party)
						
						
						msg = _("まだ迷宮内に残っているパーティーがいます\nほかのパーティーの冒険を再開しますか？\n（再開しない場合は、このまま宿屋に向かいます）")
						if GS.dungeon_parties.find_all{|x| x.alive?}.size >= 2 then
							if Game.ask(msg, :silent => true) then
								SE.select
								party.members.each{|x| x.added_status.clear}
								GS.bar_member_ids += party.member_ids
								GS.parties.delete(party)
								GS.current_party_id = 0
							
								Phase.change(DungeonPhase.new)
								return
							else
								SE.select
							end
						end
							

						Game.message(_("町に帰還します……"))
						party.reset_encount_border
						party.members.each{|x| x.added_status.clear}
						party.floor_id = nil
						
						returners = GS.bar_members + party.members
						returners.each do |member|
							spell_learned = false
							member.items.each_with_index do |item, i|
								if item.data.kind_of?(SpellBookModel) then
									unless item.spell_id.in?(GS.got_spell_ids) then
										member.lose_item(i)
										GS.got_spell_ids << item.spell_id
										Game.message(_("%{holder}が呪文書を持ち帰ったため\n新しく$c[item]%{spell}$cを習得できるようになりました！").evaluate(:holder => member.name, :spell => DB.find_spell(item.spell_id).name))
										spell_learned = true
										break
									end
								end
							end
							
							redo if spell_learned
							
							if member.find_item('GlimWand') and not GS.switch['BetaScenarioClear'] then
								GS.switch['BetaScenarioClear'] = true
								Game.message _("β版のシナリオをクリアしたため\n冒険者ギルドから「BGM演奏」を行えるようになりました")
								
								if GS.starting_game_version_code then
									GS.switch['RuleSelectable'] = true
									Game.message _("β版のシナリオをクリアしたため\n新しいセーブデータを作成して、冒険を始めるときに\nゲームのルールを選択できるようになりました")
								end
							end
						end
						
						Phase.change(InnMenuPhase.new)
						return
					else
						SE.door
						show_sub_windows
	
					end
				else
					party.walk_to_wall
				end

			when Dungeon::Area::ROOM, Dungeon::Area::ROUTE
				SE.step
				party.back_x = party.x
				party.back_y = party.y
				party.x = to_x
				party.y = to_y
			
				party.loop_sequence
				party.encount_border_up
			when Dungeon::Area::WALL
				party.walk_to_wall
			end

			DUNGEON_WINDOW.update
			floor.special_detect_caption = nil
			floor.special_action_data.clear

			
			floor.find(GS.party.x, GS.party.y)
			floor.on_enter_region(GS.party, old_region) unless GS.party.region == old_region
			floor.on_walk(GS.party)
			floor.on_move(GS.party)

			
			check_status

			
			# エンカウント処理
			if GS.party.alarm_type == TT::Alarm then

				group1 = GS.party.floor.pick_enemy_list(GS.party.region) || []
				group2 = GS.party.floor.pick_enemy_list(GS.party.region) || []
				list = group1 + group2
				unless list.empty? then
					GS.party.alarm_type = nil
					GS.party.reset_encount_border
					troop = WanderingTroop.new
					troop.add_enemies(list)
					LOGGER.log(RandomEncountLog, $troop)
					
					Game.battle(troop)
				end
			elsif GS.party.section_id && GS.room_troops[GS.party.floor_id][GS.party.section_id] then
				# 部屋エンカウント
				GS.party.reset_encount_border
				troop = GS.room_troops[GS.party.floor_id][GS.party.section_id]
				Game.battle(troop)
			elsif GS.party.encount_border <= 80 && Dice.new.roll(GS.party.encount_border) <= FAIL then
				# ランダムエンカウント
				list = GS.party.floor.pick_enemy_list(GS.party.region)
				if list then
					GS.party.reset_encount_border
					troop = WanderingTroop.new

					troop.add_enemies(list)
					LOGGER.log(RandomEncountLog, $troop)
					
					Game.battle(troop)
				end
			end
		end
		
		def turn_sequence
			GS.party.floor.special_detect_caption = nil
			GS.party.floor.special_action_data.clear
			DUNGEON_WINDOW.update
			GS.party.floor.on_move(GS.party)
			check_actions
		end
		
		def jump_to(to_floor_id, to_x, to_y, to_dir = nil, save_caption = 'ジャンプ')
			party = GS.party
			floor = party.floor
			
			to_floor_id ||= party.floor_id # nilなら同じフロア内での移動
			
			
			# 画面消去
			DUNGEON_WINDOW.hide
			hide_sub_windows
		
			dummy = Window.new # PartyWindowにフォーカスが当たるのを避けるためのダミー
			dummy.show.make_surface
			
			Game.force_refresh
			

			# 座標移動＆セーブ処理
			party.back_x = nil
			party.back_y = nil
			party.floor_id = to_floor_id
			party.x = to_x
			party.y = to_y
			party.direction = to_dir if to_dir
			party.reset_encount_border
			
			# 新しい階層ならlatest_floor_idを更新（判定は文字列比較で行う）
			if GS.latest_floor_id < to_floor_id then
				GS.latest_floor_id = to_floor_id
				GS.player_records[to_floor_id] ||= {}
			end
			

			Game.save(FQ_BIG_CHANGE, "#{save_caption} / F:#{to_floor_id} / (#{to_x}, #{to_y}), #{to_dir}")
			


			# BGMフェードアウト（変更される場合のみ）
			to_bgm = FLOORS[to_floor_id].bgm
			unless floor.bgm == to_bgm then
				BGM.fade_out
				BGM.load(:Camp, to_bgm, :Battle, :NPC1, :NPC2)
			end
			
			# 画面更新＆待ち時間
			Game.wait(30)
			
			# 画面表示
			DUNGEON_WINDOW.update.show
			show_sub_windows
			
			dummy.hide
			
			BGM.play(to_bgm)
			
			
			#caption = TextWindow.new.make_surface(150, 1)
			#caption.align = AL_CENTER
			#caption.update(floor.name).centering.show
			#Game.wait(30)
			#caption.hide

		end
		
		def check_other_parties
			others = GS.party.layering_parties
			if others.empty? then
				@other_party_window.hide
			else
				params = {:other_party => others.first.name}
				if others.first.alive? then
					@other_party_window.text = eval_text(_('%{other_party}がいます'), params)
				else
					@other_party_window.text = eval_text(_('%{other_party}が倒れています'), params)
				end
				@other_party_window.set_good_width.make_surface(nil, nil, AL_CENTER).update.set_position(8, 8)
				@other_party_window.show
				return
			end
		end
		
		def check_treasure
			if GS.party.viewable_treasure_box then
				@treasure_window.update(_("宝箱がある")).show
			else
				@treasure_window.hide
			end
		end

		
		def check_encount
			if GS.party.alarm_type == TT::Alarm then
				@encount_window.update(_("敵が近づいてくる")).show
			elsif GS.party.encount_up_rest >= 1 then
				@encount_window.update(_("黒の香")).show
			elsif GS.party.encount_down_rest >= 1 then
				@encount_window.update(_("白の香")).show
			elsif GS.party.encount_border <= 83 then
				if Game.debug_mode? then
					@encount_window.update(_("敵の気配がする") + GS.party.encount_border.to_s).show
				else
					@encount_window.update(_("敵の気配がする")).show
				end
				
			else
				if Game.debug_mode? then
					@encount_window.update("Border:#{GS.party.encount_border}").show
				else
					@encount_window.hide
				end
			end
		end
		
		def check_actions
			items = @special_action_window.select_items.clear
			
			if (cap = GS.party.floor.special_detect_caption) then
				items << SelectItem.new(:special_detect, "[D] #{cap}", SDL::Key::D)
			else
				items << SelectItem.new(:detect, _("[D] 調べる"), SDL::Key::D)
			end
			
			if GS.party.viewable_treasure_box then
				items << SelectItem.new(:treasure, _("[T] 宝箱を調べる"), SDL::Key::T)
			end
			
			if GS.party.floor.wall(GS.party.x, GS.party.y, GS.party.direction) == Dungeon::Area::DOOR then
				items << SelectItem.new(:check_room, _("[H] 聞き耳を立てる"), SDL::Key::H)
			end
			
			GS.party.floor.special_action_data.each do |id, caption, shortcut|
				items << SelectItem.new(id, caption, shortcut)
			end
			
			@special_action_window.update
		end
		
		
		def on_select(id)
			case id
			when :detect
				unless GS.party.floor.on_detect(GS.party) then
					message(_("%{detector}は辺りを調べた\n\nとくに何も見つからなかった").evaluate(:detector => GS.party.members.first.name))
					DUNGEON_WINDOW.show
				end

			when :check_room
				fx, fy = GS.party.forward_point
				section_id = GS.party.floor.cells[fx][fy].section_id
				checker = GS.party.members.find{|x| x.kind_of?(Explorer)} || GS.party.members.first
				troop = GS.room_troops[GS.party.floor_id][section_id] if section_id
				
				if section_id && troop then
					border = 50
					troop.groups.each do |group|
						border += (group.size * 2)
					end
					border += 300 if checker.kind_of?(Explorer)
					LOGGER.puts "#{checker.name}:聞き耳（成功率#{success_rate(border)}%）"
					
					list = []
					if Roll.check_3d33(border) then
						enemy_ids = troop.groups.map{|x| x.first.data_id}
						enemy_ids.each do |id|
							next if ENEMY_DATA[id].silent?
							if checker.kind_of?(Explorer) and (msg = DB.pick_enemy_sign_message(id)) then
								list << msg
							else
								list << _('扉の向こうに何かの気配を感じる……')
							end
							list.uniq!
						end
						LOGGER.puts "\t成功！"
					else
						LOGGER.puts "\t失敗"
					end
					
					if list.empty? then
						msg = _('扉の向こうには誰もいないようだ')
					else
						msg = list.join("\n")
					end

					
				else
					msg = _('扉の向こうには誰もいないようだ')
				end
				
				message(_("%{checker}は静かに聞き耳を立てた").evaluate(:checker => checker.name) + "\n\n#{msg}")
				DUNGEON_WINDOW.show
			when :treasure
				Game.challange_treasure(GS.party.viewable_treasure_box)
			else
				GS.party.floor.on_special_action(GS.party, id)
			end
			
			
		end
		
		def on_dungeon_window?
			Game.current_window == DUNGEON_WINDOW
		end
		
		def on_special_action_window?
			Game.current_window == @special_action_window
		end
		
		def movable?
			case Game.current_window
			when MessageWindow, @special_action_window
				false
			else
				true
			end
		end
		
		

		
		def on_cancel
			if on_special_action_window? then
				DUNGEON_WINDOW.show
			else
				Phase.change(CampPhase.new)
			end
		end
	end
	
	
	class DungeonHelpPhase < Phase
		include BackablePhase
		
		
		def on_enter(from)
			@page = 1
			
			$windows.clear
			
			title = TextWindow.new
			title.align = AL_CENTER
			title.has_frame = false
			title.text = _('- ダンジョン探索時の操作 -')
			title.make_surface(SCREEN_WIDTH - 8*2).update
			title.set_position(:center, 16).show
			
					
			win1 = TextWindow.new.make_surface(_('キーボード'), 1, AL_CENTER)
			win1.update.dock_beside(title, :bottom, 16).show
			win1.left += 32
			win2 = DoubleTextWindow.new
			win2.has_frame = false
			texts = []
			texts << [_('移動:'), _('←↑→')]
			texts << [_('キャンプメニュー:'), _('キャンセルキー')]
			texts << [_('特殊行動メニュー:'), _('[Shift]')]
			texts << [_('並行に移動:'), _('[Alt] + ←→')]
			win2.texts = texts
			win2.set_good_width.make_surface(240, texts.size).update
			win2.dock_beside(win1, :bottom).show
			
			
			win5 = TextWindow.new.make_surface(_('ゲームパッド'), 1, AL_CENTER)
			win5.update.dock_beside(win1, :right, 32).set_position(320, nil).show
			win6 = DoubleTextWindow.new
			win6.has_frame = false
			texts = []
			texts << [_('移動:'), _('←↑→')]
			texts << [_('キャンプメニュー:'), _('キャンセルキー')]
			
			assign = GS.game_config.key_assign
			if (key = assign['ActionMenu']) then
				texts << [_('特殊行動メニュー:'), _('ボタン%{key}').evaluate(:key => key)]
			end
			
			if (key = assign['SideWalkShift']) then
				texts << [_('並行移動:'), _('ボタン%{key} + ←→').evaluate(:key => key)]
			end
			
			if (key = assign['LeftWalk']) then
				texts << [_('左並行移動:'), _('ボタン%{key}').evaluate(:key => key)]
			end
			
			if (key = assign['RightWalk']) then
				texts << [_('右並行移動:'), _('ボタン%{key}').evaluate(:key => key)]
			end

			win6.texts = texts
			win6.set_good_width.make_surface(280, 6).update
			win6.dock_beside(win5, :bottom).show

		
			win3 = TextWindow.new.make_surface(_('マウス'), 1, AL_CENTER)
			win3.update.dock_beside(win6, :bottom, 32).set_position(win1.left, nil).show
			win4 = DoubleTextWindow.new
			win4.has_frame = false
			texts = []
			texts << [_('前進:'), _('左クリック')]
			texts << [_('左右を向く:'), _('左ボタンを押したまま、マウスを左右に移動')]
			texts << [_('キャンプメニュー:'), _('右クリック')]
			texts << [_('特殊行動メニュー:'), _('右上のウインドウをクリック')]
			win4.texts = texts
			win4.set_good_width.make_surface(nil, 4).update
			win4.dock_beside(win3, :bottom).show
			
					

					


			
			help = TextWindow.new
			help.make_surface(_('決定キーで閉じる'), 1, AL_CENTER).update
			help.set_position(:center, SCREEN_HEIGHT - 60).show
			
			update_sequence
		end
		
		def on_left_key
			SE.cursor_move
			@page = Util.next_index(@page, 3)
			update_sequence
		end
		
		def on_right_key
			SE.cursor_move
			@page = Util.prev_index(@page, 3)
			update_sequence
		end
		
		def update_sequence
			case @page
			when 1, 2, 3
			end
		end
		
		def on_enter_key
			Phase.back
		end
		
		def on_cancel
			Phase.back
		end
	end
	
	class DungeonMapPhase < Phase
		include BackablePhase
		
		def initialize(map_window = nil)
			@map_window = (map_window || DungeonMapWindow.new.make_surface.update.centering)
		end
		
		def on_enter(from)
			$windows = [PARTY_WINDOW, DUNGEON_WINDOW, @map_window]
			
			if GS.party.find_item_holder('CompassOfSpell') then
				@compass_window = TextWindow.new
				@compass_window.has_caption = true
				msg = _("羅針盤\n%{floor}\n座標 %{x}, %{y}\n%{dir}の方角")\
				      .evaluate(:floor => GS.party.floor.name, :x => GS.party.x, :y => GS.party.y, :dir => DIR_NAMES[GS.party.direction])
				width = SCREEN_WIDTH - (@map_window.right + 16 * 2)
				@compass_window.make_surface(width, 4).update(msg).dock_beside(@map_window, :right).show
				@map_window.show
			end

			
			unless from.kind_of?(DungeonMapPhase) then
				@map_window.reset_origin
				@map_window.update
			end
			
			
		end
		
		def on_key_press(keycode)
			case keycode
			when SDL::Key::UP
				@map_window.up_cursor
			when SDL::Key::LEFT
				@map_window.left_cursor
			when SDL::Key::RIGHT
				@map_window.right_cursor
			when SDL::Key::DOWN
				@map_window.down_cursor
			when SDL::Key::ESCAPE
				on_cancel
			end
			@map_window.update
		end
		
		def on_cancel
			throw(:exit_operation_loop)
		end
		
	end
	
	class DungeonMapEditPhase < DungeonMapPhase
		
		def on_enter(from)
			super
			@map_window.all_found = true
			@map_window.make_surface.update
			HELP_WINDOW.make_surface(240, 8).show
			HELP_WINDOW.right = PARTY_WINDOW.right - 8
			HELP_WINDOW.bottom = PARTY_WINDOW.bottom - 8
			update_help
			@map_window.show

		end
		
		def update_help
			texts = []
			if @edit_mode then
				texts << ["編集モード"]
				texts << ['Shift+方向キー', '範囲選択']
				texts << ['[D]+方向キー', 'ドアをセット']
				texts << ['[Z]', '通路をセット']
				texts << ['[R]', '部屋をセット']
				texts << ['Ctrl+[R]', 'リージョン設定']
				texts << ['[A][T]', '部屋の敵出現率/宝箱指定']
				texts << ['Ctrl+[T]', 'テレポートポイント指定']
				
			else
				texts << ['[F]', '未踏破区域の表示/非表示']
				texts << ['[R]', 'ダンジョンデータ再読み込み']
				texts << ['[M]', 'パーティーの位置を移動']
				texts << ['数字キー', 'フロア移動']		
			end
			HELP_WINDOW.update(texts)
		end
		
		def on_enter_key
			fill(Dungeon::Area::ROUTE)

		end
		
		def fill(area_type)
			if @edit_mode then
				if @map_window.ranging? then
					floor = GS.party.floor
					bx, by = @map_window.begin_x, @map_window.begin_y
					case area_type
					when Dungeon::Area::ROOM
						floor.pset_room(bx, by, @map_window.cursor_x, @map_window.cursor_y)
					when Dungeon::Area::ROUTE
						floor.pset_route(bx, by, @map_window.cursor_x, @map_window.cursor_y)
					when Dungeon::Area::WALL
						floor.pset_wall_area(bx, by, @map_window.cursor_x, @map_window.cursor_y)
					else
						floor.pset_flooring_area(area_type, bx, by, @map_window.cursor_x, @map_window.cursor_y)
					end
					
					#sect_id = floor.sections.size # sectionsの最後に追加する
					#floor.cells[bx][by].section_id = sect_id
					#floor.sections[sect_id] = Dungeon::Section.new(floor.flooring(bx, by))
					#floor.section_fill_sequence(bx, by, sect_id)
					floor.update_section_data
				end
				
				@map_window.static_make_surface
			end
		end
		
		def cursor_x
			@map_window.cursor_x
		end
		
		def cursor_y
			@map_window.cursor_y
		end
		
		def on_key_press(keycode)
			SDL::Key.scan
			shift_pressed = SDL::Key.press?(SDL::Key::LSHIFT) || SDL::Key.press?(SDL::Key::RSHIFT)
			ctrl_pressed = SDL::Key.press?(SDL::Key::LCTRL) || SDL::Key.press?(SDL::Key::RCTRL)

			
			case keycode
			when SDL::Key::UP, SDL::Key::RIGHT, SDL::Key::DOWN, SDL::Key::LEFT
				@map_window.clear_begin unless shift_pressed
				
				case keycode
				when SDL::Key::UP
					dir = DIR_N
					method_name = :up_cursor
				when SDL::Key::RIGHT
					dir = DIR_E
					method_name = :right_cursor
				when SDL::Key::DOWN
					dir = DIR_S
					method_name = :down_cursor
				when SDL::Key::LEFT
					dir = DIR_W
					method_name = :left_cursor
				end
				
				if SDL::Key.press?(SDL::Key::D) and @edit_mode then
					SE.select
					GS.party.floor.set_door(@map_window.cursor_x, @map_window.cursor_y, dir)
					@map_window.make_surface
				else
					@map_window.send(method_name)
				end
			when SDL::Key::W
				SE.select
				fill(Dungeon::Area::WALL)
				

			when SDL::Key::A, SDL::Key::T
				if @edit_mode then
					if keycode == SDL::Key::T and ctrl_pressed then
						# テレポートポイント指定
						pt = [cursor_x, cursor_y]
						if pt.in?(GS.party.floor.tereport_points) then
							GS.party.floor.tereport_points.delete([cursor_x, cursor_y])
							SE.equip
						else
							GS.party.floor.tereport_points << [cursor_x, cursor_y]
							SE.equip
						end
						@map_window.make_surface
					
					elsif (section_id = GS.party.floor.cells[cursor_x][cursor_y].section_id) and
					(GS.party.floor.flooring(cursor_x, cursor_y) == Area::ROOM) then
						section = GS.party.floor.sections[section_id]
						
						items = []
						
						case keycode
						when SDL::Key::A
							[:certain, :much, :medium, :little, :very_little, :nothing].each do |sym|
								items << SelectItem.new(sym, "#{EA_TABLE[sym]}%")
							end
							items << SelectItem.new(:cancel, _('キャンセル'))
							ea = Game.select(items){|win|
								win.set(section.enemy_appearance)
							}
							unless ea == :cancel then
								GS.party.floor.sections[section_id].enemy_appearance = ea
							end
						when SDL::Key::T
						
							[TRT::WEAPON, TRT::ARMOR, TRT::ACCESSORY, TRT::ITEM, TRT::POTION, TRT::BIG_GOLD].each do |type|
								items << SelectItem.new(type, type.to_s)
							end
							items << SelectItem.new(TRT::NOTHING, '宝箱なし')
							items << SelectItem.new(:cancel, _('キャンセル'))
							type = Game.select(items){|win|
								win.set(section.treasure_type)
							}
							unless type == :cancel then
								GS.party.floor.sections[section_id].treasure_type = type
							end
						end
					else
						SE.select
						Game.message('敵出現率や宝箱は、部屋にしか設定できません')
					end

					
					
					
					
				end
			when SDL::Key::R
				if @edit_mode then
					if ctrl_pressed then
						floor = GS.party.floor
						selected_section_id = floor.cells[@map_window.cursor_x][@map_window.cursor_y].section_id
						items = []
						
						region_ids = (floor.region_candidate_ids + floor.region_treasure_levels.keys + floor.enemy_appearance_table.keys).uniq
						region_ids.each do |id|
							items << SelectItem.new(id, id.to_s)
						end
						items << SelectItem.new(0, '新しいリージョン')
						
						re = Game.select(items)
						
						case re
						when 0
							new_id = Game.show_name_input_dialog('リージョンIDを入力してください（半角英字）')
							if new_id then
								if new_id !~ /^[a-zA-Z]+$/ then
									Game.message('リージョンIDが不正です')
								elsif new_id.in?(region_ids) then
									Game.message('その名前のリージョンはすでに存在しています')
								else
									floor.enemy_appearance_table[new_id] = []
									floor.cells.flatten.each do |target|
										if target and target.section_id == selected_section_id then
											target.region_id = re
										end
									end
									floor.update_section_data
								end
							end
						when :cancel
							# no act
						else
							floor.cells.flatten.each do |target|
								if target and target.section_id == selected_section_id then
									target.region_id = re
								end
							end
							floor.update_section_data
						end # case
					else
						# 部屋セット
						SE.select
						fill(Dungeon::Area::ROOM)
					end # if
				else
					# リロード
					Game.now_loading{
						Dungeon.load_floors
						FLOORS[GS.party.floor_id].update_section_data
						Game.set_room_events

						@map_window.make_surface
						DUNGEON_WINDOW.update
					}
				end
				
			when SDL::Key::E
				@edit_mode = (@edit_mode ? false : true)
				
				update_help
			
			when SDL::Key::S
				if @edit_mode then
					floor = GS.party.floor
					Game.now_saving{
						Dungeon.save_floors
					}
					message('セーブ完了')
				end
			when SDL::Key::L
				if @edit_mode then
					GS.party.floor.load_form_file("data/dungeon/#{GS.party.floor_id}f_form.dat") or raise 'load failed.'
					Game.set_room_events
					@map_window.make_surface
					SE.equip
				end

			when SDL::Key::K1
				GS.party.floor_id = '1'
				@map_window.make_surface
			when SDL::Key::K2
				GS.party.floor_id = '2'
				@map_window.make_surface
			when SDL::Key::K3
				GS.party.floor_id = '3'
				@map_window.make_surface
			when SDL::Key::K0
				GS.party.floor_id = 'random'
				@map_window.make_surface
			when SDL::Key::F
				@map_window.all_found = (@map_window.all_found ? false : true)
				@map_window.make_surface
			when SDL::Key::M
				GS.party.x = @map_window.cursor_x
				GS.party.y = @map_window.cursor_y
				SE.select
				@map_window.make_surface.update
			end
			@map_window.update
		end
		
		def on_shift_down
			if @edit_mode then
				@map_window.set_begin
				@map_window.update
			end
		end
		

		def on_cancel
			#puts FLOORS[GS.party.floor_id].area_data.map{|x| x.inspect}
			#puts
			#puts FLOORS[GS.party.floor_id].room_data.map{|x| x.inspect}
			Phase.change(DungeonPhase.new)
		end
		
	end
	

	class DungeonMapDoorAddPhase < DungeonMapPhase
	
		def on_key_press(keycode)
			floor = FLOORS[GS.party.floor_id]
			SDL::Key.scan
			case keycode
			when SDL::Key::UP
				floor.set_wall(DIR_N, @map_window.cursor_x, @map_window.cursor_y, Area::DOOR)
			when SDL::Key::LEFT
				floor.set_wall(DIR_W, @map_window.cursor_x, @map_window.cursor_y, Area::DOOR)
			when SDL::Key::RIGHT
				floor.set_wall(DIR_E, @map_window.cursor_x, @map_window.cursor_y, Area::DOOR)
			when SDL::Key::DOWN
				floor.set_wall(DIR_S, @map_window.cursor_x, @map_window.cursor_y, Area::DOOR)
			else
				return
			end
			@map_window.update
			Phase.change(DungeonMapEditPhase.new(@map_window))
		end
		
		def on_cancel
			Phase.change(DungeonMapEditPhase.new(@map_window))
		end
	end












	class MerchantBuyPhase < BuyPhase
		def update_list
			@product_data.clear
			
			# いつも売ってる
			#@product_data << [Item.new('Bandage'), nil]
			@product_data << [Item.new('WhiteIncense'), nil]
			@product_data << [Item.new('BlackIncense'), nil]
			
			# 日替わり
			GS.merchant_products.each do |product|
				@product_data << [product, 1]
			end

			
			@list_window.update(@product_data)
			
			if @list_window.select_items.empty? then
				SEAL_WINDOW.centering_on_window(@list_window).show
				DESCRIPTION_WINDOW.update('')
			else
				SEAL_WINDOW.hide
			end
			
		end
		
		def buy_sequence
			GS.merchant_products.delete(@list_window.current_product)
		end


		
		def confirm_message(product)
			_("「%{product}だな、そいつは金貨%{price}枚にしとくよ\nどうだい？　お得だろう？」").evaluate(:product => product.name, :price => product.price)
		end
	
		def ungetable_message(member)
			_("「おっと待ってくれ、%{member}は手が空いてないみたいだぜ？\n持ち物を整理してから来てくれよ」").evaluate(:member => member.name)
		end
		
		def buy_message
			_("「ありがとう友よ！」")
		end
		
		def on_cancel
			throw(:exit_operation_loop)
		end
	end
	
	class DebugBuyPhase < BuyPhase
		def update_list
			@product_data.clear
			
			COMMON_LINEUP[PD_DEBUG].each do |id|
				@product_data << [ITEM_SAMPLES[id], nil]
			end

			
			@list_window.update(@product_data)
			

		end
		def on_cancel
			throw(:exit_operation_loop)
		end
	end









	class TreasurePhase < Phase
	
		def initialize(treasure_box)
			@treasure_box = treasure_box
		end
	
		def on_enter(from)
			items = SELECT_WINDOW.select_items.clear
			items << SelectItem.new(:detect, _('調べる(D)'), SDL::Key::D)
			items << SelectItem.new(:analysis_vision, _('幻視の呪文で調べる(S)'), SDL::Key::S)
			items << SelectItem.new(:remove_trap, _('罠を外す(R)'), SDL::Key::R)
			items << SelectItem.new(:unlock, _('鍵を開ける(U)'), SDL::Key::U)
			items << SelectItem.new(:open, _('開ける(O)'), SDL::Key::O)
			items << SelectItem.new(:leave, _('立ち去る(L)'), SDL::Key::L)
			items << SelectItem.new(:info, "＊宝箱の情報") if Game.debug_mode?
			items << SelectItem.new(:trap_test, "＊任意の罠をセット") if Game.debug_mode?
			items << SelectItem.new(:change_level, "＊宝箱レベル変更") if Game.debug_mode?
			items << SelectItem.new(:reset, "＊リセット") if Game.debug_mode?
			SELECT_WINDOW.make_surface(240, items.size)
			
			analyser_check
			
			@treasure_window = PictureWindow.new.make_surface('box.png')
			@treasure_window.centering
			@treasure_window.left = 80
			SELECT_WINDOW.dock_beside(@treasure_window, :right, 16)
			SELECT_WINDOW.top += (@treasure_window.height/2 - SELECT_WINDOW.height/2)
			SELECT_WINDOW.reset_index.warp	
			
			@trap_window = TemporaryMultiColumnWindow.new.make_surface(240, 8)
			@trap_window.silent = true
			@trap_window.columns = [ColumnItem.new(s_("ColumnCaption|TYPE"), [], 140), ColumnItem.new(s_("ColumnCaption|DIFFICULT"), [], 60, AL_RIGHT)]
			
			
			items = @trap_window.select_items.clear
			TRAP_TYPES.each_with_index do |id, i|
				key = KEY_TABLE[i]
				items << SelectItem.new(id, TRAP_CAPTIONS[id] + "(#{key})", key)
				@trap_window.columns[1].captions << TRAP_LEVELS[id]
			end
			@trap_window.update.centering
			
			PARTY_WINDOW.update

			
			$windows = [PARTY_WINDOW, @treasure_window, SELECT_WINDOW]
			
			BGM.fade_out
			Game.reporter.on_treasure_box_find(GS.party, @treasure_box)
			
		end
		
		def analyser_check
			unless GS.party.alive_members.find{|x| x.analysis_vision_usable?} then
				SELECT_WINDOW.select_items.find{|x| x.id == :analysis_vision}.disable 
			end
			SELECT_WINDOW.update
		end
		
		
		def on_select(id)
			case id
			when :detect
				SELECT_WINDOW.hide
				PARTY_WINDOW.select_items.each do |item|
					item.disable if GS.party.members[item.id].dead?
				end
				index = Game.member_select(_("だれが？"), :silent => true, :default_member_index => GS.party.default_trap_remover_index)
				if index then
					detector = GS.party.members[index]
					
					operating = @treasure_box.try_avoid_operating_trap(detector, 90)
					
					if operating && TRAP_LEVELS[operating] > (detector.skill_levels[SKILL::REMOVE_TRAP] + 5) then
						operate_trap(detector, operating)
					else
						SE.select
						message(_("%{detector}は宝箱を注意深く調べた").evaluate(:detector => detector.name) + "\n#{@treasure_box.unlocking_hint(detector)}\n#{@treasure_box.detect_traps(detector)}")
					end
				end
				SELECT_WINDOW.show.warp
			when :analysis_vision
				SELECT_WINDOW.hide
				PARTY_WINDOW.update([get_vision_mp_column])
				PARTY_WINDOW.select_items.each do |item|
					item.disable unless GS.party.members[item.id].analysis_vision_usable?
				end
				index = Game.member_select(_("だれが？"))
				if index then
					detector = GS.party.members[index]
					detector.use_mp(TRICK_DATA['AnalysisVision'].mp_cost, 'Vision')
					PARTY_WINDOW.update([get_vision_mp_column])
					message(_("%{detector}は幻視の呪文を唱えた！").evaluate(:detector => detector.name) + "\n#{@treasure_box.unlocking_hint(detector)}\n#{@treasure_box.analysis_traps}\n#{@treasure_box.content_description}")
				end
				analyser_check
				SELECT_WINDOW.show.warp
				PARTY_WINDOW.update([])
				
			when :unlock
				SELECT_WINDOW.hide
				PARTY_WINDOW.silent = true
				PARTY_WINDOW.select_items.each do |item|
					item.disable if GS.party.members[item.id].dead?
				end
				index = Game.member_select(_("だれが？"), :silent => true, :default_member_index => GS.party.default_unlocker_index)
				if index then
					unlocker = GS.party.members[index]
					if !(@treasure_box.locked?) then
						SE.select
						message(_("%{unlocker}は鍵を開けようとした\nしかし、この宝箱には鍵は掛かっていないようだ").evaluate(:unlocker => unlocker.name))
					elsif @treasure_box.try_unlock(unlocker) then
						get_sp = unlocker.on_use_skill(SKILL::UNLOCK, @treasure_box.lock_level, 0.2)
						SE.unlock

						suc_msg = _("%{unlocker}は鍵開けを試みた\n成功！\n鍵開け経験値 +%{exp}").evaluate(:unlocker => unlocker.name, :exp => get_sp)

						if unlocker.skill_levelup?(SKILL::UNLOCK) then
							unlocker.skill_levelup(SKILL::UNLOCK)
							PARTY_WINDOW.update
							message(suc_msg + _("\n%{unlocker}の鍵開けレベルが%{new}になった").evaluate(:unlocker => unlocker.name, :new => unlocker.skill_levels[SKILL::UNLOCK]))
						else
							message(suc_msg)
						end
						@treasure_box.unlock
					else
						SE.select
						message(_("%{unlocker}は鍵開けを試みた\n失敗！").evaluate(:unlocker => unlocker.name))
					end
				end
				
				SELECT_WINDOW.show.warp
				
			when :remove_trap
				loop do
					PARTY_WINDOW.select_items.each do |item|
						item.disable if GS.party.members[item.id].dead?
					end
					$member_index = Game.member_select(_("だれが？"), :default_member_index => GS.party.default_trap_remover_index)
					unless $member_index then
						SELECT_WINDOW.show.warp
						break
					end
					remover = GS.party.members[$member_index]
					PARTY_WINDOW.update_surface
					
					
					
					@trap_window.show.warp
					@trap_window.silent = true
					trap_id = Game.operation_loop
					@trap_window.hide
					$member_index = nil
					if trap_id == :cancel then
						SE.cancel
						retry
					else
						trap_name = TRAP_CAPTIONS[trap_id]
						failure_msg = _("%{remover}は%{trap}の解除を試みた\n失敗！").evaluate(:remover => remover.name, :trap => trap_name)
						
						if @treasure_box.traps.include?(trap_id) then
							# 正しい罠を選んだ
							dice = @treasure_box.get_remove_trap_dice(remover, trap_id)
							
							if dice.roll then
								get_sp = remover.on_use_skill(SKILL::REMOVE_TRAP, TRAP_LEVELS[trap_id], 0.2)
								suc_msg = _("%{remover}は%{trap}の解除を試みた\n成功！\n罠外し経験値 +%{exp}").evaluate(:remover => remover.name, :trap => trap_name, :exp => get_sp)
								SE.remove_trap
								if remover.skill_levelup?(SKILL::REMOVE_TRAP) then
									remover.skill_levelup(SKILL::REMOVE_TRAP)
									PARTY_WINDOW.update
									message(suc_msg + _("\n%{remover}の罠外しレベルが%{new}になった").evaluate(:remover => remover.name, :new => remover.skill_levels[SKILL::REMOVE_TRAP]))
								else
									message(suc_msg)
								end
								@treasure_box.remove_trap(trap_id)
							else
								# 失敗時10%で発動
								if @treasure_box.traps.empty? or diceroll('d100', 90) then
									SE.select
									message(failure_msg)
								else
									operate_trap(remover, @treasure_box.traps.first)
								end
							end
						else 
							# 罠の種類を間違えた（100%発動）
							if @treasure_box.traps.empty? then
								SE.select
								message(failure_msg)
							else
								operate_trap(remover, @treasure_box.traps.first)
							end
						end
						
						break
					end
				end

				SELECT_WINDOW.show.warp

			when :open
				
				
				SELECT_WINDOW.hide
				PARTY_WINDOW.select_items.each do |item|
					item.disable if GS.party.members[item.id].dead?
				end
				index = Game.member_select(_("だれが？"), :silent => true)
				if index then
					opener = GS.party.members[index]
					if !(@treasure_box.traps.empty?) then
						operate_trap(opener, @treasure_box.traps.first)
						SELECT_WINDOW.show.warp
					elsif @treasure_box.locked? then
						SE.select
						message(_("この宝箱には鍵が掛かっているようだ"))
						SELECT_WINDOW.show.warp
					else
						@treasure_window.hide
						SE.open_box
						lines = [_("宝箱を開けた！")]

						got = (@treasure_box.gold / GS.party.alive_members.size.to_f).round
						if got >= 1 then
							GS.party.alive_members.each do |member|
								member.get_gold(got)
								GS.player_profiles[PPT::GotGoldFromTreasureBox] += got
								Game.increment_player_record(PRT::GotGold, PPT::GotGoldFromTreasureBox, got)
							end
							(GS.party.members - GS.party.alive_members).each do |member|
								member.get_gold(0)
							end
							
							lines << _("生存メンバーはそれぞれ%{gold}枚の金貨を獲得").evaluate(:gold => got)
							@treasure_box.gold = 0
						end
						
						finishing = false
						
						if (item = @treasure_box.item) then
							# アイテムの入っている宝箱の場合
							if opener.getable_item?(item) then
								opener.get_item(item)
								lines << _("%{opener}は%{item}を手に入れた").evaluate(:opener => opener.name, :item => item.name)
								
								finishing = true
							else
								getter = GS.party.get_item(item)
								if getter then
									lines << _("%{item}を手に入れ、%{getter}に渡した").evaluate(:item => item.name, :getter => getter.name)

									finishing = true
								else
									lines << _("%{item}が入っていたが、誰も持てないのでそのままにした").evaluate(:item => item.name)
								end
							end
							
							
							if finishing then
								Game.increment_player_record(PRT::GotTreasure, item.data_id)
							end
						else
							# アイテムの入ってない宝箱の場合
							finishing = true
						end
						Game.reporter.on_treasure_box_open
						message(lines.join("\n"))
						
						if finishing then
							finish(true) 
						else
							SELECT_WINDOW.show.warp
						end
					end
				else
					SELECT_WINDOW.show.warp
				end
				PARTY_WINDOW.silent = false
				

			when :leave
				if Game.ask(_("宝箱をそのままにして立ち去ります。\n（後から回収できますが、時間が経つと無くなってしまいます）\nよろしいですか？")) then
					Game.reporter.on_treasure_box_leave
					finish(false)
				else
					SELECT_WINDOW.show.warp
				end
			when :trap_test
				@trap_window.show.warp
				trap_id = Game.operation_loop
				@trap_window.hide
				if trap_id then
					@treasure_box.set_traps([trap_id])
				end
			when :info
				Game.show_debug_data_window(@treasure_box.debug_info_texts)
			when :change_level
				if (level = Game.number_input('レベルを入力してください', @treasure_box.level, 1..6)) then
					@treasure_box = TreasureBox.new(level, @treasure_box.type)
				end
			when :reset
				@treasure_box = TreasureBox.new(@treasure_box.level, @treasure_box.type)
			end

		end
		
		def get_vision_mp_column
			captions = GS.party.members.map{|x| ((x.spell_caster? && x.spells.include?('Vision'))? x.mp['Vision'] : '')}
			return ColumnItem.new('MP', captions, 50, AL_RIGHT)
		end
		
		def finish(removing)
			if removing then
				GS.room_treasure_boxes[GS.party.floor_id][GS.party.section_id] = nil
			end
			
			Game.save(FQ_BIG_CHANGE, "宝箱終了")
			
			
			throw(:exit_operation_loop, true)
		end
		
		def operate_trap(toucher, id)
			GS.party.members.each do |member|
				member.update_last_parameters
			end
			PARTY_WINDOW.update_surface
			
			effect = nil
			targets = nil
			vanish = false
			case id
			when TT::Stone
				effect = AttackEffect.new
				effect.fixed_damage = 15
				effect.hitting_number = 6
				effect.hitting_border_base = 50
				effect.messages << _("宝箱から石つぶてが飛び出す！")
				
				targets = [toucher]
				
			when TT::Needle
				effect = AttackEffect.new
				effect.fixed_damage = 30
				effect.shield_guardable = false
				effect.hitting_border_base = 65
				effect.messages << _("宝箱から鋭い針が飛び出した！")

				targets = [toucher]

			when TT::PoisonNeedle
				effect = AttackEffect.new
				effect.fixed_damage = 30
				effect.shield_guardable = false
				effect.hitting_border_base = 65
				effect.messages << _("宝箱から鋭い針が飛び出した！")
				effect.state_data << [PoisonState, 1, 70]

				targets = [toucher]

			when TT::Burner
				effect = AttackEffect.new
				effect.shield_guardable = false
				effect.fixed_damage = 50
				effect.hitting_border_base = 80
				effect.messages << _("宝箱は一瞬のうちに燃え上がった！")
				vanish = true

				targets = [toucher]
			when TT::MiniBomb
				effect = AttackEffect.new
				effect.fixed_damage = 50
				effect.hitting_border_base = 60
				effect.messages << _("宝箱は爆発し、粉々に砕け散った！\n爆風が%{target}を襲う！")
				targets = GS.party.members
				vanish = true
			when TT::MiniTereporter
				effect = MemberTereportEffect.new
				effect.messages << _("%{target}の足元に魔方陣が現れ、光を放った！")

				
				targets = [toucher]
			when TT::Alarm
				effect = AlarmEffect.new
				effect.alarm_type = TT::Alarm
				targets = GS.party.members
				
			else
				raise "unknown trap - #{id}"
			end
			
			dummy_actor = Fighter.new
			SE.trap_switch
			message(_("%{trap}作動！").evaluate(:trap => TRAP_CAPTIONS[id]))
			
			@treasure_window.hide if vanish
			
			old_party = GS.party
			msgs = effect.operate(dummy_actor, targets)
			PARTY_WINDOW.update
			message(msgs)

			GS.party.members.each do |member|
				member.update_last_parameters
			end
			

			PARTY_WINDOW.update_surface

			@treasure_box.remove_trap(id)
			Game.reporter.on_treasure_box_operate_trap(id, targets, vanish)
			
			if GS.party.alive? then
				
				if not old_party == GS.party then
					# テレポーター用の特殊処理
					# （全員いなくなった場合、DungeonPhaseに戻る）
					finish(false)
				elsif vanish then
					# 爆弾などで宝箱が消滅した場合
					finish(true)
				else
					analyser_check
				end
			else
				Phase.change(WipeoutPhase.new)
			end
		end
		
		def on_leave(to)
			super
			PARTY_WINDOW.update
		end
		
	end

end