查看: 23|回复: 0

[原创发布] 简单的"检视战场"脚本

[复制链接]
梦石
0
星屑
201
在线时间
68 小时
注册时间
2019-1-14
回帖
11
发表于 昨天 22:01 | 显示全部楼层 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有账号?注册会员

×
本帖最后由 JasperCrazy 于 2026-10-8 22:50 编辑

未标题-1.jpg
最近用DS给自己的游戏加了一堆奇妙插件,难得有这么个简单易用的,分享一下。
操作方法:在ActorCommand阶段(显示[攻击][防御][技能][物品]的阶段),按下A键,弹出此场景。
按←→切换左右部分。
左半边记录了所有Message,一页显示不下,可以按↑↓滚动。
右半边列出了所有角色和敌人,可以按↑↓切换,底下会有敌人的简介(备注<说明:XX>),及角色/敌人附有的状态名(备注<状态名:XX>,无备注则不显示状态,按优先等级排列)。
再按A或Esc退出场景。
自测可用,希望万机之神保佑你……
  1. #==============================================================================
  2. # ■ 检视战场(独立场景版)
  3. #------------------------------------------------------------------------------
  4. # 【操作】
  5. #   ActorCommand 阶段按 A / Shift 打开;A / Shift / Esc / X 关闭。
  6. #   左半为战斗日志:↑↓ 滚动(可按住快速滚动),→ 切到右侧
  7. #   右半为人物查看:↑↓ 切换成员(首尾循环),← 切回左侧
  8. #   打开时默认滚到最新一条。
  9. #
  10. # 【滚动提示】
  11. #   左侧有未显示内容时,底部显示黑框 + "按 ↑ 上翻" / "按 ↓ 下翻"
  12. #
  13. # 【记录来源】
  14. #   Window_BattleLog#add_text / replace_text、$game_message.add
  15. #
  16. # 【转义符处理(记录时固定)】
  17. #   \V[n] → 变量当前值;\name[XX] → "XX:";其他转义符去掉
  18. #
  19. # 【数据库备注】
  20. #   敌人:<说明:XX>(\n 变换行,自动换行)
  21. #   状态:<状态名:XX>(无此标签则不显示)
  22. #
  23. # 【加载位置】
  24. #   Materials 下方、Main 上方,放在所有战斗脚本之后。
  25. #==============================================================================

  26. $battle_log_history = nil
  27. $char_width_cache   = {}

  28. #--------------------------------------------------------------------------
  29. # ● 滚动参数
  30. #--------------------------------------------------------------------------
  31. HOLD_START_FRAMES = 15    # 按住多少帧后开始连续滚动
  32. HOLD_INTERVAL     = 3     # 连续滚动间隔(帧)

  33. #--------------------------------------------------------------------------
  34. # ● 转义符处理
  35. #--------------------------------------------------------------------------
  36. module BattleLogSanitizer
  37.   def self.sanitize(text)
  38.     return text if text.nil?
  39.     s = text.dup
  40.     s.gsub!(/\\[Vv]\[(\d+)\]/) { $game_variables[$1.to_i].to_s }
  41.     s.gsub!(/\\[Nn]ame\[([^\]]*)\]/) { "#{$1}:" }
  42.     s.gsub!(/\\[A-Za-z_]+\[[^\]]*\]/, "")
  43.     s.gsub!(/\\[A-Za-z_]+/, "")
  44.     s.gsub!(/\\[.><|!^$]/, "")
  45.     s
  46.   end
  47. end

  48. #--------------------------------------------------------------------------
  49. # ● Window_BattleLog — 记录
  50. #--------------------------------------------------------------------------
  51. class Window_BattleLog < Window_Selectable
  52.   alias history_log_add_text add_text
  53.   def add_text(text)
  54.     if $battle_log_history
  55.       s = BattleLogSanitizer.sanitize(text)
  56.       $battle_log_history << s unless s.nil? || s.empty?
  57.     end
  58.     history_log_add_text(text)
  59.   end

  60.   alias history_log_replace_text replace_text
  61.   def replace_text(text)
  62.     if $battle_log_history
  63.       s = BattleLogSanitizer.sanitize(text)
  64.       $battle_log_history << s unless s.nil? || s.empty?
  65.     end
  66.     history_log_replace_text(text)
  67.   end
  68. end

  69. #--------------------------------------------------------------------------
  70. # ● Game_Message — 记录事件文字
  71. #--------------------------------------------------------------------------
  72. class Game_Message
  73.   alias history_log_message_add add
  74.   def add(text)
  75.     if $battle_log_history && SceneManager.scene.is_a?(Scene_Battle)
  76.       s = BattleLogSanitizer.sanitize(text)
  77.       $battle_log_history << s unless s.nil? || s.empty?
  78.     end
  79.     history_log_message_add(text)
  80.   end
  81. end

  82. #--------------------------------------------------------------------------
  83. # ● Window_BattleHistoryLog — 全屏双面板窗口
  84. #--------------------------------------------------------------------------
  85. class Window_BattleHistoryLog < Window_Base
  86.   FONT_SIZE       = 20
  87.   STATE_FONT_SIZE = 18
  88.   HINT_FONT_SIZE  = 18
  89.   LINE_HEIGHT     = 24
  90.   MAX_MEMBERS     = 9
  91.   STATE_ICON_W    = 24

  92.   def initialize
  93.     super(0, 0, Graphics.width, Graphics.height)
  94.     contents.font.size = FONT_SIZE
  95.     @active          = false
  96.     @close_requested = false
  97.     @side            = :left
  98.     @left_oy         = 0
  99.     @member_index    = 0
  100.     @data            = []
  101.     @battler_list    = []
  102.     @history_lines   = []
  103.     @prev_press      = {}
  104.     @hold            = {}
  105.     self.z = 1000
  106.     self.openness = 0
  107.   end

  108.   def close_requested?; @close_requested; end

  109.   #--------------------------------------------------------------------------
  110.   # ● 激活:快照当前所有按键状态
  111.   #--------------------------------------------------------------------------
  112.   def activate
  113.     @active = true
  114.     @prev_press = {}
  115.     @hold = {}
  116.     [:UP, :DOWN, :LEFT, :RIGHT, :A, :B, :X].each do |k|
  117.       @prev_press[k] = Input.press?(k)
  118.       @hold[k] = nil
  119.     end
  120.   end

  121.   def deactivate; @active = false; end
  122.   def active?;    @active;         end

  123.   def left_width;   (contents.width * 0.5).to_i - 8; end
  124.   def right_x;      left_width + 16;                 end
  125.   def right_width;  contents.width - right_x - 4;    end
  126.   def right_top_height
  127.     [MAX_MEMBERS * LINE_HEIGHT + 4, contents.height - 80].min
  128.   end
  129.   def right_bottom_y; right_top_height + 6; end

  130.   #--------------------------------------------------------------------------
  131.   # ● 字符宽度缓存
  132.   #--------------------------------------------------------------------------
  133.   def char_width(ch)
  134.     key = [contents.font.size, ch]
  135.     $char_width_cache[key] ||= contents.text_size(ch).width
  136.   end

  137.   #--------------------------------------------------------------------------
  138.   # ● 设置数据
  139.   #--------------------------------------------------------------------------
  140.   def set_data(history, battlers)
  141.     @data            = (history || []).dup
  142.     @battler_list    = (battlers || []).first(MAX_MEMBERS)
  143.     @member_index    = 0
  144.     @side            = :left
  145.     @close_requested = false
  146.     rebuild
  147.     @left_oy = left_max_oy
  148.     refresh
  149.   end

  150.   def rebuild
  151.     contents.font.size = FONT_SIZE
  152.     @history_lines = []
  153.     @data.each { |text| @history_lines.concat(wrap_text(text, left_width)) }
  154.   end

  155.   #--------------------------------------------------------------------------
  156.   # ● 自动换行(逐字增量测量)
  157.   #--------------------------------------------------------------------------
  158.   def wrap_text(text, max_width)
  159.     return [""] if text.empty?
  160.     return [""] if text.nil?
  161.     lines = []
  162.     current = ""
  163.     current_w = 0
  164.     text.each_char do |c|
  165.       if c == "\n"
  166.         lines << current
  167.         current = ""
  168.         current_w = 0
  169.         next
  170.       end
  171.       w = char_width(c)
  172.       if current_w + w > max_width
  173.         lines << current
  174.         current = c
  175.         current_w = w
  176.       else
  177.         current << c
  178.         current_w += w
  179.       end
  180.     end
  181.     lines << current unless current.empty?
  182.     lines
  183.   end

  184.   def left_max_oy
  185.     [@history_lines.size * LINE_HEIGHT - contents.height, 0].max
  186.   end

  187.   #--------------------------------------------------------------------------
  188.   # ● 绘制
  189.   #--------------------------------------------------------------------------
  190.   def refresh
  191.     contents.clear
  192.     draw_left_panel
  193.     draw_left_hints
  194.     draw_separator
  195.     draw_right_panel
  196.     draw_focus_frame
  197.   end

  198.   def draw_left_panel
  199.     w = left_width
  200.     y = -@left_oy
  201.     @history_lines.each do |line|
  202.       if y + LINE_HEIGHT > 0 && y < contents.height
  203.         contents.draw_text(0, y, w, LINE_HEIGHT, line)
  204.       end
  205.       y += LINE_HEIGHT
  206.       break if y > contents.height
  207.     end
  208.   end

  209.   #--------------------------------------------------------------------------
  210.   # ● 左侧滚动提示:↑ 在顶部,↓ 在底部
  211.   #--------------------------------------------------------------------------
  212.   def draw_left_hints
  213.     has_prev = @left_oy > 0
  214.     has_next = @left_oy < left_max_oy

  215.     lh = LINE_HEIGHT
  216.     old_color = contents.font.color.clone
  217.     old_size  = contents.font.size
  218.     contents.font.color = Color.new(255, 220, 100)
  219.     contents.font.size  = HINT_FONT_SIZE

  220.     # 顶部:"↑ 上翻"
  221.     if has_prev
  222.       top_rect = Rect.new(0, 0, left_width, lh)
  223.       contents.fill_rect(top_rect, Color.new(0, 0, 0, 200))
  224.       contents.draw_text(top_rect, "↑ 上翻", 1)
  225.     end

  226.     # 底部:"↓ 下翻"
  227.     if has_next
  228.       bottom_rect = Rect.new(0, contents.height - lh, left_width, lh)
  229.       contents.fill_rect(bottom_rect, Color.new(0, 0, 0, 200))
  230.       contents.draw_text(bottom_rect, "↓ 下翻", 1)
  231.     end

  232.     contents.font.color = old_color
  233.     contents.font.size  = old_size
  234.   end

  235.   def draw_separator
  236.     x = left_width + 4
  237.     contents.fill_rect(x, 0, 2, contents.height, Color.new(255, 255, 255, 60))
  238.   end

  239.   def draw_right_panel
  240.     x = right_x
  241.     w = right_width

  242.     @battler_list.each_with_index do |b, i|
  243.       y = i * LINE_HEIGHT
  244.       if i == @member_index && @side == :right
  245.         contents.fill_rect(x - 4, y - 1, w, LINE_HEIGHT + 2,
  246.                            Color.new(255, 200, 50, 80))
  247.         draw_border(x - 4, y - 1, w, LINE_HEIGHT + 2)
  248.       end
  249.       contents.draw_text(x, y, w, LINE_HEIGHT, b.name)
  250.     end

  251.     contents.fill_rect(x, right_top_height, w, 2, Color.new(255, 255, 255, 80))
  252.     draw_member_detail(x, right_bottom_y, w, contents.height - right_bottom_y)
  253.   end

  254.   def draw_member_detail(x, y, w, h)
  255.     return if @battler_list.empty?
  256.     b = @battler_list[@member_index]
  257.     return unless b

  258.     contents.draw_text(x, y, w, LINE_HEIGHT, b.name)
  259.     y += LINE_HEIGHT

  260.     desc = get_description(b)
  261.     if desc && !desc.empty?
  262.       wrap_text(desc, w).each do |line|
  263.         break if y + LINE_HEIGHT > contents.height
  264.         contents.draw_text(x, y, w, LINE_HEIGHT, line)
  265.         y += LINE_HEIGHT
  266.       end
  267.     end

  268.     y += 6
  269.     draw_states_two_columns(b, x, y, w, contents.height - y)
  270.   end

  271.   #--------------------------------------------------------------------------
  272.   # ● 两列显示状态
  273.   #--------------------------------------------------------------------------
  274.   def draw_states_two_columns(battler, x, y, w, h)
  275.     return if h <= 0
  276.     old_size = contents.font.size
  277.     contents.font.size = STATE_FONT_SIZE
  278.     begin
  279.       states = battler.states
  280.                       .sort_by { |s| [-s.priority, s.id] }
  281.                       .select  { |s| state_display_name(s) }
  282.       return if states.empty?

  283.       col_w    = w / 2
  284.       text_max = col_w - STATE_ICON_W - 8

  285.       first_col = []
  286.       cur_y = 0
  287.       remaining = []
  288.       states.each do |s|
  289.         if cur_y + LINE_HEIGHT > h
  290.           remaining << s
  291.           next
  292.         end
  293.         name = state_display_name(s)
  294.         too_long = contents.text_size(name).width > text_max
  295.         first_col << [s, too_long, cur_y]
  296.         cur_y += LINE_HEIGHT
  297.       end

  298.       first_col.each do |s, too_long, offset_y|
  299.         name = state_display_name(s)
  300.         row_y = y + offset_y
  301.         draw_icon(s.icon_index, x, row_y)
  302.         if too_long
  303.           contents.draw_text(x + STATE_ICON_W, row_y,
  304.                              w - STATE_ICON_W, LINE_HEIGHT, name)
  305.         else
  306.           contents.draw_text(x + STATE_ICON_W, row_y,
  307.                              col_w - STATE_ICON_W, LINE_HEIGHT, name)
  308.         end
  309.       end

  310.       second_x = x + col_w
  311.       cur_y = 0
  312.       remaining.each do |s|
  313.         while cur_y + LINE_HEIGHT <= h
  314.           row_index = cur_y / LINE_HEIGHT
  315.           if row_index < first_col.size && first_col[row_index][1]
  316.             cur_y += LINE_HEIGHT
  317.           else
  318.             break
  319.           end
  320.         end
  321.         break if cur_y + LINE_HEIGHT > h
  322.         name = state_display_name(s)
  323.         row_y = y + cur_y
  324.         draw_icon(s.icon_index, second_x, row_y)
  325.         contents.draw_text(second_x + STATE_ICON_W, row_y,
  326.                            col_w - STATE_ICON_W, LINE_HEIGHT, name)
  327.         cur_y += LINE_HEIGHT
  328.       end
  329.     ensure
  330.       contents.font.size = old_size
  331.     end
  332.   end

  333.   def get_description(battler)
  334.     return nil unless battler.is_a?(Game_Enemy)
  335.     note = battler.enemy.note
  336.     return nil if note.nil? || note.empty?
  337.     if note =~ /<说明[::]\s*([^>]+)>/m
  338.       return $1.gsub("\\n", "\n").strip
  339.     end
  340.     nil
  341.   end

  342.   def state_display_name(state)
  343.     return nil if state.note.nil?
  344.     if state.note =~ /<状态名[::]\s*([^>]+)>/
  345.       return $1.strip
  346.     end
  347.     nil
  348.   end

  349.   def draw_focus_frame
  350.     draw_border(0, 0, left_width, contents.height) if @side == :left
  351.   end

  352.   def draw_border(x, y, w, h)
  353.     c = Color.new(255, 200, 50)
  354.     contents.fill_rect(x,     y,     w, 2, c)
  355.     contents.fill_rect(x,     y+h-2, w, 2, c)
  356.     contents.fill_rect(x,     y,     2, h, c)
  357.     contents.fill_rect(x+w-2, y,     2, h, c)
  358.   end

  359.   #--------------------------------------------------------------------------
  360.   # ● 输入状态更新(每帧一次)
  361.   #--------------------------------------------------------------------------
  362.   def update_input_state
  363.     [:UP, :DOWN, :LEFT, :RIGHT, :A, :B, :X].each do |k|
  364.       now = Input.press?(k)
  365.       was = @prev_press[k] || false
  366.       just = now && !was
  367.       @prev_press[k] = now

  368.       if just
  369.         @hold[k] = 0
  370.       elsif now
  371.         @hold[k] = (@hold[k] || 0) + 1
  372.       else
  373.         @hold[k] = nil
  374.       end
  375.     end
  376.   end

  377.   # 首次按下(严格上升沿)
  378.   def just_pressed?(key)
  379.     @hold[key] == 0
  380.   end

  381.   # 首次按下 OR 按住后连续触发
  382.   def triggered?(key)
  383.     c = @hold[key]
  384.     return false if c.nil?
  385.     return true if c == 0
  386.     c >= HOLD_START_FRAMES && (c - HOLD_START_FRAMES) % HOLD_INTERVAL == 0
  387.   end

  388.   #--------------------------------------------------------------------------
  389.   # ● 更新
  390.   #--------------------------------------------------------------------------
  391.   def update
  392.     super
  393.     update_input_state
  394.     return unless @active && openness > 0

  395.     # 关闭
  396.     if just_pressed?(:A) || just_pressed?(:B) || just_pressed?(:X)
  397.       @close_requested = true
  398.       deactivate
  399.       close
  400.       return
  401.     end

  402.     if @side == :left
  403.       if just_pressed?(:RIGHT)
  404.         @side = :right
  405.         refresh
  406.       elsif triggered?(:UP)
  407.         @left_oy = [@left_oy - LINE_HEIGHT, 0].max
  408.         refresh
  409.       elsif triggered?(:DOWN)
  410.         @left_oy = [@left_oy + LINE_HEIGHT, left_max_oy].min
  411.         refresh
  412.       end
  413.     else
  414.       if just_pressed?(:LEFT)
  415.         @side = :left
  416.         refresh
  417.       elsif triggered?(:UP)
  418.         move_cursor(-1)
  419.       elsif triggered?(:DOWN)
  420.         move_cursor(1)
  421.       end
  422.     end
  423.   end

  424.   def move_cursor(delta)
  425.     return if @battler_list.empty?
  426.     @member_index = (@member_index + delta) % @battler_list.size
  427.     refresh
  428.   end
  429. end

  430. #--------------------------------------------------------------------------
  431. # ● Scene_BattleHistory — 独立场景
  432. #--------------------------------------------------------------------------
  433. class Scene_BattleHistory < Scene_Base
  434.   def start
  435.     super
  436.     create_background
  437.     create_window
  438.   end

  439.   def create_background
  440.     @bg_sprite = Sprite.new
  441.     @bg_sprite.bitmap = Bitmap.new(Graphics.width, Graphics.height)
  442.     @bg_sprite.bitmap.fill_rect(0, 0, Graphics.width, Graphics.height,
  443.                                  Color.new(0, 0, 0, 200))
  444.     @bg_sprite.z = 500
  445.   end

  446.   def create_window
  447.     battlers = $game_party.members + $game_troop.members
  448.     @window = Window_BattleHistoryLog.new
  449.     @window.set_data($battle_log_history, battlers)
  450.     @window.openness = 255
  451.     @window.activate
  452.   end

  453.   def perform_transition
  454.     Graphics.transition(4)
  455.   end

  456.   def update
  457.     super
  458.     @window.update
  459.     if @window.close_requested? && @window.openness == 0
  460.       return_scene
  461.     end
  462.   end

  463.   def terminate
  464.     @window.dispose
  465.     @bg_sprite.dispose
  466.     super
  467.   end
  468. end

  469. #--------------------------------------------------------------------------
  470. # ● Scene_Battle — 集成
  471. #--------------------------------------------------------------------------
  472. class Scene_Battle < Scene_Base
  473.   alias history_log_battle_start battle_start
  474.   def battle_start
  475.     $battle_log_history = []
  476.     history_log_battle_start
  477.   end

  478.   alias history_log_turn_end turn_end
  479.   def turn_end
  480.     history_log_turn_end
  481.     if $battle_log_history && !$battle_log_history.empty? && $battle_log_history.last != ""
  482.       $battle_log_history << ""
  483.     end
  484.   end

  485.   alias history_log_update update
  486.   def update
  487.     history_log_update
  488.     update_battle_history_log
  489.   end

  490.   def update_battle_history_log
  491.     @history_cooldown ||= 0
  492.     @history_cooldown -= 1 if @history_cooldown > 0
  493.     return if @history_cooldown > 0

  494.     return unless @actor_command_window
  495.     return unless @actor_command_window.active
  496.     return unless Input.trigger?(:A) || Input.trigger?(:X)
  497.     open_battle_history_log
  498.   end

  499.   def open_battle_history_log
  500.     saved_index = @actor_command_window ? @actor_command_window.index : 0
  501.     saved_oy    = @actor_command_window ? @actor_command_window.oy : 0

  502.     @actor_command_window.deactivate if @actor_command_window

  503.     SceneManager.call(Scene_BattleHistory)
  504.     SceneManager.scene.main
  505.     if SceneManager.respond_to?(:force_recall)
  506.       SceneManager.force_recall(self)
  507.     else
  508.       SceneManager.instance_variable_set(:@scene, self)
  509.     end

  510.     if @actor_command_window && !@actor_command_window.disposed?
  511.       @actor_command_window.refresh
  512.       @actor_command_window.activate
  513.       @actor_command_window.select(saved_index)
  514.       @actor_command_window.oy = saved_oy
  515.     end
  516.     refresh_status

  517.     # 关键:解冻画面(子场景退出时执行了 Graphics.freeze)
  518.     perform_transition

  519.     @history_cooldown = 3
  520.   end
  521. end
复制代码




您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部