=begin ■フキダシ型文章表示 RGSS2 DAIpage■ v1.1 ●機能● ・「文章の表示」コマンドを直感的に扱えるフキダシ型に変更します。 ・通常の「文章の表示」との併用ができます。 ・ホールド機能で複数のウインドウを表示できます。 ・面倒なウインドウ表示位置設定が必要なく、キャラ指定だけで  極力自然な位置に表示されます。 ●素材規格●  ウインドウに重ねるフキダシ画像が必要です。  32ピクセル四方の下向き方向、上向き方向を横に並べます。(合計幅64px)  下向き画像の上2ピクセル及び上向き画像の下2ピクセルはウインドウに重なります。  それをゲーム内で用いる種類だけ縦に並べた画像です。  systemフォルダに"fukidashi"というファイル名でインポートして下さい。 ●追加される制御文字● \P[n]…キャラ指定  ※ポップアップするキャラを指定します。   nにイベントIDを指定して下さい。0でこのイベント、-1でプレイヤーになります。   \HL…ホールド  ※文章が終了してもウインドウを消去しません。   なお、他の\HLでないウインドウ消去時に一緒に消去されます。 ●スクリプト制御● ※以下はイベントコマンドのスクリプトで実行します。 ★ポップアップモードの切り替え★  $game_message.pop = true / false  # trueでフキダシモード、falseで通常のウインドウに。初期値はfalse。 ★フキダシ画像の切り替え★  $game_message.pop_no = n  # インポートした画像の上からn番目に変える。初期値は0。 ★ホールドウインドウ消去★  pop_clear  # 全てのメッセージウインドウを消去します。 ★強制画面内表示切り替え★  $game_message.screen_in = true / false  # 全てのメッセージウインドウを消去します。初期値はtrue。 ●仕様詳細● ・ポップアップは上下の2方向から自動で座標計算し表示される。  基本的には下向きのキャラは上方向に、それ以外は下方向にポップアップ。  既にその位置にウインドウが存在する場合は逆方向になる。  多数のウインドウが存在する場合、最も重なりの少ない位置にポップアップ。 キャラクター画像の高さも考慮します。  プレイヤーorしゃべっているキャラとウインドウが重なりにくいようになっている。 ・強制画面内表示がオンの場合の動作  画面からウインドウがはみ出る場合に自動補正がかかる。  画面外のキャラにポップアップした場合、フキダシ画像は表示されない。 ・制御文字 \P[n] でポップアップするキャラを指定。  -1はプレイヤー、0はこのイベント。  この制御文字が含まれない文章は全て「このイベント」にポップ。 ・制御文字 \HL が文章に含まれる場合、そのウインドウは消えません。  \HLが含まれない文章を消去時にまとめて消去される。  pop_clear をイベントコマンド:スクリプトで実行しても消去可能。 ・選択肢の表示・数値入力の処理は全て強制的にプレイヤーからポップアップする。 ・文章の背景「暗くする」「透明」時、顔グラが設定されていると通常の表示になる。 ●再定義している箇所●  Game_Message、Game_Interpreter、Window_Messageをエイリアス。  ※同じ箇所を変更するスクリプトと併用した場合は競合する可能性があります。 ●更新履歴● 10/01/01:ウインドウが消える際のフキダシ画像の消去を挙動調整。  09/11/30:公開 =end #============================================================================== # ■ Game_Message #============================================================================== class Game_Message #-------------------------------------------------------------------------- # ● 公開インスタンス変数 #-------------------------------------------------------------------------- attr_accessor :pop # ポップアップモード attr_accessor :pop_no # ポップアップ画像インデックス attr_accessor :pop_clear # ポップアップ消去フラグ attr_accessor :screen_in # 強制画面内表示 #-------------------------------------------------------------------------- # ● オブジェクト初期化 #-------------------------------------------------------------------------- alias initialize_dai_pop initialize def initialize initialize_dai_pop @pop = false @pop_no = 0 @pop_clear = false @screen_in = true end end #============================================================================== # ■ Game_Interpreter #============================================================================== class Game_Interpreter #-------------------------------------------------------------------------- # ● 文章の表示 #-------------------------------------------------------------------------- alias command_101_dai_pop command_101 def command_101 unless $scene.is_a?(Scene_Map) && $game_message.pop command_101_dai_pop return false end unless $game_message.busy $game_message.face_name = @params[0] $game_message.face_index = @params[1] $game_message.background = @params[2] $game_message.position = @params[3] @index += 1 while @list[@index].code == 401 # 文章データ $game_message.texts.push(@list[@index].parameters[0]) @index += 1 end set_message_waiting # メッセージ待機状態にする end return false end #-------------------------------------------------------------------------- # ● 全ポップアップ画像消去 #-------------------------------------------------------------------------- def pop_clear $game_message.pop_clear = true end end #============================================================================== # ■ Window_Message #============================================================================== class Window_Message < Window_Selectable #-------------------------------------------------------------------------- # ● オブジェクト初期化 #-------------------------------------------------------------------------- alias initialize_dai_pop initialize def initialize initialize_dai_pop @popup_character = nil @sub_windows = [] @hold = false @lst_pos = [0, 0] create_sprite end #-------------------------------------------------------------------------- # ● ポップアップモード判定 #-------------------------------------------------------------------------- def pop? return false if $game_message.background > 0 return false if $game_message.face_name != "" return $game_message.pop end #-------------------------------------------------------------------------- # ● フキダシ画像作成 #-------------------------------------------------------------------------- def create_sprite @sprite = Sprite.new(self.viewport) @sprite.bitmap = Cache.system("fukidashi") @sprite.src_rect.set(0, 0, 32, 32) @sprite.z = self.z + 1 @sprite.visible = false end #-------------------------------------------------------------------------- # ● フキダシ画像更新 #-------------------------------------------------------------------------- def update_sprite @sprite.update @sprite.opacity = self.openness @sprite.visible = (self.openness > 0 && pop? && @v) @sprite.visible = false if @closing end #-------------------------------------------------------------------------- # ● フキダシ画像解放 #-------------------------------------------------------------------------- def dispose_sprite @sprite.dispose end #-------------------------------------------------------------------------- # ● フキダシ画像座標セット #-------------------------------------------------------------------------- def sprite_set c = @popup_character @sprite.x = c.screen_x - 16 n = $game_message.pop_no * 32 if c.screen_y < self.y @sprite.src_rect.set(32, n, 32, 32) @sprite.y = self.y - 30 else @sprite.src_rect.set(0, n, 32, 32) @sprite.y = self.y + self.height - 3 end @v = true if $game_message.screen_in if c.screen_x < 0 or c.screen_x > Graphics.width or c.screen_y < 0 or c.screen_y > Graphics.height @v = false end end end #-------------------------------------------------------------------------- # ● ウィンドウサイズを標準に戻す #-------------------------------------------------------------------------- def size_reset size_set(0, 288, Graphics.width, 128) end #-------------------------------------------------------------------------- # ● ウィンドウサイズ・座標を変更 #-------------------------------------------------------------------------- def size_set(x, y, width, height) self.x, self.y, self.width, self.height = x, y, width, height self.contents.dispose self.contents = Bitmap.new(width - 32, height - 32) end #-------------------------------------------------------------------------- # ● ウィンドウサイズを作成 #-------------------------------------------------------------------------- def size_get max = 1 size = 0 if $game_message.num_input_digits_max > 0 max = 16 * $game_message.num_input_digits_max size = 1 else bitmap = Bitmap.new(Graphics.width, 416) t = @text.clone t.gsub!("\x00", "\n") t.each_line{|line| max = [bitmap.text_size(line).width, max].max size += 1 } bitmap.dispose end size_set(self.x, self.y, max + 32, WLH * size + 32) end #-------------------------------------------------------------------------- # ● ポップアップするキャラを取得 #-------------------------------------------------------------------------- def pop_get if $game_message.choice_max > 0 or $game_message.num_input_digits_max > 0 @popup_character = $game_player @text.gsub!("  ", "") @lst_pos = [@popup_character.screen_x, @popup_character.screen_y] return end @popup_character = nil @text.sub!(/\\P\[([0-9]+)\]/i) { "" } @popup_character = get_character($1.to_i) if $1.nil? if @text.include?("\P[-1]") or @text.include?("\p[-1]") @text.sub!(/\\P\[\-1+\]/i) { "" } @popup_character = $game_player end @popup_character = get_character(0) if @popup_character.nil? end @lst_pos = [@popup_character.screen_x, @popup_character.screen_y] end #-------------------------------------------------------------------------- # ● ポップアップ座標の計算 #-------------------------------------------------------------------------- def pos_set_f c = @popup_character case c.direction when 2 ; p = 0 when 4..6 ; p = c.is_a?(Game_Player) ? 1 : 0 when 8 ; p = 1 end pos_set(p) unless @sub_windows.empty? # サブウインドウが存在 re1 = 0 re2 = 0 for i in @sub_windows next if i.openness < 255 pos_set(p) if i.rect.overlap?(self.rect) # サブウインドウと重なる場合 re1 += i.rect.overlap_size(self.rect) pos = p == 1 ? pop_under(c) : pop_on(c) size_set(pos[0], pos[1], self.width, self.height) # 逆向きに再セット if i.rect.overlap?(self.rect) # さらに重なる場合 re2 += i.rect.overlap_size(self.rect) end end end pos_set(p) if re1 < re2 # 戻す end if $game_message.screen_in if self.y < 0 - 16 pos_set(1) elsif self.y + self.height > Graphics.height + 16 pos_set(0) end end end #-------------------------------------------------------------------------- # ● ポップアップ座標を仮決定 #-------------------------------------------------------------------------- def pos_set(p) c = @popup_character pos = p == 0 ? pop_on(c) : pop_under(c) size_set(pos[0], pos[1], self.width, self.height) end #-------------------------------------------------------------------------- # ● 上へポップアップする場合の座標取得 #-------------------------------------------------------------------------- def pop_on(c) x = c.screen_x - (self.width / 2) y = c.screen_y - (self.height) - pop_height - 32#64 if $game_message.screen_in x = [0, x].max x = [Graphics.width - self.width, x].min end return [x, y] end #-------------------------------------------------------------------------- # ● 下へポップアップする場合の座標取得 #-------------------------------------------------------------------------- def pop_under(c) x = c.screen_x - (self.width / 2) y = c.screen_y + 32# pop_height if $game_message.screen_in x = [0, x].max x = [Graphics.width - self.width, x].min end return [x, y] end #-------------------------------------------------------------------------- # ● キャラクター画像の高さを取得 #-------------------------------------------------------------------------- def pop_height c = @popup_character return 32 if c.tile_id > 0 b = Cache.character(c.character_name) sign = c.character_name[/^[\!\$]./] a = (sign != nil and sign.include?('$')) ? 4 : 8 h = b.height / a h -= 4 unless sign != nil and sign.include?('!') return h end #-------------------------------------------------------------------------- # ● ホールドフラグ獲得 #-------------------------------------------------------------------------- def hold_get if @text.include?("\HL") @text.sub!(/\\HL/i, "") @hold = true elsif @text.include?("\hl") @text.sub!(/\\hl/i, "") @hold = true end end #-------------------------------------------------------------------------- # ● ホールド #-------------------------------------------------------------------------- def hold if @hold z = @sub_windows.size id = @popup_character.id id = -1 if id.nil? w = Window_Copy.new(self, @sprite, z, id) w.pause = false @sub_windows.push w end end #-------------------------------------------------------------------------- # ● ホールド終了 #-------------------------------------------------------------------------- def hold_out unless @hold for i in @sub_windows i.close i.update end end end #-------------------------------------------------------------------------- # ● 数値入力の開始 #-------------------------------------------------------------------------- alias start_number_input_dai_pop start_number_input def start_number_input start_number_input_dai_pop @number_input_window.x -= 24 if $scene.is_a?(Scene_Map) && pop? end #-------------------------------------------------------------------------- # ● 改ページ処理 #-------------------------------------------------------------------------- alias new_page_dai_pop new_page def new_page if $scene.is_a?(Scene_Map) && pop? @sprite.visible = false hold_out @hold = false hold_get # ホールドフラグ取得 pop_get # ポップアップするキャラを確定 size_get # 文字列からウインドウサイズを確定 pos_set_f # ポップアップ座標を確定 sprite_set else size_reset @v = false end new_page_dai_pop end #-------------------------------------------------------------------------- # ● メッセージの更新終了 #-------------------------------------------------------------------------- alias finish_message_dai_pop finish_message def finish_message hold finish_message_dai_pop end #-------------------------------------------------------------------------- # ● ウィンドウを閉じる #-------------------------------------------------------------------------- def close return if @hold super end #-------------------------------------------------------------------------- # ● キャラクターの取得 #-------------------------------------------------------------------------- def get_character(param) return $game_map.interpreter.get_character(param) end #-------------------------------------------------------------------------- # ● フレーム更新 #-------------------------------------------------------------------------- alias update_dai_pop update def update sub_windows_update update_sprite update_dai_pop update_pos end #-------------------------------------------------------------------------- # ● ウインドウ座標の更新 #-------------------------------------------------------------------------- def update_pos return if self.openness == 0 or pop? == false or @popup_character.nil? c = @popup_character if @lst_pos != [c.screen_x, c.screen_y] self.x -= @lst_pos[0] - c.screen_x self.y -= @lst_pos[1] - c.screen_y @sprite.x -= @lst_pos[0] - c.screen_x @sprite.y -= @lst_pos[1] - c.screen_y @lst_pos = [c.screen_x, c.screen_y] end end #-------------------------------------------------------------------------- # ● 解放 #-------------------------------------------------------------------------- alias dispose_dai_pop dispose def dispose dispose_dai_pop sub_windows_dispose dispose_sprite end #-------------------------------------------------------------------------- # ● サブウインドウの更新 #-------------------------------------------------------------------------- def sub_windows_update if $game_message.pop_clear $game_message.pop_clear = false c = true end return if @sub_windows.empty? for i in @sub_windows next if i.nil? or i.disposed? if c i.dispose next elsif self.rect == i.rect i.opacity == 0 i.back_opacity = 0 i.contents_opacity = 0 else i.opacity == 255 i.back_opacity = 200 i.contents_opacity = 255 end i.update unless i.disposed? if i.openness == 0 or (self.openness == 0 && @hold == false) i.dispose @sub_windows.delete(i) end end @sub_windows = [] if c end #-------------------------------------------------------------------------- # ● サブウインドウの開放 #-------------------------------------------------------------------------- def sub_windows_dispose return if @sub_windows.empty? for i in @sub_windows next if i.nil? or i.disposed? i.dispose end end end #============================================================================== # ■ Window_Copy #============================================================================== class Window_Copy < Window_Base #-------------------------------------------------------------------------- # ● オブジェクト初期化 #-------------------------------------------------------------------------- def initialize(w, s, z, id) super(w.x, w.y, w.width, w.height) self.contents = w.contents.dup self.opacity = w.opacity self.back_opacity = w.back_opacity self.contents_opacity = w.contents_opacity self.z = z @id = id c = $game_map.interpreter.get_character(@id) @lst_pos = [c.screen_x, c.screen_y] @sprite = Sprite_Copy.new(s, @id) @sprite.z = self.z end #-------------------------------------------------------------------------- # ● 解放 #-------------------------------------------------------------------------- def dispose super @sprite.dispose end #-------------------------------------------------------------------------- # ● フレーム更新 #-------------------------------------------------------------------------- def update super @sprite.update c = $game_map.interpreter.get_character(@id) if @lst_pos != [c.screen_x, c.screen_y] self.x -= @lst_pos[0] - c.screen_x self.y -= @lst_pos[1] - c.screen_y @lst_pos = [c.screen_x, c.screen_y] end end end #============================================================================== # ■ Sprite_Copy #============================================================================== class Sprite_Copy < Sprite #-------------------------------------------------------------------------- # ● オブジェクト初期化 #-------------------------------------------------------------------------- def initialize(s, id) super() self.bitmap = s.bitmap.dup self.opacity = s.opacity self.x = s.x self.y = s.y self.z = s.z - 2 self.ox = s.ox self.oy = s.oy self.visible = s.visible self.src_rect.set(s.src_rect.x, s.src_rect.y, s.src_rect.width, s.src_rect.height) @id = id c = $game_map.interpreter.get_character(@id) @lst_pos = [c.screen_x, c.screen_y] end #-------------------------------------------------------------------------- # ● フレーム更新 #-------------------------------------------------------------------------- def update super c = $game_map.interpreter.get_character(@id) if @lst_pos != [c.screen_x, c.screen_y] self.x -= @lst_pos[0] - c.screen_x self.y -= @lst_pos[1] - c.screen_y @lst_pos = [c.screen_x, c.screen_y] end end end #============================================================================== # ■ Window #============================================================================== class Window #-------------------------------------------------------------------------- # ● 矩形の取得 #-------------------------------------------------------------------------- def rect return Rect.new(self.x, self.y, self.width, self.height) end end #============================================================================== # ■ Rect #============================================================================== class Rect #-------------------------------------------------------------------------- # ● 重なっているか? #-------------------------------------------------------------------------- def overlap?(rect) return false if self.x >= rect.x + rect.width return false if self.x + self.width <= rect.x return false if self.y >= rect.y + rect.height return false if self.y + self.height <= rect.y return true end #-------------------------------------------------------------------------- # ● 重なっている面積 #-------------------------------------------------------------------------- def overlap_size(rect) return 0 unless overlap?(rect) if self.x <= rect.x rect1, rect2 = self, rect else rect1, rect2 = rect, self end if rect1.x == rect2.x a = [rect1.width, rect2.width].min elsif rect1.x + rect1.width > rect2.x + rect2.width a = rect2.width else a = rect1.width - rect2.x + rect1.x end if self.y <= rect.y rect3, rect4 = self, rect else rect3, rect4 = rect, self end if rect3.y == rect4.y b = [rect3.height, rect4.height].min elsif rect3.y + rect3.height > rect4.y + rect4.height b = rect4.height else b = rect3.height - rect4.y + rect3.y end return a * b end end