スクリプトたのしい！RGSS3たのしい！（白目）

こういう風に設定して、アイテム選択の時にShiftを押すと
![160602.jpg](https://qiita-image-store.s3.amazonaws.com/0/128059/14bf3d3e-6558-2cc6-b889-231038b654f7.jpeg)


こうなる
![160601.jpg](https://qiita-image-store.s3.amazonaws.com/0/128059/dd606a15-8fc6-b076-85ba-3f935b098b6b.jpeg)

とりあえず、[未完のダンボール](http://www14.atpages.jp/mikadan/)さんの詳しいアイテム説明ウィンドウ表示スクリプトを参考にしつつ、VXAce向けアイテム表示ウィンドウのかんせーい

やったぜ。

作っているうちに色々ハマったけど、~~それはまた今度書く。多分。~~　書きました。
まずは未解決の課題から。

# 未だに出来ていないこと

 0. **アイテム説明ウィンドウを開くボタンと閉めるボタンを同じボタンに割り振れない**

正確に言うと、同じボタンを割り振る事自体は出来る。ただし、**開いた瞬間に閉じる**。
set_handlerを置く場所が悪いのかな～とか思って色々試してみた（ウィンドウを開く時に置いてみるとか）したけどやっぱり開いた瞬間に閉じる。
ウィンドウをactivateさせる前に置こうが後に置こうが開いた瞬間に閉じる。
ウェイトを置こうが開いた瞬間に落ちる。ただ、この時ウェイトを大きくしてみたらウィンドウだけ描画されていて中身が描画されていない（＝@big_item_window.showは発火しているけどset_big_itemは発火していない？）ことがわかったのでそれがヒントなのかな。set_big_itemのほうを先に置いてるんだけど……。
というわけでここは保留。


 0. **説明文にアイコンを表示させる制御文字（\I[]）が使えない**

これは元のスクリプトからの仕様。実装してみようと思ったけどまだBitmapクラスの全貌がわからないので……。~~でも\C[]とか\V[]が使えるなら別段困らないよね？~~

#ハマったところとか
 １． **RGSS2とRGSS3の違い**

とりあえず未完のダンボールさんの詳しいアイテム説明ウィンドウ表示をそのままスクリプトエディタにぺたりしてみる。うん動かない（当たり前）。
見てみたら継承しているクラスから違う。そりゃ動かないわ。
どうやらRGSS3ではクラスの細分化が進んでいるっぽい。RGSS2ではアイテム画面のウィンドウはWindow_Baseを継承してるけど、RGSS3ではWindow_Baseを継承したWindow_Selectableを継承している。汎用選択ウィンドウのクラスが新しく出来ているということだ。なるほどなあ。

もっと大きな違いはボタンの入力にあった。RGSS2では毎フレームボタンの入力処理があったかどうかチェックしているけど、RGSS3だとハンドラを設定して入力処理の判定はそれに任せている。そのほうがわかりやすいので良いと思う。
おかげでウィンドウのクラス定義は流用効かなくて自分で書くはめになったけど、コード量は少なくなったのでよかった。自作ウィンドウ作る度に毎フレームの更新処理をいちいち書くのはあまりスマートじゃないし。

 ２． **どこまでがスクリプトで定義されているのかわからない**
 最大の敵だったかもしれない。というか今もよくわかっていない。
例えば、Window_Baseクラスはスクリプトで定義されてるのに、その継承元のWindowクラスは組み込みになっている。何故だ。
Window_Baseのメソッドを確認するのにはスクリプトエディタを見なきゃいけなくて、Windowのメソッドを見たい時はヘルプを見て……なのでめんどくさい。透明度を変えるメソッドがどこで定義されているのかわからなくて暫く探しまわってしまった。見つけたと思ったら**ウィンドウ自体**の不透明度のメソッドと**ウィンドウの背景**の不透明度のメソッドがあってわけわからなくなったけどそれはまた別の話。

３．**装備画面からの呼び出し**
ハマったこととか書きつつ上のふたつが完全に愚痴でした。反省します。というわけで本当にハマったところ。
アイテム選択画面からアイテム図鑑を呼び出すのは簡単にできた。じゃあ装備画面からの呼び出しもちょちょいのちょいで出来るだろと思ったら大間違い。
ぶち当たった問題点は、**スロットを選んでいるところからの呼び出しとスロットを選択した後に装備を選んでいるところからの呼び出しはどう見分ける？**というところ。これは、スロットのウィンドウのハンドラに設定したメソッドと装備選択ウィンドウのハンドラに設定したメソッドを違うものにして、引数を変えつつアイテム図鑑ウィンドウを表示する関数を呼び出すことによって出来た。関数内でその引数をインスタンス変数に入れてやれば、アイテム図鑑ウィンドウを隠す関数内からでも参照できるから、activateするウィンドウの決定にそれを使ってやる。できたー。

４．**画面サイズの取得**
画面の解像度を変更していても使えるように、ウィンドウサイズを取得しようと思ったんだけど……？
結局見つからなかったので、一番最初に画面サイズの定義を置いておくことにした。自分で画面サイズ変えてる人はそこを変えればいいと思う。

#工夫したところとか
元のスクリプトから改善したところとか。
１．**元の説明文を改行表示する**
どうもBitmap.draw_textは改行文字も無視してテキスト描画をしてしまうらしい。なので、説明文を\r\nでsplitして、出来た配列を１つずつ見ていき中身が\r\nではないものをdraw_textした。Windows系のことしか考えてないけど、VXAceってWindows以外で動かないし別にいいよねっ！
この処理だと、説明文が一行でもニ行でもレイアウトが崩れることが無いのが良い。
元々のスクリプトのメモの描画しているところではどうなってるのか見てみたら、\nをヌル文字で置換して、ヌル文字があったら次の行の描画を開始するってしてるっぽい。

そうそう、メモの描画はほとんどそのまま使えたのでそのまま使っている。
画像を表示させるところだけ上手く行かなくて、どうしてかなと探っていたら切り出した配列を文字列にするところで躓いていた。scanの仕様が変わったのかな……？

２．**説明文とメモ書きの間に水平線**
水平線入れたほうがオシャレかなって思って入れてみた。ステータス画面の描画から引っ張ってきただけ。

以下コード。なんかヤバいバグがあっても元のスクリプト作者では無くこちらまでお願いします。

```ruby:アイテム説明ウィンドウ(2016/06/01ver.)
#==============================================================================
# ■ アイテム説明ウィンドウ(2016/06/01ver.)
#------------------------------------------------------------------------------
# 　アイテム画面や装備画面でShiftを押すと詳しいアイテム説明ウィンドウを表示する
# 
#   ～使い方～
#   アイテム、武器、防具のメモ欄に表示させたい内容を書く
# 　アイテムの名前、売値、説明文、メモ欄の順番で表示される
# 
# 　メモ欄の一行目に
#   Cache.picture("画像名") x=画像表示のx座標 y=画像表示のy座標
# 　と入力すると画像が表示される
# 
#==============================================================================

#////////////////////////////////////////////////////////////////
#価格と非売品の名前
#////////////////////////////////////////////////////////////////

PRICE = "売値"
NOT_FOR_SELL = "非売品"

#////////////////////////////////////////////////////////////////
#textの表示場所
#x,y   : textを表示開始する時の座標
#////////////////////////////////////////////////////////////////
BIG_ITEM_WINDOW_X=0
BIG_ITEM_WINDOW_Y=0

#////////////////////////////////////////////////////////////////
#画面サイズ
#
#////////////////////////////////////////////////////////////////
SCR_X = 544
SCR_Y = 416


#アイテム説明用Windowの定義
class Window_Big_Item < Window_Selectable
  def initialize
    super(0, 0, SCR_X, SCR_Y)
  end

  
  #--------------------------------------------------------------------------
  # ☆ 設定  <新規追加>
  #     text  : ウィンドウに表示する文字列
  #     x,y   : textを表示開始する時の座標
  #     align : アラインメント (0..左揃え、1..中央揃え、2..右揃え)
  #--------------------------------------------------------------------------
  def set_big_item(item)
    @item = item
    x = BIG_ITEM_WINDOW_X
    y = BIG_ITEM_WINDOW_Y
    align = 0
    margin = 0
    if item != nil
      self.contents.clear
      
      #アイテムの名前描画
      #アイコン描画
      if @item.icon_index > 0
        bitmap = Cache.system("Iconset")
        rect = Rect.new(@item.icon_index % 16 * 24, @item.icon_index / 16 * 24, 24, 24)
        self.contents.blt(x, y, bitmap, rect, 255)
        margin = 24
      end
      self.contents.font.color = normal_color
      self.contents.draw_text(x + margin, y, SCR_X-x, line_height, @item.name, align)
      
      #アイテムの価格描画
      self.contents.font.color = system_color
      if @item.price !=0
        self.contents.draw_text(x, y+line_height, SCR_X-x, line_height, PRICE, align)
        self.contents.font.color = normal_color
        draw_currency_value(@item.price, Vocab::currency_unit, x+line_height*3, y+line_height, 120)
      else
        self.contents.draw_text(x, y+line_height, SCR_X-x, line_height, NOT_FOR_SELL, align)
      end
      
      #アイテム説明
      self.contents.font.color = normal_color
      if @item.note != nil
        des_array = slice_des(@item.description)
        margin = 2
        for i in 0..(des_array.length - 1) do
          if des_array[i] != "\r\n"
            self.contents.draw_text(x, y+line_height*margin,SCR_X-x,line_height,des_array[i],align)
            margin += 1
          end
        end
        
        #self.contents.draw_text(x, y+line_height,SCR_X-x,line_height,@item.description,align)
        draw_horz_line(y+line_height*margin)
        margin += 1
        draw_note(x, y+line_height*margin,@item.note.clone)
      end
    end
  end
  
  #--------------------------------------------------------------------------
  # ● 水平線の描画
  #--------------------------------------------------------------------------
  def draw_horz_line(y)
    line_y = y + line_height / 2 - 1
    contents.fill_rect(0, line_y, contents_width, 2, line_color)
  end
  #--------------------------------------------------------------------------
  # ● 水平線の色を取得
  #--------------------------------------------------------------------------
  def line_color
    color = normal_color
    color.alpha = 48
    color
  end
  
  #--------------------------------------------------------------------------
  # ☆ 説明をスライスした配列を返す
  #--------------------------------------------------------------------------
  def slice_des(text)
    return text.split(/\r\n/) #ツクールだしWin系だけ考えとけば平気っしょ
  end
  
  #--------------------------------------------------------------------------
  # ☆ itemのメモの描画 <新規追加>
  # 元の関数は，Window_Message>update_message
  #--------------------------------------------------------------------------
  def draw_note(x, y, text)
    if text != nil
      text2=[]
      text2=text.dup #元のテキストコピー
      
      if text.include?("Cache.picture") 
        text3=[]
        w=[]
        w="0"
        h=[]
        h="0"
        text3=text.scan(/Cache.picture\(\"(\S*)\"\)/i)
        if text.include?("x=")
          w=text.scan(/x=(\d*)/i)
          text.gsub!(/x=(\d*)/i,"")
        end
        if text.include?("y=")
          h=text.scan(/y=(\d*)/i)
          text.gsub!(/y=(\d*)/i,"")
        end
        draw_item_gra(Cache.picture(text3[0][0]),w.to_s.to_i,h.to_s.to_i)
        text.gsub!(/Cache.picture\(\"(\w*)\"\)/i,"")
        #text.gsub!(/Cache.picture\(\"([A-Za-z0-9]*)\"\)/i,"")
      end
      text.gsub!(/\n/, "\x00")
      text.gsub!(/\s/, "")
      text.gsub!(/\\V\[([0-9]+)\]/i) { $game_variables[$1.to_i] }
      text.gsub!(/\\N\[([0-9]+)\]/i) { $game_actors[$1.to_i].name }
      text.gsub!(/\\C\[([0-9]+)\]/i) { "\x01[#{$1}]" }
      text.gsub!(/\\G/)              { $game_party.gold }
      text.gsub!(/\\\\/)             { "\\" }
      
      #初期化
      contents_x = x
      contents_y = y
      line_count = 0
      max_line   = 10
      
      loop do
        c = text.slice!(/./m)            # 次の文字を取得
        case c
        when nil                          # 描画すべき文字がない
          break
        when "\x00"                       # 改行
          contents_x  = x
          contents_y += line_height
          line_count += 1
        when "\x01"                       # \C[n]  (文字色変更)
          text.sub!(/\[([0-9]+)\]/, "")
          self.contents.font.color = text_color($1.to_i)
          next
        else                              # 普通の文字
          self.contents.draw_text(contents_x, contents_y, 40, line_height, c)
          c_width = contents.text_size(c).width
          contents_x += c_width
        end
        break if line_count >= max_line
      end
      @item.note=text2.dup
    end
  end
  #--------------------------------------------------------------------------
  # ☆ アイテムのグラフィックの描画 <新規追加>
  #     item_name  : グラフィック ファイル名
  #     x          : 描画先 X 座標
  #     y          : 描画先 Y 座標
  #     size       : 表示サイズ
  #--------------------------------------------------------------------------
  def draw_item_gra(item_name,x, y)
    bitmap = item_name
    rect = Rect.new(0, 0, 0, 0)
    rect.x = 0
    rect.y = 0
    rect.width = 544
    rect.height= 416
    self.contents.blt(x, y, bitmap, rect)
    #bitmap.dispose
  end
  
  #--------------------------------------------------------------------------
  # Window_Selectableから継承したハンドラの定義を再定義
  # ○ 決定やキャンセルなどのハンドリング処理
  # okやcancelの時も再定義して、アイテムが選べない状態でも選べるように、音が鳴らないようにしている
  #--------------------------------------------------------------------------
  def process_handling
    return unless open? && active
    return process_ok       if handle?(:ok)       && Input.trigger?(:C)
    return process_cancel   if handle?(:cancel)   && Input.trigger?(:B)
    return process_pagedown if handle?(:pagedown) && Input.trigger?(:R)
    return process_pageup   if handle?(:pageup)   && Input.trigger?(:L)
    return process_shift    if handle?(:shift)    && Input.trigger?(:SHIFT)
  end
  
  def process_shift
    call_handler(:shift)
  end
  
  def process_ok
    call_handler(:ok)
  end
  
  def process_cancel
    call_handler(:cancel)
  end
end

#==============================================================================
# □ Window_ItemList
#------------------------------------------------------------------------------
# 　アイテム画面で、所持アイテムの一覧を表示するウィンドウ。
# 　ハンドラを追加。（Shiftを追加）
#==============================================================================

class Window_ItemList < Window_Selectable
  #--------------------------------------------------------------------------
  # Window_Selectableから継承したハンドラの定義を再定義
  # ○ 決定やキャンセルなどのハンドリング処理（Shiftを追加）
  #--------------------------------------------------------------------------
  def process_handling
    return unless open? && active
    return process_ok       if ok_enabled?        && Input.trigger?(:C)
    return process_cancel   if cancel_enabled?    && Input.trigger?(:B)
    return process_pagedown if handle?(:pagedown) && Input.trigger?(:R)
    return process_pageup   if handle?(:pageup)   && Input.trigger?(:L)
    return process_shift    if handle?(:shift)    && Input.trigger?(:SHIFT) #追加
  end
  
  def process_shift
    call_handler(:shift)
  end
end

#==============================================================================
# □ Scene_Item
#------------------------------------------------------------------------------
# 　アイテム画面の処理を行うクラスを一部再定義
#==============================================================================

class Scene_Item < Scene_ItemBase
  #--------------------------------------------------------------------------
  # ○ 開始処理（追加）
  #--------------------------------------------------------------------------
  def start
    super
    create_help_window
    create_category_window
    create_item_window
    create_big_item_window #追加
  end
  #----------------------------------------------------------------------
  # ○ アイテムウィンドウの作成
  #--------------------------------------------------------------------------
  def create_item_window
    wy = @category_window.y + @category_window.height
    wh = Graphics.height - wy
    @item_window = Window_ItemList.new(0, wy, Graphics.width, wh)
    @item_window.viewport = @viewport
    @item_window.help_window = @help_window
    @item_window.set_handler(:ok, method(:on_item_ok))
    @item_window.set_handler(:cancel, method(:on_item_cancel))
    @item_window.set_handler(:shift, method(:show_big_item)) #追加
    @category_window.item_window = @item_window
  end
  
  #--------------------------------------------------------------------------
  # ～以下追加～
  # ☆大きなアイテムウィンドウ作成
  #--------------------------------------------------------------------------
  def create_big_item_window
    @big_item_window = Window_Big_Item.new
    @big_item_window.viewport = @viewport
    @big_item_window.set_handler(:ok,     method(:hide_big_item_window))
    @big_item_window.set_handler(:cancel, method(:hide_big_item_window))
    #@big_item_window.set_handler(:shift, method(:hide_big_item_window))
    @big_item_window.hide
    @big_item_window.deactivate
  end
  
  #--------------------------------------------------------------------------
  # ☆大きなアイテムウィンドウ表示
  #--------------------------------------------------------------------------
 
  def show_big_item
    if (item != nil)
      Sound.play_ok
      
      @big_item_window.set_big_item(item)
      @item_window.deactivate
    
      #アイテムウィンドウとかの消去
      @item_window.hide
      @category_window.hide
      @help_window.hide
    
      #表示する
      #@big_item_window.opacity = 255
      @big_item_window.back_opacity =205
      @big_item_window.show
      
      
      @big_item_window.activate
    end
  end
  
  #--------------------------------------------------------------------------
  # ☆大きなアイテムウィンドウ非表示
  #--------------------------------------------------------------------------
 
  def hide_big_item_window
    Sound.play_cancel
    @big_item_window.hide
    
    #アイテムウィンドウとかの描画
    @item_window.show
    @category_window.show
    @help_window.show
    
    @item_window.activate
  end
end

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
#　＊＊＊ここから下、装備画面でのアイテム図鑑呼び出し＊＊＊
#　　要らない場合は消す
#
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#==============================================================================
# □ Window_EquipSlot
#------------------------------------------------------------------------------
# 　装備画面で、アクターが現在装備しているアイテムを表示するウィンドウです。
#==============================================================================

class Window_EquipSlot < Window_Selectable
  #--------------------------------------------------------------------------
  # Window_Selectableから継承したハンドラの定義を再定義
  # ○ 決定やキャンセルなどのハンドリング処理（Shiftを追加）
  #--------------------------------------------------------------------------
  def process_handling
    return unless open? && active
    return process_ok       if ok_enabled?        && Input.trigger?(:C)
    return process_cancel   if cancel_enabled?    && Input.trigger?(:B)
    return process_pagedown if handle?(:pagedown) && Input.trigger?(:R)
    return process_pageup   if handle?(:pageup)   && Input.trigger?(:L)
    return process_shift    if handle?(:shift)    && Input.trigger?(:SHIFT) #追加
  end
  
  def process_shift
    call_handler(:shift)
  end
end



#==============================================================================
# □ Scene_Equip
#------------------------------------------------------------------------------
# 　装備画面の処理を行うクラスです。
#==============================================================================

class Scene_Equip < Scene_MenuBase
  #--------------------------------------------------------------------------
  # ○ 開始処理
  #--------------------------------------------------------------------------
  def start
    super
    create_help_window
    create_status_window
    create_command_window
    create_slot_window
    create_item_window
    create_big_item_window #追加
  end
  
  #--------------------------------------------------------------------------
  # ○ スロットウィンドウの作成
  #--------------------------------------------------------------------------
  def create_slot_window
    wx = @status_window.width
    wy = @command_window.y + @command_window.height
    ww = Graphics.width - @status_window.width
    @slot_window = Window_EquipSlot.new(wx, wy, ww)
    @slot_window.viewport = @viewport
    @slot_window.help_window = @help_window
    @slot_window.status_window = @status_window
    @slot_window.actor = @actor
    @slot_window.set_handler(:ok,       method(:on_slot_ok))
    @slot_window.set_handler(:cancel,   method(:on_slot_cancel))
    @slot_window.set_handler(:shift ,   method(:show_big_item_slot)) #追加
  end
  #--------------------------------------------------------------------------
  # ○ アイテムウィンドウの作成
  #--------------------------------------------------------------------------
  def create_item_window
    wx = 0
    wy = @slot_window.y + @slot_window.height
    ww = Graphics.width
    wh = Graphics.height - wy
    @item_window = Window_EquipItem.new(wx, wy, ww, wh)
    @item_window.viewport = @viewport
    @item_window.help_window = @help_window
    @item_window.status_window = @status_window
    @item_window.actor = @actor
    @item_window.set_handler(:ok,     method(:on_item_ok))
    @item_window.set_handler(:cancel, method(:on_item_cancel))
    @item_window.set_handler(:shift , method(:show_big_item_item)) #追加
    @slot_window.item_window = @item_window
  end
  
  #--------------------------------------------------------------------------
  # ～以下追加～
  # ☆大きなアイテムウィンドウ作成
  #--------------------------------------------------------------------------
  def create_big_item_window
    @big_item_window = Window_Big_Item.new
    @big_item_window.viewport = @viewport
    @big_item_window.set_handler(:ok,     method(:hide_big_item_window))
    @big_item_window.set_handler(:cancel, method(:hide_big_item_window))
    @big_item_window.hide
    @big_item_window.deactivate
  end
  
  #--------------------------------------------------------------------------
  # ☆呼び出した場所によってshow_big_itemを違う引数で呼び出す
  #--------------------------------------------------------------------------
 
  def show_big_item_slot
    show_big_item(0)
  end
  
  def show_big_item_item
    show_big_item(1)
  end
  
  
  #--------------------------------------------------------------------------
  # ☆大きなアイテムウィンドウ表示
  #--------------------------------------------------------------------------
 
  def show_big_item(id)
    #itemに武器を入れたり装備を入れたり
    case id
      when 0 then
        #スロットからの呼び出し
        index = @slot_window.index
        item = @actor.equips[index]
      when 1 then
        #アイテム選択からの呼び出し
        item = @item_window.item
    end
      
    if (item != nil)
      #どこから呼び出したか
      #0...スロット
      #1...アイテムウィンドウ
      @id = id
      
      Sound.play_ok
      @big_item_window.set_big_item(item)
      #読んだ場所によってdeactivateさせるウィンドウを変える
      case id
        when 0 then
          @slot_window.deactivate
        when 1 then
          @item_window.deactivate
      end
      
    
      #アイテムウィンドウとかの消去
      @item_window.hide
      @slot_window.hide
      @status_window.hide
      @help_window.hide
      @command_window.hide
    
      #表示する
      #@big_item_window.opacity = 255
      @big_item_window.back_opacity =205
      @big_item_window.show
      @big_item_window.activate
    end
  end
  
  #--------------------------------------------------------------------------
  # ☆大きなアイテムウィンドウ非表示
  #--------------------------------------------------------------------------
 
  def hide_big_item_window
    Sound.play_cancel
    @big_item_window.hide
    
    #アイテムウィンドウとかの描画
    @item_window.show
    @slot_window.show
    @status_window.show
    @help_window.show
    @command_window.show
    
    case @id
      when 0 then
        @slot_window.activate
      when 1 then
        @item_window.activate
    end
  end
end

#////////////////////////////////////////////////////////////////
#作成者: shichirinne
#URL: http://qiita.com/shichirinne
#   
#　当スクリプトは、未完のダンボール（http://www14.atpages.jp/mikadan/）様の
#　　　「詳しいアイテム説明ウィンドウ表示」スクリプトの改変です。
#
#　使用報告は任意。
#////////////////////////////////////////////////////////////////
```