查看: 991|回复: 1

[原创发布] 仿《 DELTARUNE》战斗物品动态字幕评价脚本

[复制链接]
梦石
1
星屑
1184
在线时间
59 小时
注册时间
2023-9-8
回帖
19
发表于 2026-4-22 00:27:27 | 显示全部楼层 |阅读模式

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

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

×
[pre lang="ruby"]#==============================================================================
# ■ 仿《德尔塔符文》战斗物品动态字幕评价
#==============================================================================
# 作者:瑞吉
# 标签说明:
#   单人:<C:角色ID, I:台词>
#   双人:<C:使用者ID P:队友ID CI:使用者台词 PI:队友台词>
#   多人:<M:使用者ID | US:使用者台词 | 队友ID1:台词1 | 队友ID2:台词2 | ...>
# 适用范围:物品、技能
#==============================================================================

module DataManager
  class << self
    alias load_database_subtitle_final load_database
  end

  def self.load_database
    load_database_subtitle_final
    load_notetags_subtitle_final
  end

  def self.load_notetags_subtitle_final
    [$data_skills, $data_items, $data_weapons, $data_armors].each do |group|
      group.each do |obj|
        next if obj.nil?
        obj.load_notetag_subtitle_final if obj.respond_to?(:load_notetag_subtitle_final)
      end
    end
  end
end

#==============================================================================
# ■ RPG::BaseItem
#==============================================================================

class RPG::BaseItem
  attr_accessor :subtitle_map        # 单人
  attr_accessor :subtitle_pair_map   # 双人
  attr_accessor :multi_subtitle_map  # 多人 { user_id => { user_speech: 台词, targets: { target_id => 台词 } } }

  def load_notetag_subtitle_final
    @subtitle_map = {}
    @subtitle_pair_map = {}
    @multi_subtitle_map = {}

    # 单人 <C:数字, I:台词>
    self.note.scan(/<C:\s*(\d+)\s*[,,]\s*I:\s*(.+?)\s*>/i) do |match|
      @subtitle_map[match[0].to_i] = match[1].to_s
    end

    # 双人 <C:使用者ID P:队友ID CI:台词 PI:台词>
    self.note.scan(/<C:\s*(\d+)\s*P:\s*(\d+)\s*CI:\s*(.+?)\s*PI:\s*(.+?)\s*>/i) do |match|
      @subtitle_pair_map[[match[0].to_i, match[1].to_i]] = {
        ci: match[2].to_s,
        pi: match[3].to_s
      }
    end

    # 多人 <M:使用者ID | US:使用者台词 | 队友ID:台词 | ...>
    self.note.scan(/<M:\s*(\d+)\s*\|\s*(.+?)\s*>/i) do |match|
      user_id = match[0].to_i
      content = match[1].to_s
      target_map = {}
      user_speech = nil

      content.split('|').each do |part|
        part = part.strip
        if part =~ /^US\s*:\s*(.+)/i
          user_speech = $1.to_s.strip
        elsif part =~ /^(\d+)\s*:\s*(.+)/i
          target_map[$1.to_i] = $2.to_s.strip
        end
      end

      @multi_subtitle_map[user_id] = {
        user_speech: user_speech,
        targets: target_map
      }
    end
  end

  def subtitle_for(actor_id)
    return @subtitle_map[actor_id] if @subtitle_map.key?(actor_id)
    return @subtitle_map[0] if @subtitle_map.key?(0)
    nil
  end

  def pair_subtitle_for(user_id, target_id)
    @subtitle_pair_map[[user_id, target_id]]
  end

  def multi_subtitle_for(user_id)
    @multi_subtitle_map[user_id]
  end
end

#==============================================================================
# ■ Scene_Battle
#==============================================================================

class Scene_Battle < Scene_Base
  alias use_item_subtitle_final use_item

  def use_item
    @last_used_item = @subject.current_action.item
    @last_user = @subject
    @last_targets = @subject.current_action.make_targets.compact

    use_item_subtitle_final

    show_subtitle_final if should_show_subtitle_final?
  end

  def should_show_subtitle_final?
    return false unless @last_used_item.is_a?(RPG::UsableItem)
    return false unless @last_used_item.respond_to?(:subtitle_for)

    item = @last_used_item
    user = @last_user

    multi = item.multi_subtitle_for(user.id)
    if multi
      targets = multi[:targets]
      return true if @last_targets.any? { |t| t.is_a?(Game_Actor) && targets.key?(t.id) }
      return true if multi[:user_speech] && !multi[:user_speech].empty?
    end

    @last_targets.each do |t|
      return true if t.is_a?(Game_Actor) && item.pair_subtitle_for(user.id, t.id)
    end

    return true if item.subtitle_for(user.id)

    false
  end

  def show_subtitle_final
    item = @last_used_item
    user = @last_user
    return unless @log_window

    # 1. 多人评价
    multi_data = item.multi_subtitle_for(user.id)
    if multi_data
      targets_map = multi_data[:targets]
      user_speech = multi_data[:user_speech]

      # 使用者台词
      if user_speech && !user_speech.empty?
        user_name = $game_actors[user.id].name
        @log_window.add_text("#{user_name}:#{user_speech}")
        wait(30)
      end

      # 队友台词
      shown = false
      @last_targets.each do |target|
        next unless target.is_a?(Game_Actor)
        text = targets_map[target.id]
        next unless text
        name = $game_actors[target.id].name
        @log_window.add_text("#{name}:#{text}")
        wait(20)
        shown = true
      end

      if shown || user_speech
        wait(30)
        return
      end
    end

    # 2. 双人评价
    @last_targets.each do |target|
      next unless target.is_a?(Game_Actor)
      pair = item.pair_subtitle_for(user.id, target.id)
      if pair
        user_name = $game_actors[user.id].name
        target_name = $game_actors[target.id].name
        @log_window.add_text("#{user_name}:#{pair[:ci]}\n#{target_name}:#{pair[:pi]}")
        wait(60)
        return
      end
    end

    # 3. 单人评价
    single = item.subtitle_for(user.id)
    if single
      user_name = $game_actors[user.id].name
      @log_window.add_text("#{user_name}:#{single}")
      wait(60)
    end
  end
end[/pre]

更多图片 小图 大图
组图打开中,请稍候......

评分

参与人数 2赞 +2 收起 理由
jienluck + 1 精品文章
MireniamuYuki + 1 塞糖

查看全部评分

梦石
0
星屑
387
在线时间
25 小时
注册时间
2023-7-27
回帖
18
发表于 2026-7-29 12:10:18 | 显示全部楼层
虽然本人脚本方面并非专业,但是还是找AI进行了一下抓错之类的,因为直接使用存在致命问题...
  1. #==============================================================================
  2. # ■ 仿《三角符文》战斗物品动态字幕评价
  3. #==============================================================================
  4. # 作者:瑞吉
  5. #
  6. # 修复内容:
  7. #   1. 提取 BattlerIDHelper 模块,统一封装角色/敌人的身份识别与名字获取
  8. #   2. 【关键】增加使用者类型检查,彻底阻止敌人触发字幕(修复原脚本敌人回合崩溃)
  9. #   3. 【关键】修复敌人 enemy_id 与角色 actor_id 命名空间冲突导致的错配问题
  10. #   4. 增加名字获取的异常兜底(rescue ""),防止非法 ID 导致崩溃
  11. #
  12. # 标签说明:
  13. #   单人:<C:角色ID, I:台词>
  14. #   双人:<C:使用者ID P:队友ID CI:使用者台词 PI:队友台词>
  15. #   多人:<M:使用者ID | US:使用者台词 | 队友ID1:台词1 | 队友ID2:台词2 | ...>
  16. # 适用范围:物品、技能
  17. #==============================================================================

  18. #==============================================================================
  19. # ■ BattlerIDHelper
  20. #------------------------------------------------------------------------------
  21. # 统一处理战斗者的身份识别与名字获取,避免在 Scene_Battle 中硬编码
  22. #==============================================================================
  23. module BattlerIDHelper
  24.   # 获取战斗者的 ID
  25.   #   Game_Actor  -> 返回 actor.id(角色 ID)
  26.   #   Game_Enemy  -> 返回 enemy.enemy_id(敌人 ID)
  27.   #   其他/空     -> 返回 nil
  28.   def get_battler_id(battler)
  29.     return nil if battler.nil?
  30.     if battler.is_a?(Game_Actor)
  31.       return battler.id
  32.     elsif battler.is_a?(Game_Enemy)
  33.       return battler.enemy_id
  34.     else
  35.       return nil
  36.     end
  37.   end

  38.   # 获取战斗者的显示名称
  39.   #   Game_Actor  -> 返回 $game_actors 中的名字
  40.   #   Game_Enemy  -> 返回 $data_enemies 中的名字
  41.   #   失败/空     -> 返回空字符串
  42.   def get_battler_name(battler)
  43.     return "" if battler.nil?
  44.     if battler.is_a?(Game_Actor)
  45.       return $game_actors[battler.id].name rescue ""
  46.     elsif battler.is_a?(Game_Enemy)
  47.       return $data_enemies[battler.enemy_id].name rescue ""
  48.     else
  49.       return ""
  50.     end
  51.   end
  52. end

  53. #==============================================================================
  54. # ■ DataManager
  55. #==============================================================================
  56. module DataManager
  57.   class << self
  58.     alias load_database_subtitle_final load_database
  59.   end

  60.   def self.load_database
  61.     load_database_subtitle_final
  62.     load_notetags_subtitle_final
  63.   end

  64.   def self.load_notetags_subtitle_final
  65.     [$data_skills, $data_items, $data_weapons, $data_armors].each do |group|
  66.       group.each do |obj|
  67.         next if obj.nil?
  68.         obj.load_notetag_subtitle_final if obj.respond_to?(:load_notetag_subtitle_final)
  69.       end
  70.     end
  71.   end
  72. end

  73. #==============================================================================
  74. # ■ RPG::BaseItem
  75. #==============================================================================
  76. class RPG::BaseItem
  77.   attr_accessor :subtitle_map        # 单人
  78.   attr_accessor :subtitle_pair_map   # 双人
  79.   attr_accessor :multi_subtitle_map  # 多人 { user_id => { user_speech: 台词, targets: { target_id => 台词 } } }

  80.   def load_notetag_subtitle_final
  81.     @subtitle_map = {}
  82.     @subtitle_pair_map = {}
  83.     @multi_subtitle_map = {}

  84.     # 单人 <C:数字, I:台词>
  85.     self.note.scan(/<C:\s*(\d+)\s*[,,]\s*I:\s*(.+?)\s*>/i) do |match|
  86.       @subtitle_map[match[0].to_i] = match[1].to_s
  87.     end

  88.     # 双人 <C:使用者ID P:队友ID CI:台词 PI:台词>
  89.     self.note.scan(/<C:\s*(\d+)\s*P:\s*(\d+)\s*CI:\s*(.+?)\s*PI:\s*(.+?)\s*>/i) do |match|
  90.       @subtitle_pair_map[[match[0].to_i, match[1].to_i]] = {
  91.         ci: match[2].to_s,
  92.         pi: match[3].to_s
  93.       }
  94.     end

  95.     # 多人 <M:使用者ID | US:使用者台词 | 队友ID:台词 | ...>
  96.     self.note.scan(/<M:\s*(\d+)\s*\|\s*(.+?)\s*>/i) do |match|
  97.       user_id = match[0].to_i
  98.       content = match[1].to_s
  99.       target_map = {}
  100.       user_speech = nil

  101.       content.split('|').each do |part|
  102.         part = part.strip
  103.         if part =~ /^US\s*:\s*(.+)/i
  104.           user_speech = $1.to_s.strip
  105.         elsif part =~ /^(\d+)\s*:\s*(.+)/i
  106.           target_map[$1.to_i] = $2.to_s.strip
  107.         end
  108.       end

  109.       @multi_subtitle_map[user_id] = {
  110.         user_speech: user_speech,
  111.         targets: target_map
  112.       }
  113.     end
  114.   end

  115.   def subtitle_for(actor_id)
  116.     return @subtitle_map[actor_id] if @subtitle_map.key?(actor_id)
  117.     return @subtitle_map[0] if @subtitle_map.key?(0)
  118.     nil
  119.   end

  120.   def pair_subtitle_for(user_id, target_id)
  121.     @subtitle_pair_map[[user_id, target_id]]
  122.   end

  123.   def multi_subtitle_for(user_id)
  124.     @multi_subtitle_map[user_id]
  125.   end
  126. end

  127. #==============================================================================
  128. # ■ Scene_Battle
  129. #==============================================================================
  130. class Scene_Battle < Scene_Base
  131.   include BattlerIDHelper

  132.   alias use_item_subtitle_final use_item

  133.   def use_item
  134.     @last_used_item = @subject.current_action.item
  135.     @last_user = @subject
  136.     @last_targets = @subject.current_action.make_targets.compact

  137.     use_item_subtitle_final

  138.     show_subtitle_final if should_show_subtitle_final?
  139.   end

  140.   def should_show_subtitle_final?
  141.     return false unless @last_used_item.is_a?(RPG::UsableItem)
  142.     return false unless @last_used_item.respond_to?(:subtitle_for)
  143.    
  144.     # 【修复 1】原脚本缺少此检查,敌人 AI 使用技能时会因调用 user.id 而抛出 NoMethodError
  145.     # 原脚本的设计意图本就是"我方角色互动",所有目标过滤都是 is_a?(Game_Actor)
  146.     return false unless @last_user.is_a?(Game_Actor)

  147.     item = @last_used_item
  148.     user = @last_user
  149.     user_id = get_battler_id(user)
  150.    
  151.     # 【修复 2】防御性兜底:若 get_battler_id 返回 nil,直接跳过
  152.     return false if user_id.nil?

  153.     multi = item.multi_subtitle_for(user_id)
  154.     if multi
  155.       targets = multi[:targets]
  156.       return true if @last_targets.any? { |t| t.is_a?(Game_Actor) && targets.key?(get_battler_id(t)) }
  157.       return true if multi[:user_speech] && !multi[:user_speech].empty?
  158.     end

  159.     @last_targets.each do |t|
  160.       return true if t.is_a?(Game_Actor) && item.pair_subtitle_for(user_id, get_battler_id(t))
  161.     end

  162.     return true if item.subtitle_for(user_id)

  163.     false
  164.   end

  165.   def show_subtitle_final
  166.     item = @last_used_item
  167.     user = @last_user
  168.     return unless @log_window
  169.    
  170.     # 【修复 3】与 should_show_subtitle_final? 保持一致,确保只有我方角色触发字幕
  171.     return unless user.is_a?(Game_Actor)
  172.    
  173.     user_id = get_battler_id(user)
  174.     return if user_id.nil?

  175.     # 1. 多人评价
  176.     multi_data = item.multi_subtitle_for(user_id)
  177.     if multi_data
  178.       targets_map = multi_data[:targets]
  179.       user_speech = multi_data[:user_speech]

  180.       # 使用者台词
  181.       if user_speech && !user_speech.empty?
  182.         user_name = get_battler_name(user)
  183.         @log_window.add_text("#{user_name}:#{user_speech}")
  184.         wait(30)
  185.       end

  186.       # 队友台词
  187.       shown = false
  188.       @last_targets.each do |target|
  189.         next unless target.is_a?(Game_Actor)
  190.         text = targets_map[get_battler_id(target)]
  191.         next unless text
  192.         name = get_battler_name(target)
  193.         @log_window.add_text("#{name}:#{text}")
  194.         wait(20)
  195.         shown = true
  196.       end

  197.       if shown || user_speech
  198.         wait(30)
  199.         return
  200.       end
  201.     end

  202.     # 2. 双人评价
  203.     @last_targets.each do |target|
  204.       next unless target.is_a?(Game_Actor)
  205.       pair = item.pair_subtitle_for(user_id, get_battler_id(target))
  206.       if pair
  207.         user_name = get_battler_name(user)
  208.         target_name = get_battler_name(target)
  209.         @log_window.add_text("#{user_name}:#{pair[:ci]}\n#{target_name}:#{pair[:pi]}")
  210.         wait(60)
  211.         return
  212.       end
  213.     end

  214.     # 3. 单人评价
  215.     single = item.subtitle_for(user_id)
  216.     if single
  217.       user_name = get_battler_name(user)
  218.       @log_window.add_text("#{user_name}:#{single}")
  219.       wait(60)
  220.     end
  221.   end
  222. end
复制代码

大概就是这样...敌人的处理会和玩家的混在一起,就是敌人也可以被原脚本处理导致致命错误
(用Sion的debug拓展测试战斗的时候直接冻结游戏了,虽然说我使用的时候暂时没做物品吐槽适配
希望没问题...
何为正义?无何永劫。
回复

使用道具 举报

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

本版积分规则

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

在本版发帖返回顶部