查看: 11531|回复: 29

[原创发布] 数据库通用备注接口插件 v2.1 增强版(附已发布范例链接)

[复制链接]

遠航の猫咪

梦石
3
星屑
26947
在线时间
2423 小时
注册时间
2005-10-15
回帖
987

开拓者

发表于 2017-10-14 21:29:31 | 显示全部楼层 |阅读模式

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

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

×
本帖最后由 SailCat 于 2017-12-17 23:23 编辑

在不魔改编辑器的情况下,最大限度的利用现有字段,模拟VA、MV数据库的备注功能。
具体使用说明,请见脚本前的注释项
注:该插件只是使你很方便的书写备注及调用备注,并未植入任何备注项具体功能在游戏系统中,需要你自己书写,写法可见范例
更新:
v2.1
去掉关于方法前缀(下划线)的配置,为兼容性及后续插件开发考虑,不再支持该字符自定义配置
追加配置合法性检查
允许给游戏事件设定备注,即分页的备注。用Game_Event类作为接口植入,便于制作各种效果。
v2.0
名字空间及依赖检查通用化,便于后期发布插件时更改冲突
增加狂野模式,相当于连数据库数值破限一起搞了……
v1.9
支持INI文件存储备注,如果你真的有这需求的话就用吧(虽然我觉得用公共事件已经很方便了)
v1.8
备注中的标签允许缺省,按默认值(名称)处理
超长备注现在可以设定不在一个公共事件内写完了,避免重名或事件过长带来的麻烦
v1.7
备注中的表达式现在可以用v简写变量,如v[1]指引用1号变量
备注中的字符串现在可以包括分段符(默认为;分号),需要前置反斜杠进行转义
增加了相关注释
v1.6
备注中的标识符现在可以使用大写字母和数字开头(因为加了方法前缀),不再限定为小写字母
更改了部分代码避免被论坛错误转换
v1.5
添加公共事件标签引用备注,备注内容不再受到字段长度的限制,理论上可以如VA、MV一样无限长
(多说两句:超长备注可能有许多解决办法,比如写在F11脚本里(即运行时设定),用外挂文件等等。这里的核心思路是,第一数据库的事要数据库内解决,第二拷贝粘贴数据时备注不能错乱)
v1.4
添加地图和事件备注数据的植入
v1.3
添加respond_to?定义和方法前缀设定,避免与普通字段重名,且制作依赖插件时,便于检查冲突(感谢@taroxd),默认的前缀是单下划线
所有分隔字符全部开放配置(感谢@hyrious的灵感),你现在想写成URL参数的样子都可以……
v1.2
添加define_method方法定义,添加analyze方法,同一对象只解析一次,增加再次调用的效率
允许运行时设定备注,包括个别项设定和整体重写,满足某些黑科技用法需要,现在备注如同普通字段一样
备注书写格式放松:前导符号前可以加空格(不影响原字段),分号后可以跟空格(不影响标识符的识别)
v1.1
使用元编程简化代码(感谢@taroxd的提示)
预设返回false的备注方式,无法识别将返回nil而不是false,未定义的默认值继续有效
增加了表达式的容错配置
增加了注释

范例的链接:
1. 技能名个别设定(原桜雅在土脚本)
2. 技能公式自定义
3. 技能消耗依赖

代码:
[pre lang="ruby"]#==============================================================================
# ■ 数据库通用备注接口 v2.1 by SailCat
#------------------------------------------------------------------------------
#   方法:本脚本插入到Main之前使用,且需在所有依赖此脚本的其他插件之前
#   依赖:无
#   版本:v2.1 (Build 171217)
#   效果:给所有数据库项目增加如同VA、MV一样的备注,且备注长度同VA、MV一样无限
#   配置:有大量配置项,见脚本配置区的具体说明,按默认设定可以正常运行
#   冲突:其他的数据库扩展脚本
#   说明:
#
#   1. 备注字段来源:
#      游戏事件页、公共事件数据使用注释(第1条起的连续内容)作为备注(无限)
#      队伍数据使用注释(第1页[不论条件]第1条起的连续内容)作为备注(无限)
#      物品、特技、武器、防具数据,用描述作为备注(最多100字)
#      角色、职业、敌人、状态、动画、图块数据,用名称作为备注(最多40字)
#      地图、事件数据,用名称作为备注(最多100字)
#      所有数据,皆可以引用公共事件中的标签或文件作为备注,因此理论上长度无限
#      用于批量存放备注数据的公共事件编号和文件名,可以在脚本配置中指定
#
#   2. 备注字段写法:
#      标准写法1:#备注项1;备注项2;...备注项n
#      标准写法2:#>公共事件中的超长备注标签名
#      标准写法3:#<配置文件中的超长备注标签名
#      以上标准写法互相排斥,根据需要任选一种
#      符号# (半角井号)用于区分备注与描述/名称正文,以及识别注释事件
#      符号; (半角分号)用于给备注项分段
#      符号#>用于整体引用公共事件中的超长备注标签
#      符号#<用于整体引用配置文件中的超长备注标签
#      上述分隔符可在下方脚本配置中,修改为其他字符
#      非标准写法:#任意备注内容,此种情况下,备注不会自动解析为备注项集的形式
#
#   3. 备注项的写法:
#      标准写法:标识符=表达式
#      由于直接书写空间有限,当表达式为某些特殊情况时,本脚本支持以下简写:
#      当表达式的值为true时,可写作 标识符 或者 标识符+
#      当表达式的值为false时,可写作 标识符-
#      当表达式的值为字符串时,可写作 标识符:字符串
#      当表达式的值为超长备注标签的引用字符串时,可写作 标识符.标签名
#      表达式中可用v 指代游戏变量,如v[8]相当于$game_variables[8]
#      表达式中包括备注分段符号(默认为;)时,用前置反斜杠转义
#
#   4. 标准写法备注的使用:
#      脚本中调用备注项,使用 数据对象._+标识符(默认值) 方法
#      符号_ 为标识符前缀,区分同原有字段的偶然重名,锁定不可修改
#      若调用的标识符无效或未定义,将返回默认值,默认值省略的话返回nil
#      强烈建议在植入备注功能时,用好记的写法和默认值,对标识符进行二次引用定义
#    调用示例:
#      角色1  名称设为:阿尔西斯 #a;b=4;c:5
#      则  $data_actors[1].name       # => "阿尔西斯" 自动区隔备注内容
#          $data_actors[1]._a         # => true
#          $data_actors[1]._b         # => 4
#          $data_actors[1]._c         # => "5"
#          $data_actors[1]._d         # => nil 未定义的标识符,采用nil
#          $data_actors[1]._d(2)      # => 2   未定义的标识符,采用默认值
#      技能1  描述设为:回复己方单体HP。#f:a.atk - b*def / 2;cd=1.5;no_ref-
#      则  $data_skills[1].description# => "回复己方单体HP。"
#          $data_skills[1]._f         # => "a.atk - b.def / 2"
#          $data_skills[1]._cd        # => 1.5
#          $data_skills[1]._no_ref    # => false
#    二次定义示例(采用默认值和好记写法,方便他处脚本的书写,并增强可读性):
#      module RPG
#        class Skill
#          def formula;  _f;       end
#          def cooldown; _cd(0.0); end
#        end
#      end
#
#   5. 非标准写法备注的使用:数据对象.note 方法可取得备注内容(字符串),以上
#
#   6. 狂野模式:
#      如果在脚本配置中将相关选项打开,可以使用狂野模式的备注
#      此时,若标识符写作该类数据的默认字段名称(只能是数值),原定数值会被覆盖
#      该方法可以轻易突破数据库的数值限制,如负数武器攻击力,超过200的能力F值
#      该方法可能破坏数据库的自洽性,因此请谨慎使用该功能
#
#   7. 本脚本只提供数据库内容的备注接口,没有植入关于备注项实装的任何扩展功能
#      备注项的实际应用代码,请自行实现或插入后续发布的依赖插件
#      脚本作者只承诺发布的所有基于本接口的依赖插件之间不会互相冲突
#      如果与非依赖本接口的插件或他人插件冲突,请自行解决
#
#   8. 关于用公共事件实现超长备注:
#      1)公共事件的超长备注,用事件指令书写在指定编号的公共事件中
#         公共事件的编号在脚本配置区指定,默认分开12项,也可合并为1项
#         超长备注可用“显示文章”或“注释”或“脚本”指令书写,具体指令无区别
#         仅提取这三条指令的文字内容,其他的指令会被自动忽略,不会有影响
#      2) 建议在公共事件最开始插入“中断事件处理”指令,避免误执行(不是必须)
#      3)每条超长备注之前,加上标签指令,标签名不得互相重复
#      4)超长备注可以作为备注项的字符串使用,例:
#           阿尔西斯 #pr.c1 # => 将标签c1后文字作为$data_actors[1]._pr的内容
#         也可以作为备注项数据,整体合使用,例:
#           阿尔西斯 #>c1   # => 将标签c1后文字作为阿尔西斯的备注,并自动解析
#         整体使用时,写在公共事件中的备注项需省略前置的#符号(因为已经写过了)
#      5) 不是用配置中的公共事件ID存放时,标签名需指定公共事件ID,如:#>99,c1
#      6) 标签名可以省略不写,用数据的名称和相关标识符作为默认标签名,如:
#           阿尔西斯 #>   # => 系统会查找名为“阿尔西斯”的标签并取值
#           阿尔西斯 #pr. # => 系统会查找名为“阿尔西斯_pr”的标签并取值
#      7) 引用的标签没有定义的情况下,返回空的字符串
#
#   9. 关于备注文件:
#      1) 备注文件的写法须为标准的INI配置文件写法,即标签分段,键值对书写,如:
#           [特萝西]
#           profile=这是特萝西,她是盗贼
#           age=28
#           gender=女
#           height=175
#           weight=60
#           is_dual_wield=false
#      2) 数据库引用的写法为:#<标签名,当标签名与数据名称一样时可以省略,如:
#           特萝西 #<
#         则有:$data_actors[4]._profile="这是特萝西,她是盗贼" 等等
#==============================================================================
#==============================================================================
# ■ SailCat's 插件公用
#==============================================================================
module SailCat
  $sailcat_import ||= {}
  #--------------------------------------------------------------------------
  # ● 脚本配置区
  #--------------------------------------------------------------------------
  module DataNoteCore_Config
    SEPARATOR = "#"               # 备注与正文的分隔符
    DELIMITER = ";"               # 分隔不同备注项的符号
    NOTE_LABEL_CHAR = ?>          # 超长备注(公共事件存储)的识别符
    NOTE_FILE_CHAR = ?<           # 超长备注(文件存储)的识别符
    NOTE_FILE = "Data/Note.ini"   # 超长备注(文件存储)的文件名
    ACTOR_COMMON_EVENT_ID = 1     # 超长备注(角色类)用公共事件ID
    CLASS_COMMON_EVENT_ID = 2     # 超长备注(职业类)用公共事件ID
    SKILL_COMMON_EVENT_ID = 3     # 超长备注(特技类)用公共事件ID
    ITEM_COMMON_EVENT_ID = 4      # 超长备注(物品类)用公共事件ID
    WEAPON_COMMON_EVENT_ID = 5    # 超长备注(武器类)用公共事件ID
    ARMOR_COMMON_EVENT_ID = 6     # 超长备注(防具类)用公共事件ID
    ENEMY_COMMON_EVENT_ID = 7     # 超长备注(敌人类)用公共事件ID
    STATE_COMMON_EVENT_ID = 8     # 超长备注(状态类)用公共事件ID
    ANIMATION_COMMON_EVENT_ID = 9 # 超长备注(动画类)用公共事件ID
    TILESET_COMMON_EVENT_ID = 10  # 超长备注(图块类)用公共事件ID
    MAPINFO_COMMON_EVENT_ID = 11  # 超长备注(地图类)用公共事件ID
    EVENT_COMMON_EVENT_ID = 12    # 超长备注(事件类)用公共事件ID
    NOTE_STRING_LF = "\n"         # 超长备注(字符串引用)的行分隔符号
    NOTE_DATA_LF = DELIMITER      # 超长备注(整体引用)的行分隔符号
    USE_WILD_MODE = true          # 是否开启狂野模式
    #--------------------------------------------------------------------------
    # ● 配置检查,请勿更改
    #--------------------------------------------------------------------------
    raise "备注分隔符与分项分隔符不得一致" if SEPARATOR == DELIMITER
    raise "公共事件标识与文件标识不得一致" if NOTE_LABEL_CHAR == NOTE_FILE_CHAR
  end
  #--------------------------------------------------------------------------
  # ● 植入
  #--------------------------------------------------------------------------
  $sailcat_import[:DataNoteCore] = 2.1
end

#==============================================================================
# ■ DataNoteCore
#------------------------------------------------------------------------------
#   数据库内容通用备注接口核心引擎
#==============================================================================
module DataNoteCore
  include SailCat::DataNoteCore_Config
  #--------------------------------------------------------------------------
  # ● 常量
  #--------------------------------------------------------------------------
  SEP_REGEX = /#{SEPARATOR}.+$/  # 切分用正则表达式,请不要修改
  #--------------------------------------------------------------------------
  # ● 获取备注字段
  #--------------------------------------------------------------------------
  def note
    # 如果备注值没有初始化
    if @note.nil?
      # 如果是公共事件,用执行列表前面的注释
      @note = split(get_command_note(list)) if self.is_a?(RPG::CommonEvent)
      # 如果是游戏事件,用执行列表前面的注释
      @note = split(get_command_note(list)) if self.is_a?(Game_Event)
      # 如果是队伍,用第1页执行列表前面的注释
      @note = split(get_command_note(pages[0].list)) if self.is_a?(RPG::Troop)
      # 如果是其他,用描述或者名称来备注
      @note ||= has_description? ? split(@description) : split(@name)
      # 解析备注字符串
      analyze
    end
    # 返回值
    return @note
  end
  #--------------------------------------------------------------------------
  # ● 设置备注字段(运行时改变)
  #      note: 新的备注
  #--------------------------------------------------------------------------
  def note=(note)
    # 设置新值
    @note = note
    # 重新解析备注
    analyze
  end
  #--------------------------------------------------------------------------
  # ● 取得/设置备注值(泛用)
  #--------------------------------------------------------------------------
  def method_missing(param_name, *args, &block)
    return super unless respond_to?(param_name)
    param_str = param_name.to_s.sub!(/^_/, "")
    # 备注赋值的情况下
    if param_str[-1] == 61
      param_key = param_str.chop
      self.class.send :define_method, param_name do |value|
        set_note(param_key, value)
      end
      set_note(param_key, *args)
    # 备注取值的情况下
    else
      self.class.send :define_method, param_name do |value|
        get_note(param_str, value)
      end
      return get_note(param_str, *args)
    end
  end
  #--------------------------------------------------------------------------
  # ● 是否有描述字段(内部使用)
  #--------------------------------------------------------------------------
  def has_description?
    instance_variables.include?("@description")
  end
  #--------------------------------------------------------------------------
  # ● 从事件列表中切取备注(内部使用)
  #--------------------------------------------------------------------------
  def get_command_note(list)
    for index in 0...list.length
      break if list[index].code != 108 and list[index].code != 408
    end
    join_command_note(list[0...index], DELIMITER)
  end
  #--------------------------------------------------------------------------
  # ● 将事件内容转为备注字符串(内部使用)
  #--------------------------------------------------------------------------
  def join_command_note(list, linefeed)
    (list.collect {|x| x.parameters[0]}).join(linefeed)
  end
  #--------------------------------------------------------------------------
  # ● 切分备注用字段(内部使用)
  #--------------------------------------------------------------------------
  def split(text)
    text.slice(SEP_REGEX).to_s
  end
  #--------------------------------------------------------------------------
  # ● 取得备注参数值(内部使用)
  #--------------------------------------------------------------------------
  def get_note(param_name, default = nil)
    return default if self.note == "" or not @note_set.include?(param_name)
    return @note_set[param_name] == nil ? default : @note_set[param_name]
  end
  #--------------------------------------------------------------------------
  # ● 设置备注参数值(内部使用)
  #--------------------------------------------------------------------------
  def set_note(param_name, value)
    @note_set ||= {}
    @note_set[param_name] = value
  end
  #--------------------------------------------------------------------------
  # ● 获取标签备注值(内部使用)
  #--------------------------------------------------------------------------
  def get_label(label, identifier, linefeed)
    # 返回已设置的缓存值
    event_id = label.sub!(/^([0-9]+),/, "") ? $1.to_i : note_event_id
    label = default_label(identifier) if label == ""
    $data_notes ||= {}
    if $data_notes.has_key?([event_id, label])
      return $data_notes[[event_id, label]]
    end
    # 初始化公共事件
    $data_common_events ||= load_data("Data/CommonEvents.rxdata")
    event = $data_common_events[event_id]
    return "" if event.nil? or event.list.length == 1
    # 检索标签
    list = event.list
    start_index = 0
    end_index = list.length
    for index in 0...list.length
      next if list[index].code != 118
      if list[index].parameters[0] == label
        start_index = index + 1
      elsif start_index > 0
        break end_index = index
      end
    end
    # 检索结果
    if start_index == 0
      result = ""
    else
      cmds = list[start_index...end_index]
      cmds.reject! {|x| not [101, 401, 108, 408, 355, 655].include?(x.code)}
      result = join_command_note(cmds, linefeed)
    end
    $data_notes[[event_id, label]] = result
  end
  #--------------------------------------------------------------------------
  # ● 获取文件备注值(内部使用)
  #--------------------------------------------------------------------------
  def read_ini_file(label)
    return unless FileTest.exist?(NOTE_FILE)
    # 初始化标签
    label = default_label(nil) if label == ""
    # 打开备注文件
    File.open(NOTE_FILE, "r") do |f|
      label_key = "[#{label}]\n"
      found = false
      # 循环每一行
      f.each_line do |l|
        # 注释的情况下,继续
        next if l[0] == ?; or l[0] == ?#
        # 标签不符的情况下,继续
        next unless l == label_key or found
        # 跳过下一行
        (found = true; next) unless found
        # 如果已到下一个标签就中断
        break if l[0] == ?[
        # 获得键值对
        pair = l.split("=", 2)
        next if pair.size < 2
        key = pair[0].strip
        value = pair[1].strip
        # 值运行成功为表达式,失败默认为字符串
        result = lambda{|v| eval(value) rescue value}.call($game_variables)
        # 设置备注值
        @note_set[key] = result
      end
    end
  end
  #--------------------------------------------------------------------------
  # ● 获取超长标签的公共事件ID(内部使用)
  #--------------------------------------------------------------------------
  def note_event_id
    class_name = self.class.to_s[5..-1]
    eval(class_name.upcase + "_COMMON_EVENT_ID") rescue 0
  end
  #--------------------------------------------------------------------------
  # ● 获取超长标签默认名称(内部使用)
  #--------------------------------------------------------------------------
  def default_label(id)
    if self.is_a?(RPG::Event)
      sprintf("%03d#%s%s", $game_map.map_id, self.name, id ? "_#{id}" : "")
    else
      self.name + (id ? "_#{id}" : "")
    end
  end
  #--------------------------------------------------------------------------
  # ● 解析备注字符串(该方法每对象通常只会执行一次,除非运行时整体重设备注)
  #--------------------------------------------------------------------------
  def analyze
    @note_set = {}
    # 如果备注为空则返回
    return if @note == nil or @note == ""
    # 如果备注为标签引用
    if @note[1] == NOTE_LABEL_CHAR
      @note = SEPARATOR + get_label(@note[2..-1], nil, NOTE_DATA_LF)
    elsif @note[1] == NOTE_FILE_CHAR
      read_ini_file(@note[2..-1])
      return
    end
    # 解析备注字符串
    param_regex = /^ *([A-Za-z0-9_]+)\b([+:=.\-]?(.*?))$/
    # 转义分隔符
    note_array = @note[1..-1].gsub(/\\#{DELIMITER}/, "\001").split(DELIMITER)
    note_array.each do |x|
      result = nil
      if x[param_regex] != nil
        param_name = $1
        param_value = $2
        param_exp = $3.gsub(/\001/, DELIMITER)
        # 开关型(+)
        if param_value.strip == "" or param_value[0] == ?+
          result = true
        # 开关型(-)
        elsif param_value[0] == ?-
          result = false
        # 数值型
        elsif param_value[0] == ?=
          result = lambda{|v| eval(param_exp) rescue nil}.call($game_variables)
        # 字符型
        elsif param_value[0] == ?:
          result = param_exp
        # 标签型
        elsif param_value[0] == ?.
          result = get_label(param_exp, param_name, NOTE_STRING_LF)
        end
        @note_set[param_name] = result
      end
    end
  end
  #--------------------------------------------------------------------------
  # ● 判断方法名是否合法
  #--------------------------------------------------------------------------
  def respond_to?(method_name, *)
    method_name.to_s[/^_.+/] == nil ? super : true
  end
  #--------------------------------------------------------------------------
  # ● 狂野模式覆盖注入
  #--------------------------------------------------------------------------
  def define_wild_methods
    return unless USE_WILD_MODE
    v = instance_variables.select {|i| instance_variable_get(i).is_a?(Numeric)}
    v.each do |i|
      next if i == "@id"
      var = i[1..-1]
      get_method = var.to_sym; wild_get = "_#{var}".to_sym
      set_method = "#{var}=".to_sym; wild_set = "_#{var}=".to_sym
      self.class.send :define_method, get_method do
        send wild_get, instance_variable_get(i)
      end
      self.class.send :define_method, set_method do |value|
        send wild_set, value
        instance_variable_set(i, value)
      end
    end
  end
  #--------------------------------------------------------------------------
  # ● 工具方法:扁平化数组
  #--------------------------------------------------------------------------
  def flat_array(param)
    param.to_a.map {|f| f.to_a}.flatten
  end
end

#==============================================================================
# ■ RPG 模块的植入
#------------------------------------------------------------------------------
#   以下植入14个数据库内容模块(含地图数据和事件数据)
#==============================================================================
module RPG
  [Actor, Class, Enemy, State, Animation, Tileset, MapInfo, Event].each do |c|
    c.send :include, DataNoteCore
    c.send :define_method, :name do
      @name1 ||= (@name.sub(DataNoteCore::SEP_REGEX) {|s| ""}).strip
    end
    c.send :define_method, :name= do |value|
      @name1 = value.strip
      @name = @name1 + DataNoteCore::SEPARATOR + note
    end
    c.new.define_wild_methods unless c == RPG::Event
  end
  [Item, Skill, Weapon, Armor].each do |c|
    c.send :include, DataNoteCore
    c.send :define_method, :description do
      @description1 ||= (@description.sub(DataNoteCore::SEP_REGEX) {|s| ""}).strip
    end
    c.send :define_method, :description= do |value|
      @description1 = value.strip
      @description = @description1 + DataNoteCore::SEPARATOR + note
    end
    c.new.define_wild_methods
  end
  [Troop, CommonEvent].each do |c|
    c.send :include, DataNoteCore
  end
end

#==============================================================================
# ■ Game_Event
#==============================================================================
class Game_Event
  include DataNoteCore
end
[/pre]
公共事件的超长备注写法示例:
p1.png

评分

参与人数 5星屑 +250 赞 +5 收起 理由
灯笼菜刀王 + 1 神之脚本
RyanBern + 250 + 1 精品文章plus
hyrious + 1 塞糖
porlutia + 1 塞糖
张咚咚 + 1 塞糖

查看全部评分

SailCat (小猫子·要开心一点) 共上站 24 次,发表过 11 篇文章 上 次 在: [2006年01月28日11:41:18 星期六] 从 [162.105.120.91] 到本站一游。
梦石
0
星屑
9557
在线时间
5074 小时
注册时间
2013-6-21
回帖
3459

开拓者贵宾剧作品鉴家

发表于 2017-10-14 23:55:43 | 显示全部楼层
没太想明白为什么要重新定义 description 和 description= 这两个方法。个人认为只定义 description 就足够了(返回 @description 处理后的部分即可)。description= 这个方法调用的场合非常少,正常游戏不应该去调用它(除非在运行时一定要改动数据库)。

然后,建议使用元编程来免去大量重复的篇幅,例如
[pre lang="Ruby"]class A; end
class B; end
class C; end

[A, B, C].each do |i|
  i.send :define_method, :hello do
    puts "hello world"
  end
end

A.new.hello  # hello world
B.new.hello  # hello world
C.new.hello  # hello world
[/pre]

点评

也可以用模块的 included 钩子  发表于 2017-10-15 07:45
↓ 见层主的示例代码  发表于 2017-10-15 07:45
后面那里用的是include模块接口,元编程的方法试了不管用。把name重定义在Data_Note里,根本覆盖不掉原来的方法。  发表于 2017-10-15 02:48

评分

参与人数 1星屑 +20 收起 理由
guoxiaomi + 20 友好交流

查看全部评分

回复

使用道具 举报

…あたしは天

梦石
0
星屑
2308
在线时间
4033 小时
注册时间
2010-10-4
回帖
10548

开拓者贵宾

发表于 2017-10-15 07:47:08 | 显示全部楼层
本帖最后由 taroxd 于 2017-10-15 07:56 编辑

我建议在脚本里写好可能定义的字段,然后使用 define_method 而不是 method_missing?。method_missing? 是很强力的工具,但这里其实并不是很必要。

理由:
1. 可能定义的字段不会太多
2. typo 时增加 debug 难度,如某个地方手滑写脚本写成了 $data_actors[1].descrition
3. method_missing 不适合自省。比如查方法列表的时候查不到这些方法,并且你也没有重定义 respond_to?
4. 如果一定要用 method_missing 避免在脚本里写出所有字段,请把这类方法加一个前缀(如 xxx_),在方法名不以 xxx_ 开头时,在 method_missing? 中调用 super。同时重定义 respond_to?。在 method_missing? 中调用某一个方法之后,最好使用 define_method 或者 class_eval 真正定义这个方法,无论是从效率还是从自省的角度来说。前缀的必要性还体现在,如果备注里有一个 name 字段和默认的字段重合,emmmm...
5. 觉得第 4 条好麻烦?还是用 define_method 吧。使用 define_method 可以不加前缀,因为在 define_method 的时候就可以检查方法是否已经被定义

回 @RyanBern :description= 应该是原本数据库自带的方法,覆盖定义后使得该方法正常运行。

点评

预定义字段就不能模拟va mv 了。重名的问题,get_param(name),直接用方法名本来就是设计成语法糖……typo了会返回nil  发表于 2017-10-15 16:34
我其实的是,反正几乎用不到 description=,不正常运行也无所谓(  发表于 2017-10-15 09:12

评分

参与人数 1星屑 +20 收起 理由
RyanBern + 20 提出了醋瞎的建议

查看全部评分

回复

使用道具 举报

遠航の猫咪

梦石
3
星屑
26947
在线时间
2423 小时
注册时间
2005-10-15
回帖
987

开拓者

 楼主| 发表于 2017-10-15 18:38:46 | 显示全部楼层
本帖最后由 SailCat 于 2017-10-15 18:43 编辑
taroxd 发表于 2017-10-15 07:47
我建议在脚本里写好可能定义的字段,然后使用 define_method 而不是 method_missing?。method_missing? 是 ...

更新了1.1版本,楼上的几个问题简单说一下:

1. VA和MV的备注字段就是个字符串,理论上可以写任何东西,没有理由非要规定XP的备注只能定义有限的集合。我这里只是数据库内容的扩展插件,具体要扩展什么东西,我不可能预知。
2. 如果不用method_missing而是去定义方法,就无法做到我说的“方便书写备注”的作用了。比如要给技能设定冷却,一共100个技能,85个小招不用冷却,15个大招需要冷却,就没有必要每一个去写#cd=x,只要把大招写上#cd=x,小招留空,最后植入脚本用.cd(0)就可以了。开关式的,理论上也只用定义true的项目就可以了(因为ruby把nil和false都解释为假),当然,我刚修改的新版本显式的加了false的设定。
3. 我是为了备注功能可以方便和通用,不是最大限度的追求代码好看。元编程刚才测试成功了,现在改成元编程方式了,非常感谢。但如果硬要避开method_missing的话,脚本使用者备注的书写方法压力会很大。
4. 没定义respond_to?导致不报错的情况:调用未定义且没有默认值的标识会返回nil,对nil进行大部分常规操作都会报错,这里不报错就那里报错,而且错误都一样(NoMethodError),并不存在调试不便的问题。
5. 加前缀的问题,不魔改编辑器的话,XP内部能用的description只有100个字符,name只有30个字符……既然是备注,我还是给用户留点地方吧,标识符用户想写短我没必要非要让他写长……

点评

↓ 所以我楼下第二段代码也写了 method_missing 的方法  发表于 2017-10-16 14:05
↓实名反对“要求使用者在脚本里面的配置部分加上 "cd"” 这种操作  发表于 2017-10-16 11:19
如果觉得用户配置麻烦,至少把 3L 的 4 做一下吧。另外看你第 4 点,是不是没明白 respond_to? 是做什么用的?虽然是个细节,但是重定义一下没什么坏处  发表于 2017-10-15 20:41
我看你一直在说“备注的书写压力大”;但使用 define_method 代替 method_missing 完全没有影响备注的书写方式。  发表于 2017-10-15 20:39
define_method 唯一比 method_missing? 不便的地方是必须预先知道用户想要定义 "cd" 这个东西,这是可以用脚本中的配置实现的。  发表于 2017-10-15 20:37

评分

参与人数 1星屑 +20 收起 理由
guoxiaomi + 20 塞糖

查看全部评分

SailCat (小猫子·要开心一点) 共上站 24 次,发表过 11 篇文章 上 次 在: [2006年01月28日11:41:18 星期六] 从 [162.105.120.91] 到本站一游。
回复

使用道具 举报

…あたしは天

梦石
0
星屑
2308
在线时间
4033 小时
注册时间
2010-10-4
回帖
10548

开拓者贵宾

发表于 2017-10-15 20:52:21 | 显示全部楼层
本帖最后由 taroxd 于 2017-10-15 21:05 编辑
SailCat 发表于 2017-10-15 18:38
更新了1.1版本,楼上的几个问题简单说一下:

1. VA和MV的备注字段就是个字符串,理论上可以写任何东西, ...

具体来说,我想表达的是这个意思(代码未经实际测试,只是用来表达意思)
[pre lang="ruby"]# using define_method
module Data_Note
  # for users to configure
  NOTE_TYPE_LIST = ["cd"]

  NOTE_TYPE_LIST.each do |prop|
    define_method prop do |default = nil|
      get_note(prop, default)
    end
  end
end
[/pre]

[pre lang="ruby"]
# using method_missing
module Data_Note
  METHOD_PREFIX = 'xxx_'
  def method_missing(method_name, *args, &block)
    prop = method_name.to_s   
    # when prefix does not match, call super to invoke other handlers or raise an exception
    return super unless prop.sub!(/^#{METHOD_PREFIX}/, '')
    # define the method to improve performance   
    # Also, method calls for introspection, such as Data_Note.instance_methods, will return the correct result
    Data_Note.send :define_method, method_name do |default = nil|
      get_note(prop, default)
    end
    # return the value.
    # When too many arguments are given, an exception will be raised here.
    get_note(prop, *args)
  end

  # use respond_to_missing? for RUBY_VERSION >= "1.9"
  def respond_to?(method_name, *)
    method_name.to_s.start_with?(METHOD_PREFIX) || super
  end
end[/pre]








点评

查到了,default = nil的=nil要去掉……  发表于 2017-10-22 23:42
RGSS1的1.81版下面,上述代码测试失败…… Data_Note.send,报错 Data_Note::send,报错 self.send,报错 self.class.send,报错……我再研究研究  发表于 2017-10-22 23:38
回复

使用道具 举报

遠航の猫咪

梦石
3
星屑
26947
在线时间
2423 小时
注册时间
2005-10-15
回帖
987

开拓者

 楼主| 发表于 2017-10-23 01:39:32 | 显示全部楼层
update v1.2自顶,见主楼内容
SailCat (小猫子·要开心一点) 共上站 24 次,发表过 11 篇文章 上 次 在: [2006年01月28日11:41:18 星期六] 从 [162.105.120.91] 到本站一游。
回复

使用道具 举报

梦石
0
星屑
9862
在线时间
580 小时
注册时间
2017-9-28
回帖
203
发表于 2017-10-23 08:33:19 | 显示全部楼层
变成 location.search
[pre lang="ruby"]  SEPARATOR = "?"
  ...
  def analyze
    ...
    note_array ||= @note[1, @note.length - 1].split("&")[/pre]

点评

哈哈,如果MV没有备注还真可以这么搞……  发表于 2017-10-23 08:40

评分

参与人数 2星屑 +20 赞 +1 收起 理由
RyanBern + 20 醋瞎喵喵喵
SailCat + 1 我很赞同

查看全部评分

喵喵喵
回复

使用道具 举报

遠航の猫咪

梦石
3
星屑
26947
在线时间
2423 小时
注册时间
2005-10-15
回帖
987

开拓者

 楼主| 发表于 2017-10-23 10:40:06 | 显示全部楼层
update 1.3 并增补一个范例实装插件
SailCat (小猫子·要开心一点) 共上站 24 次,发表过 11 篇文章 上 次 在: [2006年01月28日11:41:18 星期六] 从 [162.105.120.91] 到本站一游。
回复

使用道具 举报

遠航の猫咪

梦石
3
星屑
26947
在线时间
2423 小时
注册时间
2005-10-15
回帖
987

开拓者

 楼主| 发表于 2017-10-27 19:34:15 | 显示全部楼层
update 1.5 备注的长度不再受限
SailCat (小猫子·要开心一点) 共上站 24 次,发表过 11 篇文章 上 次 在: [2006年01月28日11:41:18 星期六] 从 [162.105.120.91] 到本站一游。
回复

使用道具 举报

遠航の猫咪

梦石
3
星屑
26947
在线时间
2423 小时
注册时间
2005-10-15
回帖
987

开拓者

 楼主| 发表于 2017-10-28 23:56:28 | 显示全部楼层
update 1.6 并补充第2个范例,以后的实装插件将独立发布
SailCat (小猫子·要开心一点) 共上站 24 次,发表过 11 篇文章 上 次 在: [2006年01月28日11:41:18 星期六] 从 [162.105.120.91] 到本站一游。
回复

使用道具 举报

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

本版积分规则

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

在本版发帖返回顶部