# ウインドゥクラス

require 'strscan'
require 'rubyplus'
require 'rarg'

module DarkHall
	PADDING = 4
	DOCK_MARGIN = 8
	AL_LEFT = 1
	AL_CENTER = 2
	AL_RIGHT = 3
	
	
	class Window
	
		SPACE = PADDING
		
		def self.good_width(base, font = Game.regular_ttf)
			case base
			when String
				font.text_size(base)[0] + TEXT_AREA_SPACE_DEFAULT
			when Numeric
				base + TEXT_AREA_SPACE_DEFAULT
			when nil
				8
			end
		end
		
		def self.centering_x(targets, option_hash = {})
			opt = RArg.parse(option_hash) do
				default_arg :margin, 8
				default_arg :on_screen, false
			end
		
			# 必要な幅を計算
			total_width = 0
			targets.each do |win|
				total_width += win.width
			end
			total_width += (targets.size - 1) * opt[:margin]
			
			# 整列
			cx = SCREEN_WIDTH/2 - total_width/2
			targets.each do |win|
				win.left = cx
				cx += (win.width + opt[:margin])
			end
		end
		
		def self.centering_y(targets, option_hash = {})
			opt = RArg.parse(option_hash) do
				default_arg :margin, 8
				default_arg :on_screen, false
			end
			
			# 必要な高さを計算
			total_height = 0
			targets.each do |win|
				total_height += win.height
			end
			total_height += (targets.size - 1) * opt[:margin]
			
			# 整列
			if opt[:on_screen] then
				cy = SCREEN_HEIGHT/2 - total_height/2
			else
				cy = (SCREEN_HEIGHT - PARTY_WINDOW.height)/2 - total_height/2
			end
			targets.each do |win|
				win.top = cy
				cy += (win.height + opt[:margin])
			end
		end
		
		def self.blit_hp_gauge(type, dest, x, y, hp_rate, last_hp_rate, hp_max_rate = 1.0)
			gauge_width = (Game.gauge_surfaces[type][:base].w * hp_max_rate).to_i
						
			# 最大値ゲージ
			SDL.blit_surface(Game.gauge_surfaces[type][:back], 0, 0, gauge_width, 2, dest, x, y)


			# 現在値＆減少分ゲージ
			if last_hp_rate > 0 then
				SDL.blit_surface(Game.gauge_surfaces[type][:decrease], 0, 0, (last_hp_rate * gauge_width).to_i, 2, dest, x, y)
			end
			
			if hp_rate > 0 then
				color_type = (hp_rate < 0.25 ? :warning : :base)
				SDL.blit_surface(Game.gauge_surfaces[type][color_type], 0, 0, (hp_rate * gauge_width).to_i, 2, dest, x, y)
			end
		end
		
		attr_accessor :left, :top, :width, :height, :back_surface, :front_surface
		bool_attr_accessor :silent, :has_frame, :has_caption, :inline_tag_usable
		bool_attr_accessor :text_resizing
		attr_accessor :word_wrap_length
	
		def initialize
			@left = 0
			@top = 0
			@width ||= 100
			@height ||= 100
			@has_frame = true
			@has_caption = false
			@inline_tag_usable = false
			@silent = false
			@text_resizing = false
			@word_wrap_length = nil
		end
		
		
		# 一行の高さ
		def line_height(font = Game.regular_ttf)
			font.line_skip + 6
		end
		
		def line_top(line_index)
			line_height * line_index
		end
		
		def line_text_top(line_index)
			self.line_top(line_index) + ((line_height - Game.regular_ttf.height) / 2).to_i
		end
		
		# trueなら、EnterキーやESCキーを含む全てのKeyDownイベントが
		# このウインドウのon_key_downメソッドに送られる
		def capture_all_keys?
			false
		end
		
		def mouse_on_window?(x, y)
			x >= left and x <= right and y >= top and y <= bottom
		end

		# 幅・高さの設定に応じてサーフェイスを作成する
		def make_surface(width = nil, height = nil)
			@width = width if width
			@height = height if height
			
			@back_surface = SDL::Surface.new(SDL::SRCALPHA, @width, @height, $screen)
			@back_surface.fill_rect(0, 0, @width, @height, ColorTable::BACK)
			@back_surface.set_alpha(SDL::SRCALPHA | SDL::RLEACCEL, 200) if SETTING.use_alpha_bg?
			@back_surface = @back_surface.display_format
			
			# グラデーション
			#step = 10
			#step.times do |i|
			#	c = i * 6
			#	@back_surface.fill_rect(0, (@height / step).to_i * i, @width, (@height / step).to_i, [c, c, c])
			#end
			
			@front_surface = SDL::Surface.new(SDL::SRCCOLORKEY, self.width, self.height, $screen)
			@front_surface.set_color_key(SDL::SRCCOLORKEY, 0)
			#self.update
			@front_surface = @front_surface.display_format
			
			# ToDo: この辺でclear_surfaceを実行しておくと、不可解な現象が減るかもしれない
			
			return self
		end
		
		
		# サーフェイス書き換え
		def update
			self.clear_surface
			return self
		end
		
		def clear_surface
			if @front_surface then
				@front_surface.fill_rect(0, 0, @width, @height, ColorTable::BLACK)
				@front_surface.draw_rect(0, 0, @width - 1, @height - 1, ColorTable::WHITE) if @has_frame
				@front_surface.fill_rect(0, PADDING + line_top(1) - 1, @width - 1, 1, ColorTable::WHITE) if @has_caption
				@front_surface.unlock
			end
			return self
		end
		
		def break_surface
			@front_surface = nil
			@back_surface = nil
			@cursor_surface = nil
		end
		
		def maked_surface?
			return(@front_surface && @back_surface)
		end

		# スクリーンにウインドゥを表示
		def blit
			$screen.put(@back_surface, @left, @top)
			$screen.put(@front_surface, @left, @top)
			return self
		end
		

	
		def client_width
			return(self.width - PADDING * 2)
		end
	
		def client_height
			return(self.height - PADDING * 2)
		end
		
		def client_width=(w)
			@width = w + PADDING*2
		end

		def client_height=(h)
			@height = h + PADDING*2
		end


		def client_left
			@left + PADDING
		end
		
		def client_top
			@top + PADDING
		end

		def client_right
			right - PADDING
		end
		
		def client_bottom
			bottom - PADDING
		end

		
		def right
			return(@left + @width)
		end
		
		def right=(value)
			@left = value - @width
		end
	
		
		def bottom
			return(@top + @height)
		end
		
		def bottom=(value)
			@top = value - @height
		end
		
		def set_width_from_text(text, font = Game.regular_ttf)
			
			self.client_width = Window.good_width(text, font)
			return self
		end
		
		# ウインドウの種類に応じて、それなりに良い幅にしてくれる
		def set_good_width
			self
		end


		def set_height_from_line(line, font = Game.regular_ttf)
			@height = PADDING * 2 + line_height(font) * line
		end
	
		
		def set_position(x, y)
			if x == :center then
				@left = SCREEN_WIDTH/2 - @width/2
			elsif x.nil? then
				# 何もしない
			else
				@left = x
			end
			
			if y == :center then
				@top = SCREEN_HEIGHT/2 - @height/2
			elsif y.nil? then
				# 何もしない
			else
				@top = y
			end
			
			return self
		end
		
		# ウインドゥの側に移動
		def dock_beside(window, side, margin = DOCK_MARGIN)
			case side
			when :top
				self.set_position(window.left, window.top - self.height - margin)
			when :top_right, :right_top
				self.set_position(window.right - self.width, window.top - self.height - margin)
			when :top_center, :center_top
				self.set_position(window.left + (window.width - self.width) / 2, window.top - self.height - margin)
			when :bottom, :under
				self.set_position(window.left, window.bottom + margin)
			when :bottom_right, :right_bottom
				self.set_position(window.right - self.width, window.bottom + margin)
			when :bottom_center, :center_bottom
				self.set_position(window.left + (window.width - self.width) / 2, window.bottom + margin)
			when :left
				self.set_position(window.left - self.width - margin, window.top)
			when :right
				self.set_position(window.right + margin, window.top)
			end
			
			return self
		end
		
		def dock_under(window)
			self.set_position(window.left + 24, window.top + 24)
			
			return self
		end


		def centering(party_window_ignore = false)
			@left = SCREEN_WIDTH/2 - @width/2
			area_height = (party_window_ignore ? SCREEN_HEIGHT : (SCREEN_HEIGHT - PARTY_WINDOW.height))
			@top = area_height/2 - @height/2
			return self
		end
		
		def centering_on_screen
			centering(true)
		end
		
		def centering_on_window(window)
			@left = window.left + (window.width - @width)/2
			@top = window.top + (window.height - @height)/2
			return self
		end


		def show
			if $windows.include?(self) then
				$windows.delete(self)
				$windows << self
			else
				$windows << self
			end
			
			return self
		end
		
		
		def hide
			$windows.delete(self)
			return self
		end



		def shown?
			return($windows.include?(self))
		end
		
		def warp
			self
		end
		

		
		
		TAG_PATTERN = /^\$\[(.+?)\]/
		TAGS = {}
		TAGS[:goodcolor] = 'goodcolor'
		TAGS[:badcolor] = 'badcolor'
		TAGS[:disablecolor] = 'disablecolor'
		TAGS[:lightdisablecolor] = 'lightdisablecolor'
		TAGS[:solid] = 'solid'
		
		# \c[good]\c[0]
		
		INLINE_TAG_PATTERN = /(\$_*[0-9A-Za-z])(?:\[(.*)\])?/
		



		def draw_text(align, text, x, y, drawing_area_width = self.client_width, color = ColorTable::WHITE, font = Game.regular_ttf)
			text = text.to_s
			char_width = font.text_size(' ')[0]
			
			# 文字分解
			if @inline_tag_usable then
				chars = text.gsub(INLINE_TAG_PATTERN, '').each_char
			else
				if text =~ TAG_PATTERN then
					case $1.downcase
					when TAGS[:goodcolor]
						color = ColorTable::GOOD
					when TAGS[:badcolor]
						color = ColorTable::BAD
					when TAGS[:disablecolor]
						color = ColorTable::DISABLE
					when TAGS[:solid]
						solid_draw = true
	
					end
					text = $'
				end

				chars = text.each_char
			end
			
			# 幅
			text_width = 0
			chars.each do |c|
				text_width += (char_width * CHAR_LENGTH_TABLE[c.length])
			end

			# left確定
			case align
			when AL_LEFT
				left = x + PADDING
			when AL_CENTER
				left = x + PADDING + (drawing_area_width - text_width) / 2
			when AL_RIGHT
				left = x + PADDING + drawing_area_width - text_width
			end
			
			cx = left
			cy = y + PADDING
			
			
			resizing = (text_width > drawing_area_width and text_resizing?)
			
			# 印字
			if @inline_tag_usable then
				s = StringScanner.new(text)
				until s.eos? do
					if s.scan(INLINE_TAG_PATTERN) then
						name = s[1]; arg = s[2]

						case name
						when '$c'
							case arg
							when 'warn', 'bad'
								color = ColorTable::BAD
							when 'item', 'em', 'good'
								color = ColorTable::GOOD
							when 'gold'
							else
								color = ColorTable::WHITE
							end
						end
					else
						c = s.getch
						ttf_draw(font, @front_surface, c, cx, cy, color)
						cx += (char_width * CHAR_LENGTH_TABLE[c.length])
					end
				end
			else
				if resizing then
					Game.text_buffer.fill_rect(0, 0, Game.text_buffer.w, Game.text_buffer.h, Color::BLACK)
					cx = 0
					chars.each do |c|
						ttf_draw(font, Game.text_buffer, c, cx, 0, color)
						cx += (char_width * CHAR_LENGTH_TABLE[c.length])
					end
					SDL.transform(Game.text_buffer, @front_surface, 0, (drawing_area_width.to_f / text_width), 1, 0, 0, x + PADDING, y + PADDING, SDL::Surface::TRANSFORM_AA)
				else
					right_max = left + drawing_area_width
					
					drawn_at_line = 0
					chars.each do |c|
						w = char_width * CHAR_LENGTH_TABLE[c.length]
					
						# 書き込んでも右端を超えないかどうかの判定
						if cx + w <= right_max then
							ttf_draw(font, @front_surface, c, cx, cy, color)
							cx += w
							#drawn_at_line += CHAR_LENGTH_TABLE[c.length]
						else
							ttf_draw(font, @front_surface, '.', cx, y + PADDING, color)
							break
						end
						
						#if @word_wrap_length and drawn_at_line >= @word_wrap_length then
						#	# ワードラップ
						#	cx = left
						#	cy += line_height
						#	drawn_at_line = 0
						#end
					end
				end
			end
		end
		
		def ttf_draw(ttf, dest, text, x, y, color)
			if SETTING.use_ttf_blended_draw? then
				ttf.draw_blended_utf8(dest, text, x, y, color[0], color[1], color[2])
			else
				ttf.draw_solid_utf8(dest, text, x, y, color[0], color[1], color[2])
			end
		end
	
		def draw_text_left(text, x, y, width = self.client_width, color = ColorTable::WHITE, font = Game.regular_ttf)
			self.draw_text(AL_LEFT, text, x, y, width, color, font)
		end
	
		def draw_text_right(text, x, y, width = self.client_width, color = ColorTable::WHITE, font = Game.regular_ttf)
			self.draw_text(AL_RIGHT, text, x, y, width, color, font)
		end
		
		def draw_text_center(text, x, y, width = self.client_width, color = ColorTable::WHITE, font = Game.regular_ttf)
			self.draw_text(AL_CENTER, text, x, y, width, color, font)
		end
	
		def page_up_shortcut
		end
		
		def page_down_shortcut
		end
		
		def select_se
			if @silent then
				@silent = false
				return
			end

			if defined?(current_id) then
				case current_id
				when :share, :share_gold, :gather, :gather_gold
					SE.coin
					return
				when :cancel, :no
					SE.cancel
					return
				end
			end
			
			
			SE.select
		end
		
		def cancel_se
			if @silent then
				@silent = false
				return
			else			
				SE.cancel
			end
		end


		
		def on_enter
			self.on_enter_key
		end
		
		def on_enter_key
			select_se
			$phase.on_enter_key
		end
		
		def on_cancel
			cancel_se
			$phase.on_cancel
		end
		
		def on_left_key
			if defined?($phase.on_left_key) then
				$phase.on_left_key
			else
				$phase.on_key_press(SDL::Key::LEFT)
			end
		end
		
		def on_right_key
			if defined?($phase.on_right_key) then
				$phase.on_right_key
			else
				$phase.on_key_press(SDL::Key::RIGHT)
			end
		end
		
		def on_key_press(keycode)
			case keycode
			when 39 # MEMO: 定数の名前とキーコードが食い違っている。SDLのバグ?
				# cheat code
				Game.input_cheat_code
			else
				$phase.on_key_press(keycode)
			end

		end
		
		def on_shift_down
			$phase.on_shift_down
		end
		
		def on_shift_up
			$phase.on_shift_up

		end

		def on_ctrl_down
			$phase.on_ctrl_down
		end
		
		def on_ctrl_up
			$phase.on_ctrl_up
		end

		def on_change
		end
		
	end
	
	class PictureWindow < Window
		def initialize
			super
			@has_frame = false
		end
		
		def make_surface(path_or_surface)
			super()
			case path_or_surface
			when SDL::Surface
				surface = path_or_surface
			else
				surface = Res.load_picture(path_or_surface)
				surface.set_color_key(SDL::SRCCOLORKEY, surface.get_pixel(0, 0))
			end
			@width = surface.w
			@height = surface.h
			
			@front_surface = surface.display_format
			return self
		end
		
		def update
			# no action
			self
		end
	end

	
	class ScreeningWindow < Window
		def initialize
			super
			@has_frame = false
			@width = SCREEN_WIDTH
			@height = SCREEN_HEIGHT
			@left = 0
			@top = 0
		end
	end
	
	class TitleNameWindow < Window
		def initialize
			super
			@has_frame = false
			@left = 440
			@top = 360
			@width = 200
			@height = 80
		end

		def make_surface
			super
			shadow = 4
			Game.title_ttf.draw_blended_utf8(@front_surface, s_("TitleScreen|Dark Hall"), 4, 4, *[60, 60, 60])
			Game.title_ttf.draw_blended_utf8(@front_surface, s_("TitleScreen|Dark Hall"), 0, 0, *Color::WHITE)
		end
	end
	
	class DungeonWindow < Window
		#HORIZONAL_LENGTH_DATA = [160] + (0..4).map{|i| (105 * (0.65 ** i)).to_i} # 最初のデータは無限近
		#HORIZONAL_LENGTH_DATA = [150, 164 / 2 - 1, 112 / 2 - 1, 84 / 2 - 1, 70 / 2 - 1, 60 / 2 - 1]
		#VERTICAL_LENGTH_DATA = HORIZONAL_LENGTH_DATA
		
		LINE_WALL_LENGTHS = [160 * 2] + (0..4).map{|i| (105 * (0.65 ** i)).to_i * 2} # 最初のデータは無限近
		TEXTURE_WALL_LENGTHS = [300, 164, 112, 86, 70, 60]
		WINDOW_WIDTH = 250
		WINDOW_HEIGHT = 250
		WALL_COLORS = []
		DOOR_COLORS = []
		

	
		def initialize
			@width = WINDOW_WIDTH + 1 * 2
			@height = WINDOW_HEIGHT + 1 * 2
		end
		
		# 常に決定音を出さない
		def silent
			true
		end
		
		alias silent? silent
		
		LINE_POINTS = []
		
		# 距離0・正面壁の原点を計算
		ox = WINDOW_WIDTH / 2 - LINE_WALL_LENGTHS[0] / 2
		oy = WINDOW_HEIGHT / 2 - LINE_WALL_LENGTHS[0] / 2
		
		# 点の座標を計算
		(0..(VIEWABLE_FRONT_RANGE)).each do |range|
			LINE_POINTS[range] = {}
			((VIEWABLE_SIDE_RANGE * -1)..VIEWABLE_SIDE_RANGE).each do |pos|
				px1 = ox + LINE_WALL_LENGTHS[0] / 2 - LINE_WALL_LENGTHS[range] / 2 + (LINE_WALL_LENGTHS[range] - 1) * pos
				py1 = oy + LINE_WALL_LENGTHS[0] / 2 - LINE_WALL_LENGTHS[range] / 2
				px2 = px1 + LINE_WALL_LENGTHS[range] - 1
				py2 = py1 + LINE_WALL_LENGTHS[range] - 1
			
			
				points = []
				points[0] = [px1, py1]
				points[1] = [px2, py1]
				points[2] = [px1, py2]
				points[3] = [px2, py2]
				
				LINE_POINTS[range][pos] = points
				
				#puts "(#{range}, #{pos}) = (#{px1}, #{py1}) - (#{px2}, #{py2}) <#{px2 - px1 + 1}, #{py2 - py1 + 1}>"

			end
			
		end

		TEXTURE_POINTS = []
		
		# 距離0・正面壁の原点を計算
		ox = WINDOW_WIDTH / 2 - TEXTURE_WALL_LENGTHS[0] / 2
		oy = WINDOW_HEIGHT / 2 - TEXTURE_WALL_LENGTHS[0] / 2
		
		# 点の座標を計算
		(0..(VIEWABLE_FRONT_RANGE)).each do |range|
			TEXTURE_POINTS[range] = {}
			((VIEWABLE_SIDE_RANGE * -1)..VIEWABLE_SIDE_RANGE).each do |pos|
				px1 = ox + TEXTURE_WALL_LENGTHS[0] / 2 - TEXTURE_WALL_LENGTHS[range] / 2 + (TEXTURE_WALL_LENGTHS[range] - 1) * pos
				py1 = oy + TEXTURE_WALL_LENGTHS[0] / 2 - TEXTURE_WALL_LENGTHS[range] / 2
				px2 = px1 + TEXTURE_WALL_LENGTHS[range] - 1
				py2 = py1 + TEXTURE_WALL_LENGTHS[range] - 1
			
			
				points = []
				points[0] = [px1, py1]
				points[1] = [px2, py1]
				points[2] = [px1, py2]
				points[3] = [px2, py2]
				
				TEXTURE_POINTS[range][pos] = points
				
				#puts "(#{range}, #{pos}) = (#{px1}, #{py1}) - (#{px2}, #{py2}) <#{px2 - px1 + 1}, #{py2 - py1 + 1}>"

			end
			
		end
		
		def maked_surface?
			super and @front_wall_surfaces
		end
		
		
		def make_surface(dungeon_mapping = GS.game_config.dungeon_mapping)
			@dungeon_mapping = dungeon_mapping
			
			(VIEWABLE_FRONT_RANGE + 1).times do |i|
				c = 100 - i * 16
				WALL_COLORS[i] = (@dungeon_mapping == GameConfig::DM_COLORFUL ? [c, c, c] : ColorTable::BLACK)
				DOOR_COLORS[i] = (@dungeon_mapping == GameConfig::DM_COLORFUL ? Color::DOOR : ColorTable::BLACK)
			end

			#unless @front_wall_surfaces then
				case @dungeon_mapping
				when GameConfig::DM_TEXTURE
			
					surface = Res.load_dungeon_texture("floor_and_ceiling1.png")
					surface.set_color_key(SDL::SRCCOLORKEY, surface[0, 150])
					@floor_and_ceiling_surface1 = surface.display_format
					
					surface = Res.load_dungeon_texture("floor_and_ceiling2.png")
					surface.set_color_key(SDL::SRCCOLORKEY, surface[0, 150])
					@floor_and_ceiling_surface2 = surface.display_format
					
					surface = Res.load_dungeon_texture("floor1.png")
					surface.set_color_key(SDL::SRCCOLORKEY, surface[0, 150])
					@floor_surface1 = surface.display_format
	
					surface = Res.load_dungeon_texture("floor2.png")
					surface.set_color_key(SDL::SRCCOLORKEY, surface[0, 150])
					@floor_surface2 = surface.display_format
	
	
					@front_wall_surfaces = []
					@front_door_surfaces = []
					(1..(VIEWABLE_FRONT_RANGE)).each do |range|
						surface = Res.load_dungeon_texture("wall_f#{range}.png")
						@front_wall_surfaces[range] = surface.display_format
						
						surface = Res.load_dungeon_texture("wall_fd#{range}.png")
						@front_door_surfaces[range] = surface.display_format
					end
			
					@left_wall_surfaces = []
					@right_wall_surfaces = []
					@left_door_surfaces = []
					@right_door_surfaces = []
					
					(0...(VIEWABLE_FRONT_RANGE)).each do |range|
						@left_wall_surfaces[range] = {}
						@right_wall_surfaces[range] = {}
						@left_door_surfaces[range] = {}
						@right_door_surfaces[range] = {}
						
						(1..2).each do |pos|
							surface = Res.load_dungeon_texture("wall_l#{range + 1}p#{pos}.png")
							surface.set_color_key(SDL::SRCCOLORKEY, surface[surface.w - 1, 0])
							@left_wall_surfaces[range][pos * -1] = surface.display_format
							
							surface = Res.load_dungeon_texture("wall_ld#{range + 1}p#{pos}.png")
							surface.set_color_key(SDL::SRCCOLORKEY, surface[surface.w - 1, 0])
							@left_door_surfaces[range][pos * -1] = surface.display_format
							
							surface = Res.load_dungeon_texture("wall_r#{range + 1}p#{pos}.png")
							surface.set_color_key(SDL::SRCCOLORKEY, surface[0, 0])
							@right_wall_surfaces[range][pos] = surface.display_format
							
							surface = Res.load_dungeon_texture("wall_rd#{range + 1}p#{pos}.png")
							surface.set_color_key(SDL::SRCCOLORKEY, surface[0, 0])
							@right_door_surfaces[range][pos] = surface.display_format
						end
					end
					
					
				when GameConfig::DM_MONOCHROME, GameConfig::DM_COLORFUL
					@front_wall_surfaces = []
					@front_door_surfaces = []
					(0..(VIEWABLE_FRONT_RANGE)).each do |range|
						points = get_points(range, 0)
						width = points[1][0] - points[0][0] + 1
						height = points[2][1] - points[0][1] + 1
						
						# 壁
						surface = SDL::Surface.new(0, width, height, $screen)
						surface.draw_rect(0, 0, width - 1, height - 1, ColorTable::WHITE)
						surface.fill_rect(1, 1, width - 1 - 1, height - 1 - 1, WALL_COLORS[range])
						surface.unlock
						@front_wall_surfaces[range] = surface.display_format
						
						# ドア
						surface = surface.display_format
						door_w = (width * 0.6).to_i
						door_h = (height * 0.8).to_i
						surface.draw_rect(width / 2 - door_w / 2, height - door_h, door_w, door_h, ColorTable::WHITE)
						surface.fill_rect(width / 2 - door_w / 2 + 1, height - door_h + 1, door_w - 1, door_h - 1, DOOR_COLORS[range])
						surface.draw_rect(0, 0, width - 1, height - 1, ColorTable::WHITE)
						surface.unlock
						
						@front_door_surfaces[range] = surface.display_format
					end
			
					@left_wall_surfaces = []
					@right_wall_surfaces = []
					@left_door_surfaces = []
					@right_door_surfaces = []
					(0...(VIEWABLE_FRONT_RANGE)).each do |range|
						@left_wall_surfaces[range] = {}
						@right_wall_surfaces[range] = {}
						@left_door_surfaces[range] = {}
						@right_door_surfaces[range] = {}
						(1..(VIEWABLE_SIDE_RANGE)).each do |pos|
							front_points = LINE_POINTS[range][pos]
							left_points = LINE_POINTS[range + 1][pos - 1]
							width = front_points[0][0] - left_points[1][0] + 1
							height = front_points[2][1] - front_points[0][1]
							next if width <= -1
							
							surface = SDL::Surface.new(0, width, height, $screen)
			
							x1 = x2 = width - 1
							x3 = x4 = 0
							y1 = 0
							y2 = height - 1
							y3 = left_points[0][1] - front_points[0][1]
							y4 = left_points[2][1] - front_points[0][1]
	
							surface.set_color_key(SDL::SRCCOLORKEY | SDL::RLEACCEL, ColorTable::AQUA)
							surface.fill_rect(0, 0, width - 1, height - 1, ColorTable::AQUA)
							surface.draw_line(x1, y1, x3, y3, ColorTable::WHITE)
							surface.draw_line(x2, y2, x4, y4, ColorTable::WHITE)
							surface.draw_line(x1, y1, x2, y2, ColorTable::WHITE)
							surface.draw_line(x3, y3, x4, y4, ColorTable::WHITE)
							door_surface = surface.display_format
							door_w = (width * 0.6).to_i
							door_left = width / 2 - door_w / 2
							door_right = door_left + door_w - 1
							
							white_pixel = surface.map_rgb(*ColorTable::WHITE)
							door_p1x = door_p1y = door_p2x = door_p2y = 0
							
							(1..(width - 1)).each do |x|
								y = 0
								
								until surface.get_pixel(x, y) == white_pixel do
									y += 1
								end
								
								y += 1
								top = y
			
								until surface.get_pixel(x, y) == white_pixel do
									y += 1
								end
								
								height = y - top
								door_h = (height * 0.8).to_i
								door_y = top + height - door_h
							
								surface.fill_rect(x, top, 1, height, WALL_COLORS[range])
								door_surface.fill_rect(x, top, 1, height - 1, WALL_COLORS[range])
								
								if x == door_left then
									door_surface.fill_rect(x, door_y, 1, door_h, ColorTable::WHITE)
									door_p1x = x; door_p1y = door_y
								elsif x == door_right then
									door_surface.fill_rect(x, door_y, 1, door_h, ColorTable::WHITE)
									door_p2x = x; door_p2y = door_y
								elsif (door_left..door_right).include?(x) then
									door_surface.fill_rect(x, door_y, 1, door_h, DOOR_COLORS[range])
									door_surface.fill_rect(x, door_y, 1, 1, ColorTable::WHITE)
								end
							end
							
							surface.unlock
							@right_wall_surfaces[range][pos] = surface.display_format
	
							door_surface.unlock
							@right_door_surfaces[range][pos] = door_surface.display_format
	
						end
						
						((VIEWABLE_SIDE_RANGE * -1)..-1).each do |pos|
							front_points = LINE_POINTS[range][pos]
							right_points = LINE_POINTS[range + 1][pos + 1]
							width = right_points[0][0] - front_points[1][0] + 1
							height = front_points[2][1] - front_points[0][1]
							next if width <= -1
							
							surface = SDL::Surface.new(0, width, height, $screen)
			
							x1 = x2 = 0
							x3 = x4 = width - 1
							y1 = height - 1
							y2 = 0
							y4 = right_points[0][1] - front_points[0][1]
							y3 = right_points[2][1] - front_points[0][1]
							
							surface.set_color_key(SDL::SRCCOLORKEY | SDL::RLEACCEL, ColorTable::AQUA)
							surface.fill_rect(0, 0, width - 1, height - 1, ColorTable::AQUA)
							surface.draw_line(x1, y1, x3, y3, ColorTable::WHITE)
							surface.draw_line(x2, y2, x4, y4, ColorTable::WHITE)
							surface.draw_line(x1, y1, x2, y2, ColorTable::WHITE)
							surface.draw_line(x3, y3, x4, y4, ColorTable::WHITE)
							door_surface = surface.display_format
							door_w = (width * 0.6).to_i
							door_left = width / 2 - door_w / 2
							door_right = door_left + door_w - 1
							
							white_pixel = surface.map_rgb(*ColorTable::WHITE)
							door_p1x = door_p1y = door_p2x = door_p2y = 0
	
							(1..(width - 1)).each do |x|
								y = 0
								
								until surface.get_pixel(x, y) == white_pixel do
									y += 1
								end
								
								y += 1
								top = y
			
								until surface.get_pixel(x, y) == white_pixel do
									y += 1
								end
								
								height = y - top
								door_h = (height * 0.8).to_i
								door_y = top + height - door_h
							
								surface.fill_rect(x, top, 1, height, WALL_COLORS[range])
								door_surface.fill_rect(x, top, 1, height, WALL_COLORS[range])
								
								if x == door_left then
									door_surface.fill_rect(x, door_y, 1, door_h, ColorTable::WHITE)
									door_p1x = x; door_p1y = door_y
								elsif x == door_right then
									door_surface.fill_rect(x, door_y, 1, door_h, ColorTable::WHITE)
									door_p2x = x; door_p2y = door_y
								elsif (door_left..door_right).include?(x) then
									door_surface.fill_rect(x, door_y, 1, door_h, DOOR_COLORS[range])
									door_surface.fill_rect(x, door_y, 1, 1, ColorTable::WHITE)
								end
							
							end
							
							surface.unlock
							@left_wall_surfaces[range][pos] = surface.display_format
	
							door_surface.unlock
							@left_door_surfaces[range][pos] = door_surface.display_format
	
						end
		
					end
				end
			#end

=begin
=end
			super()
			
			
			
			#save_wall_surfaces
	
			return self
		end

		#def save_wall_surfaces
		#	
		#	@left_wall_surfaces.each_index do |range|
		#		@left_wall_surfaces[range].each_pair do |pos, surface|
		#			surface.save_bmp("surface/leftwall_#{range}_#{pos}.bmp") 
		#		end
		#	end
		#	
		#	@right_wall_surfaces.each_index do |range|
		#		@right_wall_surfaces[range].each_pair do |pos, surface|
		#			surface.save_bmp("surface/rightwall_#{range}_#{pos.abs}.bmp") 
		#		end
		#	end
		#
		#end
		
	
		def update
			super
			floor = GS.party.floor
			area_data = floor.get_viewable_area_data(GS.party)
			side_range = VIEWABLE_SIDE_RANGE
			
			floor_base = GS.party.x + GS.party.y
			floor_base += 1 if GS.party.direction == DIR_E or GS.party.direction == DIR_W
			
			if @dungeon_mapping == GameConfig::DM_TEXTURE then
				case GS.party.region
				when :FeatherHome
					# 翼の道用特殊処理
					@front_surface.put((floor_base % 2 == 0 ? @floor_surface1 : @floor_surface2), -25, -25)
				else
					@front_surface.put((floor_base % 2 == 0 ? @floor_and_ceiling_surface1 : @floor_and_ceiling_surface2), -25, -25)
				end
			end
			
			range = VIEWABLE_FRONT_RANGE
			while range >= 0 do
				# 初回だけスキップ
				unless range == VIEWABLE_FRONT_RANGE then
					((side_range * -1)..-1).each do |pos|
						case area_data[range * 2][pos * 2 + 1]
						when Dungeon::Area::WALL
							draw_left_wall(range, pos)
						when Dungeon::Area::DOOR, Dungeon::Area::EXIT_DOOR
							draw_left_door(range, pos)
						end
					end
					
					pos = side_range
					while pos >= 1 do
						case area_data[range * 2][pos * 2 - 1]
						when Dungeon::Area::WALL
							draw_right_wall(range, pos)
						when Dungeon::Area::DOOR, Dungeon::Area::EXIT_DOOR
							draw_right_door(range, pos)
						end
						pos -= 1
					end
				end
				

				((side_range * -1)..(side_range)).each do |pos|
					break if range == 0
					case area_data[range * 2 - 1][pos * 2]
					when Dungeon::Area::WALL
						draw_front_wall(range, pos)
					when Dungeon::Area::DOOR, Dungeon::Area::EXIT_DOOR
						draw_front_door(range, pos)
					end
				end

				
				range -= 1
				
			end
			
			@front_surface.draw_rect(0, 0, @width - 1, @height - 1, ColorTable::WHITE)
			@front_surface.unlock
			
			dir = %W(N E W S)[GS.party.direction]
			sect = "section#{GS.party.room_id}"
			self.draw_text_left("#{GS.party.x}, #{GS.party.y}, #{dir}, #{sect}", 0, 0) if Game.debug_mode?
			
			#dup = @front_surface.display_format
			# SDL.transform(src,dst,angle,xscale,yscale,px,py,qx,qy,flags)
			#SDL.transform(dup, @front_surface, 0, 2.0, 2.0, @front_surface.w / 2, @front_surface.h / 2, @front_surface.w / 2, @front_surface.h / 2, SDL::Surface::TRANSFORM_AA)
			
			
			
			return self
		end
		
		
		

		private		
		def draw_front_wall(range, position)
			points = get_points(range, position)
			x, y = points[0]
			@front_surface.put(@front_wall_surfaces[range], x, y)
		end
		
		def draw_front_door(range, position)
			points = get_points(range, position)
			x, y = points[0]
			@front_surface.put(@front_door_surfaces[range], x, y)
		end
		
		def draw_left_wall(range, position)
			return if range >= VIEWABLE_FRONT_RANGE
			front_points = get_points(range, position)

			surface = @left_wall_surfaces[range][position]
			return unless surface
			
			if @dungeon_mapping == GameConfig::DM_TEXTURE then
				# 画像のサイズが足りないので、それに合わせて修正
				x = front_points[1][0] + 1
				y = front_points[1][1] + 1
			else
				x = front_points[1][0]
				y = front_points[1][1]
			end
			
			# 範囲外なら描画スキップ
			right = x + surface.w
			if right < 0 then
				return
			end
			
			@front_surface.put(surface, x, y) if position >= -3
			#SDL::Surface.blit(surface, src_x, src_y, 0, 0, @front_surface, x, y)
		end

		def draw_left_door(range, position)
			return if range >= VIEWABLE_FRONT_RANGE
			front_points = get_points(range, position)

			surface = @left_door_surfaces[range][position]
			return unless surface
			
			if @dungeon_mapping == GameConfig::DM_TEXTURE then
				# 画像のサイズが足りないので、それに合わせて修正
				x = front_points[1][0] + 1
				y = front_points[1][1] + 1
			else
				x = front_points[1][0]
				y = front_points[1][1]
			end
			
			# 範囲外なら描画スキップ
			right = x + surface.w
			if right < 0 then
				return
			end

			@front_surface.put(surface, x, y) if position >= -3
			#SDL::Surface.blit(surface, x, y, 1000, 1000, @front_surface, x, y)

		end

		
		def draw_right_wall(range, position)
			return if range >= VIEWABLE_FRONT_RANGE
			front_points = get_points(range, position)
			right_points = get_points(range + 1, position - 1)
			
			surface = @right_wall_surfaces[range][position]
			return unless surface
			
			if @dungeon_mapping == GameConfig::DM_TEXTURE then
				# 画像のサイズが足りないので、それに合わせて修正
				x = right_points[1][0]
				y = front_points[0][1] + 1
			else
				x = right_points[1][0]
				y = front_points[0][1]
			end

			# 範囲外なら描画スキップ
			if x > @front_surface.w then
				return
			end

			@front_surface.put(surface, x, y) if position <= 3
		end
		
		def draw_right_door(range, position)
			return if range >= VIEWABLE_FRONT_RANGE
			front_points = get_points(range, position)
			right_points = get_points(range + 1, position - 1)

			surface = @right_door_surfaces[range][position]
			return unless surface
			
			if @dungeon_mapping == GameConfig::DM_TEXTURE then
				# 画像のサイズが足りないので、それに合わせて修正
				x = right_points[1][0]
				y = front_points[0][1] + 1
			else
				x = right_points[1][0]
				y = front_points[0][1]
			end

			# 範囲外なら描画スキップ
			if x > @front_surface.w then
				return
			end

			@front_surface.put(surface, x, y) if position <= 3

		end
		
		def draw_down_stair(range, position)
			return
			p1 = POINTS[range][position]
			p2 = POINTS[range + 1][position]
			
			center_x = (p1[0][0] + p1[1][0]) / 2
			center_y = (p2[2][1] + p1[2][1]) / 2

			
			@front_surface.fill_rect(x, y, width, height, ColorTable::WHITE)
		end


		def get_points(range, position)
			if @dungeon_mapping == GameConfig::DM_TEXTURE then
				TEXTURE_POINTS[range][position]
			else
				LINE_POINTS[range][position]
			end
		end
		

		
	end
	
	
	class DungeonMapWindow < Window
	
		attr_accessor :origin_x, :origin_y, :cursor_x, :cursor_y, :begin_x, :begin_y
		attr_accessor :range_cursor_surface, :cursor_surface, :all_found, :wall_mode
		
		VIEWABLE_WIDTH = 30
		VIEWABLE_HEIGHT = 24
		MODE_NORMAL = 0
		MODE_WALL_EDIT = 1
	
		def initialize
			@width = VIEWABLE_WIDTH * (GRID + 1) + 1 * 2
			@height = VIEWABLE_HEIGHT * (GRID + 1) + 1 * 2
			@origin_x = 0
			@origin_y = 0
			@cursor_x = 0
			@cursor_y = 0
			@all_found = false
			@wall_mode = false
		end
		
		GRID = 10
		
		def reset_origin
			@origin_x = GS.party.x - VIEWABLE_WIDTH / 2
			@origin_y = GS.party.y - VIEWABLE_HEIGHT / 2
			@cursor_x = GS.party.x
			@cursor_y = GS.party.y
			@begin_x = nil
			@begin_y = nil
			return self
		end
		
		def up_cursor
			if nil then #ranging? && @cursor_y == @begin_y then
				return
			elsif @cursor_y >= 1 then
				@cursor_y -= 1
				@origin_y -= 1 if (@cursor_y - @origin_y) <= 0
			end

			
		end
		
		def down_cursor
			if @cursor_y < (GS.party.floor.height - 1) then
				@cursor_y += 1
				@origin_y += 1 if (@cursor_y - @origin_y) >= VIEWABLE_HEIGHT
			end
		end
		
		def right_cursor
			if @cursor_x < (GS.party.floor.width - 1) then
				@cursor_x += 1
				@origin_x += 1 if (@cursor_x - @origin_x) >= VIEWABLE_WIDTH
			end
		end
		
		def left_cursor
			if nil then #ranging? && @cursor_x == @begin_x then
				return
			elsif @cursor_x >= 1 then
				@cursor_x -= 1
				@origin_x -= 1 if (@cursor_x - @origin_x) <= 0
			end

		end
		
		def set_begin
			@begin_x = @cursor_x
			@begin_y = @cursor_y
			return self
		end
		
		def clear_begin
			@begin_x = @begin_y = nil
			return self
		end
		
		def ranging?
			return(@begin_x && @begin_y)
		end
		
		def found_area?(x, y)
			return(@all_found || GS.found_area[GS.party.floor_id]["#{x},#{y}"])
		end
		

		# 正方形マーカーを描画
		def draw_rect_marker(x, y, color, padding = 1)
			@map_surface.fill_rect(draw_x(x) + padding + 1, draw_y(y) + padding + 1, GRID - (padding * 2), GRID - (padding * 2), color)
		end
		

		def make_surface
			super
			
			floor = GS.party.floor
			@map_surface = SDL::Surface.new(SDL::SRCCOLORKEY, draw_x(floor.width) + 1, draw_y(floor.height) + 1, $screen)
			surface = @map_surface
			
			
			(0...(floor.width)).each do |x|
				(0...(floor.height)).each do |y|
					surface.draw_rect(draw_x(x), draw_y(y), GRID + 1, GRID + 1, ColorTable::MAP_GRID)
				end
			end
			
			(0...(floor.width)).each do |x|
				(0...(floor.height)).each do |y|
					
				
					# 床
					if found_area?(x, y) then
						case floor.flooring(x, y)
						when Dungeon::Area::ROOM, Dungeon::Area::ROUTE then
							if Game.debug_mode? then
								color = (floor.flooring(x, y) == Dungeon::Area::ROOM ? ColorTable::MAP_ROOM : ColorTable::MAP)
							else
								color = ColorTable::MAP
							end
							surface.fill_rect(draw_x(x), draw_y(y), GRID + 2, GRID + 2,color)
							
							pos = [x, y]
							if floor.tereport_points.include?(pos) and Game.debug_mode? then
								ttf_draw(Game.regular_ttf, @map_surface, 't', draw_x(x) + 2, draw_y(y), ColorTable::WHITE)
							end
							
							
							length = GRID + 2
							door_length = 2
							door_margin = 3
	
							# 北の壁
							area = floor.wall(x, y, DIR_N)
							case area
							when Area::WALL, Area::DOOR, Area::EXIT_DOOR
								surface.fill_rect(draw_x(x), draw_y(y), length, 1, ColorTable::WHITE)
								case area
								when Area::DOOR, Area::EXIT_DOOR
									surface.fill_rect(draw_x(x) + door_margin, draw_y(y) + 1, 1, door_length, ColorTable::WHITE)
									surface.fill_rect(draw_x(x + 1) - door_margin, draw_y(y) + 1, 1, door_length, ColorTable::WHITE)
								end
							end
						
							# 東の壁
							area = floor.wall(x, y, DIR_E)
							case area
							when Area::WALL, Area::DOOR, Area::EXIT_DOOR
								surface.fill_rect(draw_x(x + 1), draw_y(y), 1, length, ColorTable::WHITE)
								case area
								when Area::DOOR, Area::EXIT_DOOR
									surface.fill_rect(draw_x(x + 1) - door_length, draw_y(y) + door_margin, door_length, 1, ColorTable::WHITE)
									surface.fill_rect(draw_x(x + 1) - door_length, draw_y(y + 1) - door_margin, door_length, 1, ColorTable::WHITE)
									unless floor.passable_door?(x, y, DIR_E) then
										y1 = draw_y(y) + door_margin
										y2 = draw_y(y + 1) - door_margin
										surface.fill_rect(draw_x(x + 1) - door_length, y1, 1, y2 - y1, ColorTable::WHITE)
									end
								end
							end
		
							# 南の壁
							area = floor.wall(x, y, DIR_S)
							case area
							when Area::WALL, Area::DOOR, Area::EXIT_DOOR
								surface.fill_rect(draw_x(x), draw_y(y + 1), length, 1, ColorTable::WHITE)
								case area
								when Area::DOOR, Area::EXIT_DOOR
									x1 = draw_x(x) + door_margin
									x2 = draw_x(x + 1) - door_margin
									surface.fill_rect(x1, draw_y(y + 1) - door_length, 1, door_length, ColorTable::WHITE)
									surface.fill_rect(x2, draw_y(y + 1) - door_length, 1, door_length, ColorTable::WHITE)
									unless floor.passable_door?(x, y, DIR_S) then
										surface.fill_rect(x1, draw_y(y + 1) - door_length, x2 - x1, 1, ColorTable::WHITE)
									end
								end
							end
		
							# 西の壁
							area = floor.wall(x, y, DIR_W)
							case area
							when Area::WALL, Area::DOOR, Area::EXIT_DOOR
								surface.fill_rect(draw_x(x), draw_y(y), 1, length, ColorTable::WHITE)
								case area
								when Area::DOOR, Area::EXIT_DOOR
									y1 = draw_y(y) + door_margin
									y2 = draw_y(y + 1) - door_margin
								
									surface.fill_rect(draw_x(x) + 1, y1, door_length, 1, ColorTable::WHITE)
									surface.fill_rect(draw_x(x) + 1, y2, door_length, 1, ColorTable::WHITE)
									unless floor.passable_door?(x, y, DIR_W) then
										surface.fill_rect(draw_x(x - 1) + door_length, y1, 1, y2 - y1, ColorTable::WHITE)
									end
								end
							end
						end # case floor.flooring?
						
					end # if found_area?
						
				end
			end
			# 現在地
			draw_rect_marker(GS.party.x, GS.party.y, Color::YELLOW)
			
			# 他のパーティ
			(GS.dungeon_parties - [GS.party]).each do |other_party|
				if other_party.floor_id == GS.party.floor_id then
					draw_rect_marker(other_party.x, other_party.y, Color::LIGHT_GREEN)
				end
			end
			
			# イベントポイント
			floor.event_points.each do |x, y|
				if found_area?(x, y) then
					draw_rect_marker(x, y, Color::WHITE, 2)
				end
			end
						

						
			surface.unlock
			@map_surface = surface.display_format
			
			@cursor_surface = SDL::Surface.new(SDL::SRCALPHA, (GRID + 1) + 1,(GRID + 1) + 1, $screen)
			@cursor_surface.fill_rect(0, 0, @cursor_surface.w - 1, @cursor_surface.h, ColorTable::SELECT)
			@cursor_surface.set_alpha(SDL::SRCALPHA, $cursor_alpha)
			@cursor_surface = @cursor_surface.display_format


			@range_cursor_surface = SDL::Surface.new(SDL::SRCALPHA, FLOORS[GS.party.floor_id].width * (GRID + 1) + 1, FLOORS[GS.party.floor_id].height * (GRID + 1) + 1, $screen)
			@range_cursor_surface.fill_rect(0, 0, @range_cursor_surface.w - 1, @range_cursor_surface.h, ColorTable::RED)
			@range_cursor_surface.set_alpha(SDL::SRCALPHA, $cursor_alpha)
			@range_cursor_surface = @range_cursor_surface.display_format
			return self
		end
		
		# 部屋を設定する
		def set_room
			FLOORS[GS.party.floor_id].set_room(@begin_x, @begin_y, (@cursor_x - @begin_x) + 1, (@cursor_y - @begin_y) + 1)
			static_make_surface

		end
		
		# 表示位置やカーソル位置を動かさずにmake_surface
		def static_make_surface
			ox, oy, cx, cy = @origin_x, @origin_y, @cursor_x, @cursor_y
			make_surface
			@origin_x, @origin_y, @cursor_x, @cursor_y = ox, oy, cx, cy
			update
		end
		
		def update
			super
			if @map_surface then
				SDL.blit_surface(@map_surface, @origin_x * (GRID + 1), @origin_y * (GRID + 1), @width, @height, @front_surface, 1, 1)
				cell = GS.party.floor.cells[@cursor_x][@cursor_y]
				draw_text_left("#{@cursor_x}, #{@cursor_y}", 0, 0) if Game.debug_mode?
				if cell.section_id then
					sect = GS.party.floor.sections[cell.section_id]

					if Game.debug_mode? then
						draw_text_left("Region: #{sect.region_id}", 0, 16)
						
						text = "section#{cell.section_id} (a#{sect.area},l#{sect.largeness})"
						if GS.party.floor.flooring(@cursor_x, @cursor_y) == Dungeon::Area::ROOM then
							text += " TRT=#{sect.treasure_type} EA=#{EA_TABLE[sect.enemy_appearance] || sect.enemy_appearance}%"
						end
						draw_text_left(text, 0, 32)
						
						if (troop = GS.room_troops[GS.party.floor_id][cell.section_id]) then
							cap = troop.groups.map{|x| x.first.name + x.size.to_s}.join('/')
							draw_text_left("Troop: #{cap}", 0, 48)
						end
						if (treasure_box = GS.room_treasure_boxes[GS.party.floor_id][cell.section_id]) then
							draw_text_left("Treasure: #{treasure_box.gold}G #{treasure_box.item.name if treasure_box.item}", 0, 64)
						end
					end
				end
			end
			
			case $phase
			when DungeonMapEditPhase
				texts = []
				texts.each_index do |i|
					draw_text_right(texts[i], 0, line_text_top(10 - i), @width - 4)
				end
			end
			
			return self
		end
		
		def blit
			super
			if ranging? then
				# 始点と終点を設定
				if @begin_x < @cursor_x then
					xl = @begin_x; xr = @cursor_x
				else
					xl = @cursor_x; xr = @begin_x
				end
				
				if @begin_y < @cursor_y then
					yt = @begin_y; yb = @cursor_y
				else
					yt = @cursor_y; yb = @begin_y
				end

				x = @left + 1 + ((xl - @origin_x) * (GRID + 1))
				y = @top + 1 + ((yt - @origin_y) * (GRID + 1))
				width = cursor_length(xr - xl + 1)
				height = cursor_length(yb - yt + 1)
				
				SDL.blit_surface(@range_cursor_surface, 0, 0, width, height, $screen, x, y)
			elsif @wall_mode then
				SDL.blit_surface(@cursor_surface, 0, 0, 0, 0, $screen, @left + 1 + (@cursor_x - @origin_x) * (GRID + 1), @top + 1 + (@cursor_y - @origin_y) * (GRID + 1))
			else
				SDL.blit_surface(@cursor_surface, 0, 0, 0, 0, $screen, @left + 1 + (@cursor_x - @origin_x) * (GRID + 1), @top + 1 + (@cursor_y - @origin_y) * (GRID + 1))
			end
		end
		
		private
		def cursor_length(length)
			return(length * (GRID + 1) + 1).abs
		end
		
		def draw_x(x)
			(GRID + 1) * x
		end
		
		def draw_y(y)
			(GRID + 1) * y
		end
	end
	
	class EnemyGraphicWindow < Window
		GHOST_ALPHA = 128
		attr_accessor :enemy
		bool_attr_accessor :hp_visible
	
		def initialize(enemy)
			super()
			@width = enemy.graphic.width
			@height = enemy.graphic.height
			@enemy = enemy
			@hp_visible = false
		end
		
		def current_surface
			@enemy.current_surface
		end
		alias surface current_surface
		
		def set_alpha_rate(rate)
			current_surface.set_alpha(SDL::SRCALPHA | SDL::RLEACCEL, (rate * (@enemy.transparent? ? GHOST_ALPHA : 255)).to_i)
		end
		
		def reset_alpha
			if @enemy.transparent? then
				current_surface.set_alpha(SDL::SRCALPHA | SDL::RLEACCEL, GHOST_ALPHA)
			else
				current_surface.set_alpha(0, 255)
			end
		end
		
		# no act
		def make_surface
			self
		end
		
		# no act
		def clear_surface
			self
		end
		
		

		def update
			if enemy.dead? then
				self.hide
				$enemy_windows.delete(self)
			end
		
			return self
		end
		
		
		
		def blit
			$screen.put(current_surface, @left, @top)

			if @hp_visible then
				gauge_left = @left + (width / 2 - 40 / 2)
				last_hp_rate = @enemy.last_hp.to_f / @enemy.hp_max
				$screen.fill_rect(gauge_left - 1, bottom - 2 - 1, 40 + 2, 2 + 2, Color::BLACK)
				Window.blit_hp_gauge(:hp_small, $screen, gauge_left, bottom - 2, @enemy.hp_rate, last_hp_rate)
			end

			return self
		end		
		

	end

	
	class TextWindow < Window
		attr_accessor :text
		attr_accessor :align, :with_caption
		attr_accessor :color
		

		
		def initialize
			self.set_height_from_line(1)
			super()
			@text = ''
			@align = AL_LEFT
			@color = Color::WHITE
		end
		
		def blit
			super if @text
		end
		
		# テキスト幅から
		def set_good_width(text = @text)
			set_width_from_text(text.split("\n").sort_by{|x| x.length}.last, self.ttf)
		end


		def make_surface(width = nil, line = nil, align = nil)
			self.set_height_from_line(line, self.ttf) if line
			@align = align if align
			if width.kind_of?(String) then
				@text = width
				set_good_width
				width = nil
			end
			super(width, nil)
		end
		
		
		def update(text = nil)
			super()
			@text = text if text
			
			case @text
			when Array
				lines = @text 
			when nil
				return self
			else
				lines = @text.to_s.split("\n")
			end
			
			lines.each_index do |i|
				line = lines[i]
				if @has_caption && i == 0 then
					self.draw_text_center(line, 0, line_text_top(0), self.client_width, @color, ttf)
				else
					case @align
					when AL_CENTER
						self.draw_text_center(line, 0, line_text_top(i), self.client_width, @color, ttf)
					when AL_RIGHT
						self.draw_text_right(line, 0, line_text_top(i), self.client_width, @color, ttf)
					else
						self.draw_text_left(line, 0, line_text_top(i), self.client_width, @color, ttf)
					end
				end
			end
			
			return self
		end
		
		def ttf
			Game.regular_ttf
		end
		
	end
	
	class SmallTextWindow < TextWindow
		def ttf
			Game.column_caption_ttf
		end
	end
	
	class CheatCodeWindow < TextWindow
		def make_surface
			super(240, 1)
		end
		
		def capture_all_keys?
			true
		end
		
		
		def on_key_press(keycode)
			case keycode
			when SDL::Key::RETURN
				throw(:exit_operation_loop, @text)
			when SDL::Key::BACKSPACE, SDL::Key::DELETE
				unless @text.empty? then
					@text.chop!
					update
				end
			when SDL::Key::SPACE
				@text << ' '
				update
			when SDL::Key::ESCAPE
				throw(:exit_operation_loop, nil)
			else
				if keycode.in?((?a)..(?z)) or keycode.in?((?0)..(?9)) then
					if Input.shift_pressed? then
						@text << keycode.chr.upcase
					else
						@text << keycode.chr
					end
					update
				end
			end
		end
	end
	
	class CaptionWindow < TextWindow
		def initialize
			super
			@align = AL_CENTER
		end
	end
	
	class LongTextWindow < TextWindow
	end
	
	class WarningWindow < TextWindow
		def reset(width = 240, target_window = Game.current_window)
			@text = nil
			make_surface(width) unless @width == width
			dock_beside(target_window, :bottom)
			return self
		end
	
		def update(selecting_item = nil)
			if selecting_item then
				@text = selecting_item.warning
			else
				@text = nil
			end
			super()
		end
		
		def blit
			super if @text
		end
	end
	
	
	class TemporaryTextWindow < TextWindow
		def on_enter_key
			select_se
			throw(:exit_operation_loop)
		end
		
		def on_cancel
			self.on_enter_key
		end
	end

	
	class MessageWindow < TemporaryTextWindow
		TOP_Y = 16
		CENTER_Y = 120
		BOTTOM_Y = 200
	
		def initialize
			super
			@align = AL_CENTER
			@inline_tag_usable = true
		end
		
	
		def update(text = nil)
			self.clear_surface
			@text = text if text
			lines = @text.split("\n", -1) # 末尾の空要素は取り除かない
			cy = (self.client_height / 2) - (line_height * lines.size / 2)
			
			lines.each do |line|
				case @align
				when AL_CENTER
					self.draw_text_center(line, 0, cy, self.width, @color, Game.regular_ttf)
				when AL_RIGHT
					self.draw_text_right(line, 0, cy, self.width, @color, Game.regular_ttf)
				else
					self.draw_text_left(line, 0, cy, self.width, @color, Game.regular_ttf)
				end
				cy += line_height
			end
			
			return self
		end
		
		
		
		def on_key_press(keycode)
		end
		
		def on_left_key
		end
		
		def on_right_key
		end
	end

	


	class DoubleTextWindow < Window
		attr_accessor :texts
		attr_accessor :color
		
		def initialize(line = 1, texts = [[]])
			self.set_height_from_line(line)
			super()
			@texts = texts
			@color = ColorTable::WHITE
		end
		
		def set_good_width(texts = @texts)
			unless texts.empty? then
				left_max = texts.sort_by{|x| Window.good_width(x[0] || '')}.last[0]
				right_max = texts.sort_by{|x| Window.good_width(x[1] || '')}.last[1]
				
				self.client_width = Window.good_width(left_max) + Window.good_width(right_max)
			end
			return self
		end
		

		def make_surface(width_base = nil, line = nil)
			self.set_height_from_line(line) if line
			case width_base
			when String
				set_width_from_text(width_base)
				super(nil, nil)
			when Array
				@texts = width_base
				set_good_width
				super(nil, nil)
			else
				super(width_base, nil)
			end
			
		end



		def update(texts = nil)
			@texts = texts if texts
			super()
			@texts.each_index do |index|
				left_text, right_text = @texts[index]
				if @has_caption && index == 0 then
					self.draw_text_center(left_text, 0, line_text_top(index) - 2, self.client_width, @color)
				else
					self.draw_text_left(left_text, 0, line_text_top(index),self.client_width,  @color)
					self.draw_text_right(right_text, 0, line_text_top(index), self.client_width, @color)
				end
			end
	
			return self
		end
		
	end
	
	class TemporaryDoubleTextWindow < DoubleTextWindow
		def on_enter_key
			select_se
			throw(:exit_operation_loop)
		end
		
		def on_cancel
			self.on_enter_key
		end
	end
	
=begin
	
	class DecisionWindow < DoubleTextWindow
		#attr_accessor :yes_proc, :no_proc
		
		
		def make_surface
			@texts = [[_('はい(Y)'), _('いいえ(N)')]]
			
			super(200, 1)
		end
		
		# マウスカーソルを初期位置に合わせる
		def warp
			if GS.game_config.mouse_operation
				SDL::Mouse.warp(@left + @width / 2, @top + @height / 2)
			end
			return self
		end

		
		
	end
=end
	
	class ItemDataWindow < DoubleTextWindow
		LINE = 6
	
		def make_surface(width = 200)
			super(width, LINE)
		end
		
		def update(item = nil)
			@item = item
			
			@texts.clear
			
			if @item.kind_of?(Item) then
				mastery = nil
				case @item.data
				when WeaponModel
					mastery = Game.current_member.get_mastery(MST::WEAPON, @item.data_id)
				when ArmorModel
					mastery = Game.current_member.get_mastery(MST::ARMOR, @item.data_id)
				when ShieldModel
					mastery = Game.current_member.get_mastery(MST::SHIELD, @item.data_id)
				end
				@texts = @item.caption_texts
				
				if mastery and @item.usable_by?(Game.current_member) then
					# 熟練度は一番下の列に表示
					(LINE - @texts.size - 1).times do
						@texts << []
					end
					@texts << [_("熟練度"), sprintf("%d%%", mastery)]
				end
			elsif @item.kind_of?(ItemModel) then
				@texts = ITEM_SAMPLES[@item.id].caption_texts
			end
			
			super()
		end
	end
	
	class CharacterSpecWindow < DoubleTextWindow
		def make_surface(width = 200)
			super(width, 8)
		end
		
		def update(character = nil)
			
			@character = character
			@texts.clear
			if @character then
				#@texts << ["武器属性", "打"]
				@texts << [_('武器威力'), @character.weapon_power]
				n = @character.weapon_hitting_number
				@texts << [_('攻撃回数'), n_("%{n}回", "%{n}回", n).evaluate(:n => n)]
				@texts << [_('命中'), @character.weapon_hitting]
				if @character.spell_caster? then
					@texts << [_('術威力'), @character.magic_power]
					@texts << [_('術制御'), @character.magic_control]
				else
					@texts << [] << []
				end
				
				red = Calc.def2red(@character.defense)
				#@texts << [_('ダメージ軽減'), red]
				#if @character.shield then
				#	shield_red = Calc.def2red(@character.defense + @character.shield.extra_defense) - red
				#	@texts << [nil, "(盾 +#{shield_red})"]
				#else
				#	@texts << []
				#end
				@texts << [_('防護力'), @character.defense]
				if @character.shield then
					@texts << [nil, _("(盾 +%{defense})").evaluate(:defense => @character.shield.extra_defense)]
				else
					@texts << []
				end

				@texts << [_('回避'), @character.avoid]

			end
			super()
		end
	end
	
	
	class NumberInputWindow < TextWindow
		def initialize(default, range, step)
			super()
			@number = default
			@range = range
			@step = step
			@align = AL_RIGHT
		end
		
		def make_surface
			super(60)
		end
		
		def update(number = @number)
			super(number.to_s)
		end
	
		def on_enter_key
			select_se
			throw(:exit_operation_loop, @number)
		end
		
		def on_cancel
			cancel_se
			throw(:exit_operation_loop, nil)
		end
		
		def on_change
			update
		end

		def down
			@number = Util.fold_in_range(@number - @step, @range)
		end
		
		def up
			@number = Util.fold_in_range(@number + @step, @range)
		end
		
		def on_left_key
			SE.cursor_move
			@number = Util.fold_in_range(@number - @step * 10, @range)
			on_change
		end

		
		def on_right_key
			SE.cursor_move
			@number = Util.fold_in_range(@number + @step * 10, @range)
			on_change
		end
	end
	
	
	class SelectableWindow < Window
		attr_accessor :index
		attr_accessor :cursor_indexies # 複数選択用の配列（nilのときは無効）
		attr_accessor :select_items, :right_texts
		attr_accessor :select_y, :select_height, :cursor_surface
		attr_accessor :display_max, :display_top
		bool_attr_accessor :cursor_evermore_visible # この変数がtrueの間は、常にカーソル表示
		bool_attr_reader :mouse_on_up_cursor
		bool_attr_reader :mouse_on_down_cursor
		
		
		def initialize
			@index = 0
			@cursor_indexies = nil 
			@cursor_evermore_visible = false 
			@mouse_on_up_cursor = false 
			@mouse_on_down_cursor = false 
			@mouse_on_
			@select_items = []
	
			@select_y = 0
			@select_height = Game.regular_ttf.line_skip + 6
			@display_max = nil
			@display_top = 0
	
			super
		end
		
		# 選択肢の中で最大の幅を持つものに合わせる
		# ただしright_captionは考慮しない
		def set_good_width
			if (max_item = @select_items.sort_by{|x| Window.good_width(x.caption)}.last) then
				self.client_width = Window.good_width(max_item.caption)
			end
			
			return self
		end

		
		def display_bottom
			return(@display_max ? @display_top + @display_max - 1 : @select_items.size)
		end
		
		def change_display_top(index)
			if index == @display_top then
				return nil
			else
				@display_top = index
				on_display_change
				
				return @display_top
			end
		end
		
	
		def make_surface(width = nil, line = nil)
			self.set_height_from_line(line) if line
			super(width, nil)
			@cursor_surface = SDL::Surface.new(SDL::SRCALPHA, @width - 1*2, @select_height, $screen)
			@cursor_surface.fill_rect(0, 0, @width, @height, ColorTable::SELECT)
			@cursor_surface.unlock
			@cursor_surface.set_alpha(SDL::SRCALPHA | SDL::RLEACCEL, 200)
			@cursor_surface = @cursor_surface.display_format
			
			@display_max = nil
			
			self.reset_index
			self.update
			return self
	
		end
		
		def set_height_from_line(line)
			@height = @select_y + PADDING * 2 + @select_height * line
		end


		def blit
			SDL.blit_surface2(@back_surface, nil, $screen, [@left, @top])
			if cursor_visible? then
				if @cursor_indexies then
					@cursor_indexies.each do |i|
						blit_cursor(i)
					end
				elsif @index then
					blit_cursor(@index)
				end
			end
			#SDL.blit_surface2(@cursor_surface, nil, $screen, [@left + 1, @top + PADDING + @select_y + 2 + @select_height * (@index - @display_top)]) if self == $windows.last && @index
			SDL.blit_surface2(@front_surface, nil, $screen, [@left, @top])			
			return self
		end
		
		def blit_cursor(index)
			SDL.blit_surface2(@cursor_surface, nil, $screen, [@left + 1, @top + PADDING + line_top(index - @display_top)])
		end
		
		def cursor_visible?
			@index and
			(self == Game.current_window or cursor_evermore_visible?) and
			!(mouse_on_up_cursor? or mouse_on_down_cursor?)
		end
		
		def update(select_items = nil)
			@select_items = select_items if select_items
		
			super()
			if @display_max then
				(@display_top..(self.display_bottom)).each do |i|
					draw_select_item_caption(i)
					
				end
			else
				(0...(@select_items.size)).each do |i|
					draw_select_item_caption(i)
				end
			end
			
			# 上下移動カーソル表示
			font = Game.page_cursor_ttf
			if self.display_up_cursor? then
				self.draw_text_right(s_("PageCursor|▲"), 0, 0, self.client_width, ColorTable::WHITE, font)
				
			end
			
			if self.display_down_cursor? then
				self.draw_text_right(s_("PageCursor|▼"), 0, self.client_height - Game.page_cursor_width, self.client_width, ColorTable::WHITE, font)
			end
			
			# スクロールバー
			if self.display_up_cursor? or self.display_down_cursor? then
				bar_height = client_height - Game.page_cursor_height * 2
				x = PADDING + client_width - 8
				y = PADDING + Game.page_cursor_height
				@front_surface.fill_rect(x, y, 2, bar_height, [0x30, 0x30, 0x30])
				
				one_page_rate = (@display_max.to_f / @select_items.size)
				top_rate = (@display_top.to_f / @select_items.size)
				inner_bar_height = (bar_height * one_page_rate).to_i
				@front_surface.fill_rect(x, y + (bar_height * top_rate).to_i, 2, inner_bar_height, Color::WHITE)
			end
			
			return self
		end
		
		def line_top(line_index)
			return(@select_y + @select_height * line_index)
		end
		
		def line_text_top(line_index)
			self.line_top(line_index) + ((@select_height - Game.regular_ttf.height) / 2).to_i
		end
		
		def draw_select_item_caption(index, color = nil)
			item = @select_items[index]
			return unless item
			if item.caption then
				left_color = (item.disabled? ? ColorTable::GRAY : item.left_color)
				caption = item.caption
				#caption = item.caption + self.get_shortcut_caption(item.keycode) if item.keycode
				self.draw_text_left(caption, 0, line_text_top(index - @display_top), client_width, left_color)
			end
			
			if item.right_caption then
				right_color = (item.disabled? ? ColorTable::GRAY : item.right_color)

				if display_up_cursor? or display_down_cursor? then
					self.draw_text_right(item.right_caption, 0, line_text_top(index - @display_top), self.client_width - 16, right_color)
				else
					self.draw_text_right(item.right_caption, 0, line_text_top(index - @display_top), self.client_width, right_color)
				end
			end

		end
		
		def get_shortcut_caption(keycode)
			return "(#{SDL::Key.get_key_name(keycode)})".upcase
		end
		
		def display_up_cursor?
			@display_top >= 1 and @select_items.size >= 1
		end
		
		def display_down_cursor?
			self.display_bottom < (@select_items.size - 1)
		end
		
		def current_id
			return(self.current_item ? self.current_item.id : nil)
		end
		
		def current_item
			return(@index && @select_items[@index] ? @select_items[@index] : nil)
		end
		
		def set_index_from_id(id)
			@index = @select_items.map{|x| x.id if x}.index(id)
			return self
		end
		
		alias set set_index_from_id
		
		#def on_left_key
		#	SE.cursor_move
		#	page_up_shortcut
		#end
		
		#def on_right_key
		#	SE.cursor_move
		#	page_down_shortcut
		#end
		
		def on_left_click(x, y)
			if mouse_on_up_cursor? and display_up_cursor? then
				SE.cursor_move
				@display_top -= 1
				update
				@index = nil
			elsif mouse_on_down_cursor? and display_down_cursor? then
				SE.cursor_move
				@display_top += 1
				update
				@index = nil
			else
				Input.enter
			end
		end
		
		def on_enter_key
			if self.current_item && !(self.current_item.disabled?) then
				self.on_select(self.current_id)
			end
		end
		
		def on_display_change
			self.update if @display_max
		end
		
		def on_change
			WARNING_WINDOW.update(current_item)
			$phase.on_change
		end
		
		def on_select(id)
			select_se
			$phase.on_select(id)
		end
		
		
		
		def regulate_index
		
			# 選択可能なアイテムがなければ、index=nil
			if @select_items.compact.empty? then
				@index = nil
				return self
			end
			
			@index ||= 0
			
			# 下方向にオーバーしていれば一番下の位置とする
			if @index >= @select_items.size then
				@index = (@select_items.size - 1)
			end
			
			# 上方向にオーバーしていれば一番上の位置とする
			if @index <= -1 then
				@index = 0
			end
			
			return self
		end

		# @indexが画面内に収まるように表示を調整
		def regulate_display
			#if Config::PAGE_SCROLL then
			#	self.regulate_page
			#else
				# 選択無し
				if @index.nil? then
					change_display_top(0)
				# カーソルが表示範囲より上にある
				elsif @index < @display_top then
					top_of_bottom_screen = [@select_items.size - @display_max, 0].max
					change_display_top [@index, top_of_bottom_screen].min
				# カーソルが表示範囲より下にある
				elsif @index > self.display_bottom then
					change_display_top [(@index - @display_max + 1), 0].max
				else
					nil
				end
			#end
		
			return self
		end
		
		def regulate_page
			self.page = ((@index || 0) / @display_max).to_i
			return self
		end
		
		def up
			return if @select_items.compact.size <= 1
		
			@index ||= 0
			@index = prev_index(@index, @select_items.size)
			
			# SelectItemがなければスキップ
			while @select_items[@index].nil? do
				@index = prev_index(@index, @select_items.size)
			end
			
			self.regulate_display
			
		end
		
		def down
				
			return if @select_items.compact.size <= 1
			
			@index ||= 0
			@index = next_index(@index, @select_items.size)	
			
			while @select_items[@index].nil? do
				@index = next_index(@index, @select_items.size)
			end
	
			self.regulate_display
			
		end
		
		def page_up_shortcut
			if @index then
				if @display_max.nil? then
					@index = 0
					self.regulate_index
				#elsif Config::PAGE_SCROLL then
				#	@index -= @display_max
				#	self.regulate_index.regulate_page
				else
					# index移動
					@index -= (@display_max - 1)
					regulate_index
					
					# 表示位置移動
					change_display_top [@display_top - (@display_max - 1), 0].max
	
				end
			end
			self.on_change
			
		end
		
		def page_down_shortcut
			if @index then
				if @display_max.nil? then
					@index = @select_items.size - 1
					self.regulate_index
				#elsif Config::PAGE_SCROLL then
				#	@index += @display_max
				#	self.regulate_index.regulate_page
				else
					# index移動
					@index += (@display_max - 1)
					regulate_index
					
					# 表示位置移動
					if page_max >= 2 then
						inc = (@display_max - 1)
						if self.display_bottom + inc >= @select_items.size then
							change_display_top(@select_items.size - @display_max)
						else
							change_display_top(@display_top + inc)
						end
					end
				end
			end
			self.on_change

			
		end
		
		def page
			return (@display_top / @display_max).to_i
		end
		
		def page=(value)
			change_display_top(@display_max * value)
		end
		
		def page_top
			return(self.page * @display_max)
		end
		
		def page_max
			return((@select_items.size / @display_max).to_i + 1)
		end
		
		# マウスカーソルを選択肢の位置に合わせる
		def warp(index = @index)
			if index and GS.game_config.mouse_operation then
				index_on_display = @index - @display_top
				SDL::Mouse.warp(@left + @width / 2, @top + @select_y + (@select_height * index_on_display) + @select_height / 2)
			end
			return self
		end
		
		def reset_index
			@index = 0
			change_display_top(0)
				
			if @select_items.compact.size >= 1 then
				while @select_items[@index].nil? do
					@index = next_index(@index, @select_items.size)
				end
			end

			if @select_items.compact.find{|x| !(x.disabled?)} then
				while @select_items[@index].disabled? do
					@index = next_index(@index, @select_items.size)
				end
			end
			
			regulate_display
			
			return self
		end
		
		
		
		def on_mouse_move(x, y)
			index = get_mouse_index(x, y)
			if index and index != @index then
				change(index)
			end
			
			margin = 6
			if display_up_cursor? and
			x >= client_right - Game.page_cursor_width - margin and x <= client_right + margin and
			y >= client_top - margin and y <= client_top + Game.page_cursor_height + margin then
				@mouse_on_up_cursor = true
			else
				@mouse_on_up_cursor = false
			end
			
			if display_down_cursor? and
			x >= client_right - Game.page_cursor_width - margin and x <= client_right + margin and
			y >= client_bottom - Game.page_cursor_height - margin and y <= client_bottom + margin then
				@mouse_on_down_cursor = true
			else
				@mouse_on_down_cursor = false
			end

		end
		
		def get_mouse_index(x, y)
			return unless GS.game_config.mouse_operation
			
			
			top = @top + @select_y + PADDING + 2
			if @display_max then
				bottom = top + @select_height * [@select_items.size, @display_max].min
			else
				bottom = top + @select_height * @select_items.size
			end

			if x >= @left and x <= self.right and
			y >= top and y <= bottom then
				index = ((y - top) / @select_height) + @display_top
				return (@select_items[index] ? index : nil)
			else
				return nil
			end
		end

		
		# 選択肢を選ぶ。indexがnilなら選択解除
		def change(index)
			@index = index
			self.on_change if @index
			return self
		end
	
	end
	
	
	
	class TemporarySelectableWindow < SelectableWindow
		
	
		def on_select(id)
			select_se
			throw(:exit_operation_loop, id)
		end
		
		def on_cancel
			cancel_se
			throw(:exit_operation_loop, :cancel)
		end

		def on_key_press(keycode)
			return
		end
	end
	
	
	class SelectItem
		attr_accessor :id, :real_id, :caption, :keycode, :right_caption, :left_color, :right_color
		bool_attr_accessor :enabled
		attr_accessor :warning, :description
		bool_attr_accessor :need_ctrl_key
			
		def initialize(id, caption, shortcut_key = nil, right_caption = nil)
			@id = id
			@real_id = id
			@caption = caption
			@right_caption = right_caption
			@left_color = ColorTable::WHITE
			@right_color = ColorTable::WHITE
			@enabled = true
			@warning = nil
			@description = nil
			
			@keycode = nil
			@need_ctrl_key = false
			self.set_shortcut(shortcut_key) if shortcut_key
			
		end
		
		def set_shortcut(key)
			if key.kind_of?(Fixnum) then
				@keycode = key
			elsif key.kind_of?(Array) then
				case key[0]
				when :ctrl
					@need_ctrl_key = true
				end
				
				@keycode = key[1]
			else
				if key.to_s =~ /^[0-9]$/ then
					@keycode = SDL::Key.const_get("K#{key}")
				elsif key =~ /^[A-Z+]$/ then
					@keycode = SDL::Key.const_get(key)
				end
			end
			
			return self
		end
		
		
		def enabled?
			!(disabled?)
		end
		
		def disabled?
			@id.nil? or not enabled
		end
		
		def enable
			@enabled = true
			return self
		end
		
		def disable(warning = nil)
			@enabled = false
			@warning = warning
			return self
		end
		
		

	end
	
	
	class ItemWindow < SelectableWindow
		bool_attr_accessor :sortable
		
		def initialize
			super
			@sortable = false
		end
	
		def make_surface
			set_width
			super(nil, ITEM_MAX + 1)
			
		end
		
		def set_width
			self.client_width = Game.char_width * (2 + 28)
		end
		
	
		def update(disp_shortcut = false)
			@select_items.clear
			member = Game.current_member
			member.items.each_index do |i|
				item = member.items[i]
				if disp_shortcut then
					symbol = "#{(i+1) % 10}:"
				elsif member.equipped?(item) then
					symbol = _('E ')
				else
					symbol = '  '
				end
				@select_items << SelectItem.new(i, symbol + item.name)
				@select_items.last.set_shortcut(((i+1) % 10).to_s) if disp_shortcut
				set_right_captions
				@select_items.last.disable if check_disable(item)
			end
			@select_items << SelectItem.new(:sort, '  ' + _('整列(S)'), SDL::Key::S) if sortable?
			
			super(nil)
		end
		
		def set_right_captions
		end
		
		def check_disable(item)
			false
		end
	end
	
	class BattleItemWindow < ItemWindow
		def check_disable(item)
			item.effect.nil? or not item.effect.battle_usable?
		end
	end
	
	class CustomizeItemWindow < ItemWindow
		def check_disable(item)
			not item.data.kind_of?(EquipmentModel) or item.no_arrange?
		end
	end
	
	class ItemSellWindow < ItemWindow
		def set_width
			self.client_width = Game.char_width * (2 + 24 + 6)
		end
		
		def set_right_captions
			@select_items.each do |select|
				select.right_caption = Game.current_member.items[select.id].sell_price.to_i
			end
		end
	end

	class IdentificationWindow < ItemWindow
		def set_width
			self.client_width = Game.char_width * (2 + 24 + 6)
		end
		
		def set_right_captions
			@select_items.each do |select|
				select.right_caption = (Game.current_member.items[select.id].unidentified? ? IDENTIFICATION_COST.to_s : "")
			end
		end
		
		def check_disable(item)
			!(item.unidentified?) || Game.current_member.gold < IDENTIFICATION_COST
		end

	end
	
	class ItemCustomizeWindow < ItemWindow
		
		def check_disable(item)
			not item.respond_to?(:set_prefix) or item.no_arrange?
		end

	end

	
	

	
	class MultiColumnWindow < SelectableWindow
		attr_accessor :columns
	
		def initialize
			super
			@columns = []
			@select_y = Game.column_caption_ttf.line_skip
		end
		
		def set_good_width
			unless @columns.empty? then
				total = 0
				@columns.each do |column|
					total += column.width + column.right_margin
				end
				
				#total -= @columns.last.right_margin
				
				self.client_width = total
			end
			
			return self
		end
		
		def set_good_width_from_column
			unless @columns.empty? then
				total = 0
				@columns.each do |column|
					column.width = column.captions.map{|x| Window.good_width(x, Game.regular_ttf)}.max - 16
				end
				
				#total -= @columns.last.right_margin
				
				set_good_width
			end
			
			return self
		end
		
		def update
			super
			cx = 0
			@columns.each do |column|
	
				if column.header then
					if column.align == AL_RIGHT then
						draw_text_right(column.header, cx, 0, column.width, ColorTable::WHITE, Game.column_caption_ttf)
					else
						draw_text_center(column.header, cx, 0, column.width, ColorTable::WHITE, Game.column_caption_ttf)
					end
				end
				
				column.draw_captions(self, cx)
				
				cx = cx + column.width + column.right_margin
			end
			
			return self
		end
		
		def clear_captions
			@columns.each{|x| x.captions.clear}
		end
		
	end
	
	class TemporaryMultiColumnWindow < MultiColumnWindow
		
	
		def on_select(id)
			select_se
			throw(:exit_operation_loop, id)
		end
		
		def on_cancel
			cancel_se
			throw(:exit_operation_loop, :cancel)
		end
	end

	
	
	class ColumnItem
		attr_accessor :header, :width, :right_margin, :captions, :align
		bool_attr_accessor :drawing_border_break
		
		def initialize(header, captions, width, align = AL_CENTER, right_margin = 30)
			@header = header
			@width = width
			@captions = captions
			@align = align
			
			@right_margin = right_margin
			@drawing_border_break = false
	
		end
		
		def drawing_area_width
			(@drawing_border_break ? SCREEN_WIDTH : @width)
		end
		
		def draw_captions(window, left)
			if window.display_max then
				cps = []
				((window.display_top)..(window.display_bottom)).each do |i|
					cps << @captions[i]
				end
			else
				cps = @captions
			end
			
			cps.each_index do |i|
				caption = cps[i]
				if caption then
					case @align
					when AL_CENTER
						window.draw_text_center(caption, left, window.line_text_top(i), drawing_area_width)
					when AL_LEFT
						window.draw_text_left(caption, left, window.line_text_top(i), drawing_area_width)
					when AL_RIGHT
						window.draw_text_right(caption, left, window.line_text_top(i), drawing_area_width)
					end
				end
				
			end

		end
	end
	


	class YNWindow < SelectableWindow
		attr_accessor :yes_proc, :no_proc
				
		def make_surface
			@select_items.clear
			@select_items << SelectItem.new(:yes, _('はい(Y)'), SDL::Key::Y)
			@select_items << SelectItem.new(:no, _('いいえ(N)'), SDL::Key::N)

			super(120, 2)
		end
		
		def on_right_key
			change(1)
		end
		
		def on_left_key
			change(0)
		end

		def on_select(id)
			case id
			when :yes
				select_se
				throw(:exit_operation_loop, true)
			when :no
				cancel_se
				throw(:exit_operation_loop, false)
			end
		end

		def on_cancel
			self.on_select(:no)
		end
		
	end















	class CharacterListWindow < MultiColumnWindow
		attr_reader :extra_columns, :members
		bool_attr_accessor :gold_visible
		
		def initialize
			super
			@extra_columns = []
			@members = []
			@gold_visible = true
		end
		
		def default_width
			SCREEN_WIDTH - 8 * 2
		end
		
		def make_surface(line)
			super(default_width, line)
			@left = 8



			return self
		end
		
		
		def viewable_members
			@members[(@display_top)..(self.display_bottom)]
		end
		
		def update_items(members = nil, extra_columns = nil)
			@members = members if members
			@columns.clear
			
			@columns << get_first_column
			@columns.last.right_margin = 0
			
			
			
			@columns << HPColumn.new(s_("ColumnCaption|HP"), [], 100, AL_LEFT, 20)
			
			if extra_columns then
				@extra_columns = extra_columns
			end
			
			# 盾表示用の空間
			@columns << ColumnItem.new("", [], Game.char_width*3, AL_LEFT, 8)

			
			if @extra_columns && @extra_columns.empty? then
				@columns.last.captions = @members.map{|x| x.shield_usable? && $section == BATTLE ? GS.game_config.shield_mark : ""}

				if $phase.kind_of?(TreasurePhase) then
					@columns << ColumnItem.new(s_("ColumnCaption|UNLOCK"), @members.map{|x| x.skill_levels[SKILL::UNLOCK]}, 50, AL_RIGHT, 10)
					@columns << ColumnItem.new(s_("ColumnCaption|RM.TRAP"), @members.map{|x| x.skill_levels[SKILL::REMOVE_TRAP]}, 50, AL_RIGHT, 10)
				elsif $phase.kind_of?(BattleResultPhase) then
					captions = @members.map{|x| (x.last_got_gold >= 1 ? "+#{x.last_got_gold}" : '')}
					if gold_visible? then
						@columns << ColumnItem.new(s_("ColumnCaption|GOLD"), captions, 50, AL_RIGHT, 10)
					end
					captions = @members.map do |member|
						if member.last_got_exp >= 1 then
							rate = member.last_got_exp.to_f / EXP_NEXT_TABLE[member.level + 1]
							sprintf("+%3d%%", (rate * 100).to_i)
						else
							''
						end
					end
					@columns << ColumnItem.new(s_("ColumnCaption|EXP-RATE"), captions, 70, AL_RIGHT)
				#elsif $section == TOWN then
				#	@columns << ColumnItem.new(s_("ColumnCaption|GOLD"), @members.map{|x| x.gold}, 60, AL_RIGHT)
				else
					captions = @members.map do |member|
						if member.shield_usable? then
							sprintf("%2d%+3d", member.defense, member.shield.extra_defense)
						else
							sprintf("%2d   ", member.defense)
						end
					end
					#@columns << ColumnItem.new('DEF', @members.map{|x| x.defense}, 40, AL_RIGHT, 0)
					#@columns << ColumnItem.new('', @members.map{|x| "+#{x.shield.extra_defense}" if x.shield_usable?}, 30, AL_RIGHT, 0)
					#@columns << ColumnItem.new('AVD', @members.map{|x| x.constant_avoid}, 40, AL_RIGHT, 0)
					if gold_visible? then
						@columns << ColumnItem.new(s_("ColumnCaption|GOLD"), @members.map{|x| x.gold}, 50, AL_RIGHT, 10)
					end
					@columns << ColumnItem.new(s_("ColumnCaption|CONDITION"), @members.map{|x| x.condition_caption}, 180, AL_CENTER)
				end
			else
				if gold_visible? then
					@columns << ColumnItem.new(s_("ColumnCaption|GOLD"), @members.map{|x| x.gold}, 50, AL_RIGHT, 10)
				end
				@columns += @extra_columns
			end
	
			@select_items.clear
			@members.each_index do |i|
				@select_items << SelectItem.new(i, nil)
				
			end
			
			return self
		end
		
		def get_first_column
			ColumnItem.new("", [], Game.char_width * 2, AL_LEFT)
		end
		
		def show_shortcut
			@select_items.each_index do |i|
				@columns.first.captions[i] = "#{i+1}:"
			end
			return self
		end
		

		
		
		def add_extra_item(id, caption, keycode = nil)
			@select_items << SelectItem.new(id, caption, keycode)
		end
		
	
		
		alias update_surface update
		
		def update(members = nil, extra_columns = nil)
			self.update_items(members, extra_columns)
			self.update_surface
			return self
		end
		
		
		
		class NameColumn < ColumnItem
			def draw_captions(window, left)
			
				

				members = window.viewable_members
				members.each_index do |i|
					if window.select_items[i].nil? or window.select_items[i].disabled? then
						color = ColorTable::GRAY
					elsif members[i].dead? then
						color = ColorTable::RED
					elsif $phase.kind_of?(BattleRoundPhase) then
						if $current_action && members[i] == $current_action.actor then
							color = ColorTable::BLUE
						else
							color = ColorTable::WHITE
						end
					elsif i == $member_index && members[i].kind_of?(Member) then
						case $phase
						when MemberChangablePhase, BattleMemberCommandPhase
							color = ColorTable::BLUE
						else
							color = ColorTable::WHITE
						end
					else
						color = ColorTable::WHITE
					end
					
					window.draw_text_left(members[i].name, left, window.line_text_top(i), @width, color)
				end
	
			end
			
		end
		
	
		class HPColumn < ColumnItem
			def draw_captions(window, left)
				members = window.viewable_members
				members.each_index do |i|
					member = members[i]
					text_top = window.line_text_top(i)
					gauge_top = PADDING + window.line_top(i+1) - 2
					
					hp_rate = (member.dead? ? 0 : member.hp_rate)
					last_hp_rate = (member.dead? ? 0 : member.last_hp.to_f / member.hp_max)
					hp_max_rate = member.hp_max.to_f / member.hp_max_base
					Window.blit_hp_gauge(:hp, window.front_surface, left, gauge_top, hp_rate, last_hp_rate, hp_max_rate)


					if member.dead? then
						if member.soul? then
							window.draw_text_right(_('SOUL'), left, text_top, 40, ColorTable::RED)
						else
							window.draw_text_right(_('DEAD'), left, text_top, 40, ColorTable::RED)
						end
					elsif member.hp <= 0 then
						window.draw_text_right(member.hp, left, text_top, 40, ColorTable::RED)
					else
						window.draw_text_right(member.hp, left, text_top, 40)
					end
					
					window.draw_text_center("/", left + 40, text_top, 20)
					window.draw_text_right(member.hp_max, left + 40 + 20, text_top, 40, (member.hp_max_penalty > 0 ? [255, 160, 160] : ColorTable::WHITE))


				end
	
			end
		end
		
		
		class ClassColumn < ColumnItem
			def draw_captions(window, left)
				super
				members = window.viewable_members
				members.each_index do |i|
					member = members[i]
					next if member.kind_of?(Enemy)
					text_top = window.line_text_top(i)
					gauge_top = PADDING + window.line_top(i+1) - 2
					gauge_width = Game.gauge_surfaces[:exp][:back].w
					gauge_left = left + 3
					
					unless member.level_max? then
						exp_rate = (member.exp - Member::EXP_TABLE[member.level]).to_f / Member::EXP_NEXT_TABLE[member.level+1].to_f
						exp_rate = 1.0 if exp_rate > 1.0
						exp_rate = 0.0 if exp_rate < 0.0
						window.front_surface.put(Game.gauge_surfaces[:exp][:back], gauge_left, gauge_top)
						SDL.blit_surface(Game.gauge_surfaces[:exp][:base], 0, 0, (exp_rate * gauge_width).to_i, 2, window.front_surface, gauge_left, gauge_top)
					end
	
				end

	
			end
		end


	end
	
	
	class MemberListWindow < CharacterListWindow
		def update_items(members = nil, extra_columns = nil)
			super(members, extra_columns)
			col1 = NameColumn.new(s_("ColumnCaption|NAME"), [], 100, AL_LEFT)
			col2 = ClassColumn.new(s_("ColumnCaption|CLASS"), @members.map{|x| "#{x.class_initial}:L#{x.level}#{'+' if x.levelup?}"}, 60, AL_CENTER)
			@columns.insert(1, col1, col2)


		end
	end
		
	class PartyWindow < MemberListWindow
	
		def make_surface
			super(MEMBER_MAX + 1)
			self.bottom = SCREEN_HEIGHT - 8
			
			return self
		end
		
		def update_items(extra_columns = nil)
			GS.party.update_formation
			super(GS.party.members, extra_columns)
	
			@select_items.each_with_index do |item, i|
				item.set_shortcut((i + 1).to_s)
			end
			show_shortcut if $phase.kind_of?(MemberChangablePhase)
		end
		
		def update(extra_columns = nil)
			self.update_items(extra_columns)
			self.update_surface
			return self
		end

		
		def get_first_column
			ColumnItem.new("", @members.map{|x| x.position_caption}, Game.char_width * 2, AL_LEFT)
		end
		
		def on_select(id)
			select_se
			throw(:exit_operation_loop, id)
		end
		
		def on_cancel
			cancel_se
			throw(:exit_operation_loop, nil)
		end

	end
	
	class OtherMemberWindow < MemberListWindow
		def make_surface(line)
			super(line)
			self.top = 8
			@display_max = line
			return self
		end
	end
	
	
	class EnemyInspectWindow < CharacterListWindow
		#def default_width
		#	400
		#end

		def update_items(members = nil, extra_columns = nil)
			super(members, extra_columns)
			@columns.insert(1, NameColumn.new("NAME", [], 100, AL_LEFT))


		end


		def make_surface(line)
			super(line)
			@display_max = line
			return self
		end
	end
	
	class EnemyWindow < SelectableWindow
		def make_surface
			super(PARTY_WINDOW.width, 4)
			@top = 8
			self.set_position(:center, nil)
			return self
		end
		
		def update_items(troop = $troop)
			@select_items.clear
			troop.groups.each_index do |i|
				group = troop.groups[i]
				next if group.empty?
				
				caption = n_("%{enemy} - %{number}体", "%{enemy} - %{number}体", group.size)
				caption = caption.evaluate(:enemy => group.first.name_of_number(group.size), :number => group.size)
				item = SelectItem.new(i, "#{i+1}:#{caption}")
				active = group.find_all{|x| x.active?}.size
				item.right_caption = _("(行動可:%{number})").evaluate(:number => active) if active < group.size 
				item.set_shortcut((i+1).to_s)
				@select_items << item
			end
			return self
		end
		
		alias :update_surface :update
		
		def update(troop = $troop)
			self.update_items(troop)
			self.update_surface
			return self
		end
	end


	
	
	class BuyListWindow < MultiColumnWindow
		BOW_MODEL = Weapon.new(:LongBow)
		AUTOBOW_MODEL = Weapon.new(:CrossBow)
		
		attr_reader :description_window, :name_window
		attr_reader :data_window, :help_window
		attr_accessor :products, :product_symbols
		
		
		def initialize
			super
			@product_data = []
		end
		
		
		def current_product
			if current_id.kind_of?(Integer) then
				@product_data[current_id][0]
			else
				nil
			end
		end
		
		
		def update(product_data = nil)
			member = Game.current_member
			@product_data = product_data if product_data
		
			# 商品リスト設定
			@columns.clear
			
			@columns << ColumnItem.new(s_("ColumnCaption|NAME"), [], 200, AL_LEFT)
			@columns.last.right_margin = 0
			@columns << ColumnItem.new(s_("ColumnCaption|PRICE"), [], 40, AL_RIGHT, 16)
			@columns << ColumnItem.new(s_("ColumnCaption|STOCK"), [], 40, AL_RIGHT)
			
	
			@select_items.clear
			
			@select_items << SelectItem.new(:share_gold, _('お金を山分けする(S)'), SDL::Key::S)
			@select_items << SelectItem.new(:gather_gold, _('お金を集める(G)'), SDL::Key::G)
			@columns[1].captions << '' << ''
			@columns[2].captions << '' << ''

	
			@product_data.each_with_index do |data, i|
				product, stock = data
			
				next unless product.usable_by?(member)
				Game.on_found('item', product.data_id)
				@columns[1].captions << product.price
				@columns[2].captions << (stock ? stock.to_s : "$[disablecolor]--")
	
				@select_items << SelectItem.new(i, product.name)
				@select_items.last.disable if member.gold < product.price
			end
			
			
			super()
		end
		
	end


	

	
	
	class MasterableWindow < Window
		
		def make_surface
			self.set_height_from_line(6)
			super(300)
		end
		
		def update(member_class = Fighter)
			super()
			member = member_class.new

			
			cx = 0
			cl = 0

			EQUIPMENT_SAMPLE_DATA.each_with_index do |data, i|
				caption, id = data
				y = line_text_top(cl)
				
				color = (DB.find_item(id).usable_by?(member) ? ColorTable::WHITE : ColorTable::GRAY)
				draw_text_left(caption, cx, y, client_width, color)
				if i.modulo(6) == (6 - 1) then
					cx += self.client_width / 3
					cl = 0
				else
					cl += 1
				end
			end
			
			return self
		end
	end




	DUNGEON_WINDOW = DungeonWindow.new
	DESCRIPTION_WINDOW = TextWindow.new
	DESCRIPTION_WINDOW.width = SCREEN_WIDTH - 16 * 2
	DESCRIPTION_WINDOW.text_resizing = true
	
	INFORMATION_WINDOW = TextWindow.new
	
	SEAL_WINDOW = TextWindow.new
	SEAL_WINDOW.client_width = Game.char_width * 20
	SEAL_WINDOW.align = AL_CENTER

	CAPTION_WINDOW = TextWindow.new
	NAME_WINDOW = TextWindow.new
	MESSAGE_WINDOW = MessageWindow.new
	DATA_WINDOW = DoubleTextWindow.new
	HELP_WINDOW = DoubleTextWindow.new
	DEBUG_DATA_WINDOW = TemporaryDoubleTextWindow.new
	ITEM_DATA_WINDOW = ItemDataWindow.new
	SELECT_WINDOW = SelectableWindow.new
	MULTI_COLUMN_WINDOW = MultiColumnWindow.new
	YN_WINDOW = YNWindow.new
	PARTY_WINDOW = PartyWindow.new
	ENEMY_WINDOW = EnemyWindow.new
	WARNING_WINDOW = WarningWindow.new


	
end
