设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
12
返回列表 发新帖
楼主: zhang493154524
打印 上一主题 下一主题

[有事请教] 求助大神

[复制链接]

Lv1.梦旅人

梦石
0
星屑
200
在线时间
21 小时
注册时间
2024-6-21
帖子
20
11
 楼主| 发表于 2024-7-15 20:53:57 | 只看该作者
百里_飞柳 发表于 2024-7-15 20:02
名字加框是指什么,具体是怎么编写的转义符呢?
最好直接把文本内容列出来 ...

这个就是我在事件页操作的,然后游戏测试以后就这样了,第一个正常,中间那个文本框不带\name显示正常,但是第三个重新框带\name,但是就是显示不出来了

@ZCIFE@WMSSNKUS7J4`FQ6C.png (360.48 KB, 下载次数: 12)

@ZCIFE@WMSSNKUS7J4`FQ6C.png
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
200
在线时间
21 小时
注册时间
2024-6-21
帖子
20
12
 楼主| 发表于 2024-7-15 21:05:26 | 只看该作者
百里_飞柳 发表于 2024-7-15 20:02
名字加框是指什么,具体是怎么编写的转义符呢?
最好直接把文本内容列出来 ...

方便的话价加个企鹅或者啥的呗
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
200
在线时间
21 小时
注册时间
2024-6-21
帖子
20
13
 楼主| 发表于 2024-7-16 06:32:02 | 只看该作者
百里_飞柳 发表于 2024-7-15 20:02
名字加框是指什么,具体是怎么编写的转义符呢?
最好直接把文本内容列出来 ...

我在事件页设置的文本为居中,设置第二项文本时候本来设置的巨下,但是却和上一个文本一样执行起来还是居中

点评

我晚上看看,你先试试最新版本的对话框有这个问题吗  发表于 2024-7-16 09:21
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
200
在线时间
21 小时
注册时间
2024-6-21
帖子
20
14
 楼主| 发表于 2024-7-16 09:28:14 | 只看该作者
百里_飞柳 发表于 2024-7-15 20:02
名字加框是指什么,具体是怎么编写的转义符呢?
最好直接把文本内容列出来 ...

#=============================================================================
# ■ 对话框扩展 by 老鹰(https://github.com/OneEyedEagle/EAGLE-RGSS3)
#=============================================================================
$imported ||= {}
$imported["EAGLE-MessageEX"] = "1.11.8"
#=============================================================================
# - 2024.6.23.11 修复暗色背景在淡出后,背景消失的bug
#=============================================================================
# 【兼容模式】
# - 本模式用于与其他对话框兼容,确保其他对话框正常使用,同时可以用本对话框及扩展
#
#     若此常量被设为 true,则本对话框不再覆盖其他对话框:
#        而在事件脚本中调用 $game_message.eagle_message = true 后,
#         本对话框将被激活,变更为正在使用的对话框;
#        调用 $game_message.eagle_message = false 则可切回之前的原有对话框。
#
#     若此常量被设为 false,则本对话框将完全替代默认对话框。
#
# - 由于对话框存在动态的开启关闭特效,若对话框切换后,旧对话框仍然显示在地图上,
#   则请在切换对话框的事件脚本前后添加【等待10帧】,来保证旧对话框有时间顺利完成关闭。
#
EAGLE_MSG_EX_COMPAT_MODE = false

module MESSAGE_EX
#=============================================================================
# ● 什么是转义符?
#=============================================================================
# - 鼠标放置于编辑器对话框中停留一段时间,即可看到默认支持的转义符及其作用
#     如 \| 代表文本显示到该位置时,将等待 1s
#     如 \c[5] 代表之后的文本将使用 5 号颜色进行绘制
#
# - 转义符即为特别约定的、能够起到特殊功能的文本,
#     在对话框中,我们约定转义符均以 \ 符号为开头,之后接其他符号、字母、数字,
#     并且一般情况下转义符自身不会被绘制、显示

#=============================================================================
# ● 什么是扩展转义符?
#=============================================================================
# - 本插件对默认的转义符进行了扩展,
#   我们约定在对话框中编写 \code[param] 类型的文本,即可执行 code 对应的功能,
#   而 param 为特殊格式的字符串,本插件中命名其为“变量参数字符串”,
#   它将被作为调用 code 功能所需的自定义设置传入
#
# - code “指令名”解析:
#     下述列举出的各类转义符(英文、数字的组合)
#
# - param “变量参数字符串”解析:
#    · 由 变量名(字母组合)+ 参数值(整数或nil(用$符号代表传入的为nil))重复构成
#       - 字符串中可以增加无意义的空格,用于在视觉上区分不同变量
#       - 字符串中 变量名 和 参数值 之间可以添加 = 符号,增强阅读性
#    · 当传入 无变量名 的 参数值 时,将存入其【默认】变量
#    · 对于没有传入值的 变量 ,将读取上一次设置所存储的值
#    · 带有【重置】的变量,将在每一次设置转义符时,重置为脚本中的预设值
#
# - 示例:
#    某个转义符的帮助:
#      \foo[param] → 执行某个功能
#         变量名称 a → 【默认】设置某个功能
#         变量名称 b → 设置某个功能
#         变量名称 c → 设置某个功能
#
#    在对话中编写:
#      对话文本\foo[1 b=-1 tc=0 d$],其他对话文本
#
#    实际产生的效果:
#    → 调用 foo 的功能,同时给它的所有变量传入预设值
#      再给 a 变量传入值 1,b 变量传入值 -1,tc 变量传入值 0,d 变量传入值 nil
#      (转义符的帮助中并没有写 tc 和 d 两个变量,
#         但是这样传入并不会出错,也不会产生任何其他影响)
#
# - 注意:
#    · 若没有明确说明,则转义符\code[param]不会被绘制、显示
#    · “指令名”和“变量参数字符串”的大小写差异不会造成影响
#    · 转义符会在对话文本逐字显示到它时生效
#        但若有【预先】,则会在绘制开始前生效(绘制中途不会再次生效)
#        但若有【结尾】,则会在全部文字绘制完成后生效

#=============================================================================
# ● 扩展转义符列表
#=============================================================================
# 【索引】
#    此处放置转义符及其一句话简介,可通过搜索转义符进行快速定位
#
# (文本替换类)
#    \conv   自定义的文本替换
#    \rb     利用Ruby脚本获取替换文本
#    \info   显示数据库指定对象的图标和名称
#    \nl     换行符
#
# (绘制类)
#    \pic    绘制图片
#
# (控制类)
#    \font   文字绘制的基本设置,包括文字大小、加粗、阴影、边框、发光、底纹等
#    \win    对话框的基本设置,包括对话框皮肤背景、位置、大小、文字显示等
#    \move   对话框的动态移动
#    \auto   自动对话的设置
#    \pop    气泡对话框的基本设置,包括显示位置、大小、指示箭头等
#    \face   脸图的动态设置,包括脸图当前显示索引、循环等
#    \facep  脸图的预先设置,包括脸图位置、移入移出方式等
#    \facem  脸图的实时动作,包括跳跃、移动等
#    \name   姓名框的预先设置,包括姓名文本、姓名框位置、大小等
#    \pause  等待按键时的帧动画设置,包括帧动画类型、显示位置等
#    \shake  控制对话框的震动
#    \wait   直接等待
#    \ins    对话框立即完全显示
#    \hold   保留当前对话框,直到下一个不包含该转义符的对话框关闭
#    \close  当前对话框必定处理关闭
#    \next   当前对话框不关闭,下一次的显示文本在该对话框内继续显示
#    \clc    向上移出隐藏全部文字,在新一行里继续绘制之后的文字
#
# (文字特效类)
#    \cin    文字移入
#    \cout   文字移出
#    \uout   文字消散移出
#    \csin   文字正弦扭曲
#    \cwave  文字上下浮动
#    \cswing 文字左右摇摆
#    \czoom  文字缩放
#    \cshake 文字抖动
#    \cshake2 文字抖动(高频率版本)
#    \cflash 文字闪烁
#    \cmirror 文字镜像
#    \cu     文字间歇消散
#    \ctog   文字间歇切换
#    \cneon  文字霓虹灯变色
#    \cmc    文字叠加
#    \cjump  文字跳跃
#    \cfk    文字明灭闪烁
#
# (变量环境)
#    \env    保存、读取应用指定的变量环境,方便进行不同对话框设置之间的切换
#    \temp   对话框关闭时,将对话框重置为开启前的环境
#
# (高级)
#    \func   设置一些进阶功能
#    \eval   在对话中途,执行Ruby脚本
#    \set    设置文字分组
#    \setm   对指定文字分组进行处理
#
# (扩展)
#    \cg     渐变色绘制

#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# ○ 文本替换类
#     此类别的转义符将在绘制开始前,进行一次对应文本的替换
#     将按照下面的排列顺序(从上往下)进行替换,因此请注意相互之间的嵌套层级
#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#----------------------------------------------------------------------------
#  \conv[string] 或 \M[string]
#----------------------------------------------------------------------------
# 【功能】
#    整个转义符将被替换成 CONVERT_ESCAPE 中所设置的 string 所对应的文本
#
# 【参数】
#    string → 在CONVERT_ESCAPE哈希表中的键值(若不存在,则返回空字符串)
#
# 【常量设置:文本替换】
# (由于解析问题,字符串中请将 "\" 替换成 "\\")
# (如果目标转义符是用 <> 代替 [],如姓名框中的名称,
#    则需将姓名框中名称中的 "\" 替换成 "\e")
CONVERT_ESCAPE = {
# String => String,
  "底部" => "\\win[ali0dw1dh1o2do-2dx0dy-60]\\pause[do0o4]\\temp",
  "顶部" => "\\win[ali0dw1dh1o8do-8dx0dy60]\\pause[do0o4]\\temp",
}
# (之后的常量也推荐用这种方式进行新增/覆盖)
# (也可以在新脚本页中新增 module MESSAGE_EX 和 end ,并在其中添加常量设置)
CONVERT_ESCAPE["系统"] = "\\win[ali1dw1dh1o5do-5dx0dy0]\\pause[do2o5]\\temp"
#
# 【示例】
#   - 脚本常量
CONVERT_ESCAPE["测试用"] = "这是一句测试语句。"
#   - 对话编写
#       测试对话为 \conv[测试用]。
#    或 测试对话为 \M[测试用]。
#   - 实际对话
#       测试对话为 这是一句测试语句。
#
#----------------------------------------------------------------------------
#  \rb{string}
#----------------------------------------------------------------------------
# 【功能】
#    整个转义符将被替换成 eval(string) 的执行结果(强制转化为文本)
#
# 【参数】
#    string → Ruby脚本语句,可以用 ; 分隔多句,不可含有花括号
#
# 【注意】
#   该转义符使用花括号,同时 string 中不可以出现花括号
#   可用 s 代替 $game_switches ,用 v 代替 $game_variables
#   可用 event 代表执行当前对话框的事件(Game_Event对象)
#
# 【示例】
#   - 对话编写(1号变量值为1,2号变量值为2)
#       这是一句测试用对话,都已经说了\rb{v[1]+v[2]}次了!
#   - 实际对话
#       这是一句测试用对话,都已经说了3次了!
#
#----------------------------------------------------------------------------
#  \info[type+id,n]
#----------------------------------------------------------------------------
# 【功能】
#    整个转义符将被替换成指定数据库对象的 图标+名称 的文本
#
# 【参数】
#    type → 数据库对象的类型(s代表技能,i代表物品,w代表武器,a代表防具)
#      id → 数据库对象的序号
#      n  → 绘制类型(可省略,0默认绘制图标+名称,1仅绘制图标,2仅绘制名称)
#
# 【注意】
#    加号只是表示由两个必须的参数构成,实际编写时不要写 + 符号!
#
# 【示例】
#   - 对话编写
#       1号物品是\info[i1],2号武器的名称是\info[w2,2]
#   - 实际对话
#       1号物品是【图标】恢复剂,2号武器的名称是战斧
#
#----------------------------------------------------------------------------
#  \nl
#----------------------------------------------------------------------------
# 【功能】
#    将被替换成换行转义符 \n(效果等同编辑器中的手动回车)
#
# 【注意】
#    由于编辑器自身限制,部分文本框无法识别输入的\n,因此添加了该转义符,
#    实际上并没有什么用处
#

#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# ○ 绘制类
#     此类别的转义符将被视为绘制内容,与通常文字一样占用绘制空间
#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#----------------------------------------------------------------------------
#  \pic[filename|param]
#----------------------------------------------------------------------------
# 【功能】
#    在当前位置绘制指定名称的图片
#
# 【参数】
#    filename → 图片的名称,可省略后缀名
#     (图片存放于 Grphics/Pictures 目录下)
#    param → 变量参数字符串,可省略不写
# (变量一览)
#      w → 指定绘制图片的宽度(默认与原图一致,若不同则利用stretch_blt进行缩放)
#      h → 指定绘制图片的高度
#    opa → 【默认】指定绘制图片的不透明度(默认255)
#
# 【示例】
#   - 对话编写
#       这是测试对话\pic[猫猫快乐]
#   - 实际对话
#      (在转义符所在位置绘制目录下文件名为 猫猫快乐 的图片,显示宽高不变)
# 【示例】
#   - 对话编写
#       这是测试对话\pic[猫猫疑惑|w=50 h=50]
#   - 实际对话
#      (在转义符所在位置绘制目录下文件名为 猫猫疑惑 的图片,且宽度高度固定为50)
#

#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# ○ 控制类
#     此类别的转义符将对各类功能进行设置、调用
#      对于带有“是否”描述的变量,数字0代表 false,正数(推荐数字1)代表 true
#      对于未说明 nil 效果的变量,请尽量不要传入 nil(传入符号$代表传入nil)
#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#----------------------------------------------------------------------------
#  \c[id]
#----------------------------------------------------------------------------
# 【功能】
#    变更之后的文字绘制颜色(扩展了默认的\c[id]转义符)
#
# 【参数】
#    id → 可传入 TEXT_COLORS 常量中所设置的字符串,将匹配其对应的颜色
#            如 \c[pink] 将使用 "pink" 所对应的 Color 作为文字颜色
#         若匹配失败,将执行 id.to_i 转为数字,使用默认的索引颜色
#       (若传入 -1,则重置为 DEFAULT_COLOR_INDEX ,同时不透明度重置为 255)
#
# 【常量设置:每次开启对话框时,文字颜色重置为该索引颜色】
DEFAULT_COLOR_INDEX = 0
#
# 【常量设置:标识符映射到Color】
TEXT_COLORS = {
  "pink" => Color.new(255, 187, 217),
}
#
# 【示例】
#   - 脚本常量
TEXT_COLORS["gold"] = Color.new(255,215,0)
#   - 对话编写
#       这是\c[gold]测试\c[0]对话。
#   - 实际对话
#      (“测试”文字将变成设置的gold对应颜色)
#
#----------------------------------------------------------------------------
#  \font[param]
#----------------------------------------------------------------------------
# 【功能】
#    设置文字的绘制参数
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    name → 字体名称的索引(在INDEX_TO_FONT中设置)(若为nil,则与默认一致)
#    size → 【默认】字体大小
#    c → 文字颜色的索引号(与 \c 一致)
#       (若传入 -1,则重置为 DEFAULT_COLOR_INDEX ,同时不透明度重置为 255)
#    ca → 文字的不透明度(0~255)
#    i → 是否斜体(font.italic)
#    b → 是否加粗(font.bold)
#    s → 是否添加阴影(font.shadow)
#    o → 是否添加边框(font.outline)
#    or/og/ob/oa → 设置边框颜色RGBA(0~255)
#    p → 底部花纹的类型(0不绘制,1边框,2实心方框)
#    pc → 底部花纹的颜色索引号(具体查看 Windowskin 右下角,同默认\c[id]颜色)
#    l → 是否绘制外发光(若开启,则强制关闭阴影和边框)
#    lc → 外发光的颜色索引号
#    lp → 外发光的强度
#    d → 是否绘制删除线
#    dc → 删除线的颜色索引号
#    u → 是否绘制下划线
#    uc → 下划线的颜色索引号
#    k → 是否生成文字破碎效果(参考:http://www.whiteflute.org/wfrgss/)
#    kv → 单个像素的破碎概率(百分数,数字越大,文字破碎效果越明显)
#
# 【常量设置:参数预设值】
FONT_PARAMS_INIT = {
# \font[]
  :name => nil,
  :size => nil, # 字体大小
  :ca => 255, # 不透明度
  :i => nil, # 斜体绘制
  :b => nil, # 加粗绘制
  :s => nil, # 阴影
  :o => nil, # 描边
  :or => nil,
  :og => nil,
  :ob => nil,
  :oa => nil,
  :p => 0, # 底纹
  :pc => 0, # 底纹颜色index
  :l => 0, # 外发光
  :lc => 0, # 外发光颜色index
  :lp => 2, # 外发光强度
  :d => 0, # 删除线
  :dc => 0, # 删除线颜色index
  :u => 0, # 下划线
  :uc => 0, # 下划线颜色index
  :k => 0, # 文字破碎
  :kv => 50, # 像素破碎概率
}
#
# 【常量设置:序号映射到字体名称】
INDEX_TO_FONT = {
# 数字 => 字体名称字符串
  1 => "黑体",
}
#
# 【示例】
#   - 对话编写
#       这是\font[d=1]测试\font[d=0]对话。
#   - 实际对话
#      (“测试”文字将绘制删除线)
#
#----------------------------------------------------------------------------
#  \win[param]
#----------------------------------------------------------------------------
# 【功能】
#    对话框窗口的各类设置
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#  (窗口属性相关)
#    z → 对话框的z值(仅正整数有效)(默认取va里的200)
#    skin → 对话框所用windowskin的index(按常量设置进行 index → skin名称 映射)
#    bg → 对话框背景图片的index(按常量设置进行 index → 图片名称 映射)
#        (只有当 事件指令-显示文字-窗口背景 为 普通窗口 时,才生效)
#        (若图片读取成功,将不再显示窗口皮肤;若读取失败,仍绘制窗口皮肤)
#    bgo → 对话框背景图片与对话框的对齐原点(对应九宫格小键盘)
#        (默认背景图片的左上角与对话框左上角对齐,值为7)
#
#  (窗口位置相关)
#    o → 【默认】对话框的显示原点的类型(对应九宫格小键盘)(默认左上角7)
#    x/y → 对话框显示原点在屏幕上的坐标(默认nil,取va设置,对话框位于底部)
#    do → 将屏幕进行九宫格划分,对话框显示原点在屏幕上的位置类型(覆盖x/y)
#        (参考九宫格小键盘,有效值为-1~-9,-1在屏幕左下角,-5在屏幕中心点)
#    dx/dy → x、y坐标的增加量(默认0)
#    fix → 是否修正对话框位置(保证对话框完整显示在屏幕内)
#
#  (窗口大小相关)
#    w → 窗口内容的宽度(不含边框)(默认0不设置)(优先级高于dw和fw)
#    dw → 窗口内容宽度是否随文字绘制而动态变化(默认0,不动态变化)
#    fw → 窗口内容宽度是否设置为全部文字绘制完成时的宽度(优先级高于dw)
#    wmin/wmax → 窗口内容宽度的最小最大值(启用dw/fw时生效)(默认0不设置)
#    h → 窗口内容的高度(不含边框)(默认0不设置)(优先级高于dh和fh)
#       (若小于对话框的 line_height 方法值,则识别为行数,乘以行高,作为高度值)
#    dh → 窗口内容高度是否随文字绘制而动态变化(默认0,不动态变化)
#    fh → 窗口内容高度是否指定为全部文字绘制完成时的高度(优先级高于dh)
#    hmin/hmax → 窗口内容高度的最小最大值(启用dh/fh时生效)(若小于行高,则为行数)
#
#  (文本显示相关)
#    se → 启用的打字音类型index(默认0,按常量设置进行 index → 声效SE设置 映射)
#    ali → 文本的对齐方式(0左对齐,1居中对齐,2右对齐;默认0)
#    ck → 增加的字符间距值(默认0)
#    lh → 设置基础行高 line_height 值(若为nil则取当前字号)
#    ld → 增加的行间距值(默认0)(每一行的行高将取该行的最大高度)
#    cwi → 单个文字绘制完成后的等待帧数(最小值0)
#    cwo → 单个文字移出开始后的等待帧数(最小值0)
#    cor → 全部文字移出的顺序类型(0正序,1逆序,2乱序,默认0)
#    cfast → 是否允许按键快进(默认1,可以按键快进)
#    cdx/cdy/cdw/cdh → 文本绘制区域与窗口padding左侧/上侧/右侧/下侧的间距(默认0)
#
# 【常量设置:参数预设值】
WIN_PARAMS_INIT = {
# \win[]
  # (窗口属性相关)
  :z => 200,
  :skin => 0, # 对话框所用windowskin的类型
  :bg => nil, # 对话框背景所用图片(覆盖窗口皮肤)
  :bgo => 7,  # 对话框背景图片与对话框的对齐原点
  # (窗口位置相关)
  :o => 7, # 原点位置类型 默认为7左上角
  :x => nil, # 原点坐标xy
  :y => nil,
  :do => 0, # 相较于屏幕的九宫格位置(覆盖x/y的设置)
  :dx => 0, # 坐标偏移值xy
  :dy => 0,
  :fix => 0, # 是否进行位置修正,防止对话框跑出屏幕
  # (窗口大小相关)
  :w => 0,
  :h => 4,
  :dw => 0, # 若为1,则代表宽度会依据文字动态调整
  :fw => 0, # 若为1,则窗口打开时即为文字绘制完成时所需宽度值
  :wmin => 0, # 设置宽度的上下限(当dw==1时生效)
  :wmax => 0, # (有脸图时宽度会自动增加脸图宽度)
  :dh => 0, # 若为1,则代表高度会依据文字动态调整
  :fh => 0, # 若为1,则窗口打开时即为文字绘制完成时所需高度值
  :hmin => 0, # 设置高度的上下限(当dh==1时生效)
  :hmax => 0,
  # (文本显示相关)
  :se => 0, # 打字音类型序号(默认0,无声效)
  :ali => 0, # 设置文本对齐方式
  :ck => 0, # 增加的字符间距值
  :lh => nil, # 基础行高(若为nil,则取当前文字大小)
  :ld => 4, # 增加的行间距值
  :cwi => 2, # 单个文字绘制后的等待帧数(最小值0)
  :cwo => 0, # 单个文字开始移出后的等待帧数(最小值0)
  :cor => 0, # 全部文字移出的顺序类型
  :cfast => 1, # 是否允许快进
  :cdx => 0, # 文本左侧与窗口padding的间距
  :cdy => 0, # 文本上边距
  :cdw => 0, # 文本右边距
  :cdh => 0, # 文本下边距
}
#
# 【常量设置:序号映射到windowskin文件名】
#  (其中 index 必须为整数)
#  (窗口皮肤图片存储于 Graphics/System 目录下)
INDEX_TO_WINDOWSKIN = {
# 数字 => 皮肤文件名(可省略后缀)
  0 => "Window", # 默认所用皮肤名称
}
#
# 【常量设置:序号映射到窗口背景图片文件名】
# (图片的左上角会与对话框的左上角对齐)
# (其中 index 必须为整数)
# (图片存储于 Graphics/System 目录下)
INDEX_TO_WINDOW_BG = {
# 数字 => "背景图片名称"
}
#
# 【常量设置:序号映射到打字音效】
# (音效文件存储于 Audio/SE 目录下)
INDEX_TO_SE = {
# 数字 => SE文件名, 音量, 音调
  0 => ["", 80, 100], # 默认设置,不推荐修改
  1 => ["Cursor1", 40, 150],
}
#
# 【示例】
#   - 对话编写
#       \win[o5 do-5 dw1dh1]这是测试对话。
#   - 实际对话
#      (对话框显示在屏幕中心,且随着文字显示而动态扩展)
# 【示例】
#   - 对话编写
#       \win[o=2 do=-2 dy=-20 fw1fh1]这是测试对话,看看到底怎么样。
#   - 实际对话
#      (对话框显示在屏幕底部,且依据文字动态变更宽高)
#
#----------------------------------------------------------------------------
#  \move[param]
#----------------------------------------------------------------------------
# 【功能】
#    动态移动对话框位置,并等待移动结束
#
# 【注意】
#    若设置了 win 转义符的 do 参数,则该移动无效
#    若设置了 pop 转义符,则该移动无效
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    t → 移动所需的总共帧数(默认20帧)
#    x → 直接指定移动的目的地x(若为0,则重置为win转义符中的x,或RGSS中初始位置0)
#    y → 直接指定移动的目的地y(若为0,则重置为win转义符中的y,或RGSS中初始位置底部)
#    dx → 在当前位置基础上,水平移动的距离(正数为向右,负数为向左)
#    dy → 在当前位置基础上,竖直移动的距离(正数为向下,负数为向上)
#
# 【示例】
#   - 对话编写
#       这是测试对话,\move[dx=40 dy=30]看看到底怎么样。
#   - 实际对话
#      (在显示完“这是测试对话,”后,对话框将向下移动40,向下移动30的距离,
#        然后继续显示剩余的文字)
#
#----------------------------------------------------------------------------
#  \auto[t]
#----------------------------------------------------------------------------
# 【功能】
#    等待按键时,设置文本自动继续
#
# 【参数】
#    t → 在t帧后自动结束按键等待,并继续事件处理(默认nil,不自动继续)
#
# 【常量设置:参数预设值】
# 当未调用 \auto[t] 进行设置,或其值为 nil 时,读取此处的值
WIN_AUTO_T = nil
#
# 【常量设置:提示文本】
# 自动播放的UI中显示的文本
WIN_AUTO_TEXT = "自动播放"
#
# 【常量设置:UI的宽高】
WIN_AUTO_W = 100
WIN_AUTO_H = 40
#
# 【常量设置:UI的原点】
# 九宫格小键盘,如 5 代表中点为原点,9 代表右上角为原点
WIN_AUTO_O  = 9
#
# 【常量设置:UI的位置】
# 九宫格小键盘,如 3 代表绑定到对话框的右下角,5 代表绑定到对话框的中心
#  -2 代表绑定到屏幕的底部中点,-9 代表绑定到屏幕的右上角
WIN_AUTO_DO = -9
#
# 【常量设置:UI的坐标偏移值】
WIN_AUTO_DX = 0
WIN_AUTO_DY = 0
#
# 【示例】
#   - 对话编写
#       这是测试对话,看看到底怎么样。\auto[60]
#   - 实际对话
#      (在显示完成后,等待1s后自动关闭当前对话框,继续之后的事件指令)
#
#----------------------------------------------------------------------------
#  \pop[param] 【预先】
#----------------------------------------------------------------------------
# 【功能】
#    气泡类型对话框的各类设置
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#  (窗口属性相关)
#    skin → pop状态下对话框所用皮肤的index(按常量设置进行 index → skin名称 映射)
#         (默认nil,取win转义符中的skin变量的值)
#
#  (窗口位置相关)
#    id → 【重置】【默认】所绑定对象的id
#     【地图】设置 0 取执行当前对话框的事件的id
#              正数id 取当前地图id号的事件,目标事件不存在则取当前事件
#              负数id 取队列中数据库id号的角色,目标角色不在队伍中则取队首角色
#     【战斗】设置 正数id 取敌群中index序号为id的敌人,目标敌人不存在则pop无效
#              负数id 取我方参战角色中数据库id号的角色,目标角色不存在则pop无效
#    mx/my → 【重置】绑定到地图的 (mx, my) 处(同编辑器坐标)(格子中心为显示原点)
#           (当未设置id时,该变量设置才生效)
#    do → 对话框相对于绑定对象的位置类型(默认8,即对话框原点位于目标的顶部中间)
#         (参考九宫格小键盘,1代表目标左下角,5代表目标中心,9代表目标右上角)
#    o → 对话框原点的类型
#         (若为nil,则为 10-do,即当do为8时,对话框原点为底部中点2)
#    d → 对话框原点远离绑定对象中心点的像素值(默认0)
#         (用于设置对话框与绑定对象的距离,将按do自动决定xy如何增减)
#    dx/dy → x、y方向上的像素增量(默认0)
#    fix → 是否进行位置修正(保证pop对话框完整显示在屏幕内)
#
#  (窗口大小相关)(与win中相同,均覆盖win中的对应设置)
#    w → pop对话框的内容固定宽度(默认0不设置)(优先级高于dw/fw)
#    h → pop对话框的内容固定高度
#       (若小于对话框的 line_height 方法值,则识别为行数,乘以行高,作为高度值)
#    dw → 是否随文字绘制而动态变更pop对话框的内容宽度
#    fw → 是否指定为全部文字绘制完成时的所需宽度(覆盖dw)
#    dh → 是否随文字绘制而动态变更pop对话框的内容高度
#    fh → 是否指定为全部文字绘制完成时的所需高度(覆盖dh)
#
#  (Tag精灵相关)
#    tag → 所用的tag箭头图片的index(按常量设置进行 index → tag名称 映射)
#    td → 设置tag与所绑定对象的中心的远离偏移值(按pop的do自动设置xy)(默认0)
#
# 【常量设置:参数预设值】
POP_PARAMS_INIT = {
# \pop[]
  :skin => nil, # pop模式下所用skin类型
  :do => 8, # 对话框相对于绑定对象的位置(九宫格小键盘)
  :o => nil, # 对话框显示原点,若为nil,则取 10 - :do
  :d => 0, # 指定原点远离事件格子中心的偏移量
  :dx => 0,  # 指定x、y方向上的偏移量
  :dy => 0,
  :fix => 0, # 是否进行位置修正
  :w => 0, # 指定固定的宽度和高度(优先级高于win_params)
  :h => 0,
  :dw => 0, # 若为1,则代表宽度会依据文字动态调整
  :fw => 1, # 若为1,则窗口打开时将预绘制成文字区域最终大小
  :dh => 0, # 若为1,则代表高度会依据文字动态调整
  :fh => 1, # 若为1,则窗口打开时将预绘制成文字区域最终大小
  :tag => 1, # tag所用文件名index(0时表示不启用)
  :td => 3, # tag与绑定事件格子中心位置的远离值
}
#
# 【常量设置:序号映射到Tag】
# (其中 index 为正整数;为 0 时代表不启用tag)
# (图片存储于 Graphics/System 目录下)
# 【Tag图片解析】任意大小,3帧×3帧规格
#    7 8 9
#    4 5 6  ← pop对话框的原点位置类型 与 对应所用的tag位图区域
#    1 2 3
# (比如 pop对话框的原点类型为 2 时,tag显示在对话框底部中央,图像使用2号区域)
# 【注意】tag的存在不会使pop对话框产生额外偏移,请利用pop参数d/dx/dy自行移动
INDEX_TO_WINDOWTAG = {
  1 => "Window_Tag", # 默认所用tag名称
}
#
# 【常量设置:窗口皮肤绑定Tag】
# (当使用的窗口皮肤在此处有设置时,将必定使用此处对应设置的Tag)
WINDOWSKIN_TO_WINDOWTAG = {
  -1 => 1,
}
#
# 【示例】
#   - 对话编写
#       \pop[-1]这是测试对话。
#   - 实际对话
#      (在玩家头顶显示气泡对话框)
#
#----------------------------------------------------------------------------
#  \face[param]
#----------------------------------------------------------------------------
# 【功能】
#    脸图的动态设置
#
# 【注意】
#    本对话框对显示脸图进行了规格扩展:
#      当脸图文件名包含 _数字1x数字2 时(其中为字母x),
#      将定义该脸图文件的规格为 行数(数字1)x列数(数字2)(默认2行x4列)
#    如:ace_actor.png → 该脸图规格为 2行4列,含有8张脸图,与默认一致
#    如:ace_actor_1x1.png → 该脸图规格为 1行1列,含有1张脸图,只有index为0时生效
#    如:ace_actor_1x4.png → 该脸图规格为 1行4列,含有4张脸图
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    i → 【重置】【默认】脸图文件不改变,切换所显示脸图的序号index
#    ls/le → 【重置】定义脸图自动循环播放的开始index/结束index(-1时不启用自动播放)
#    lt → 自动循环播放时,每两帧之间的等待间隔帧数
#    lw → 自动循环播放时,每一次loop结束时的等待帧数(nil代表不再循环)
#
#
# 【高级】
#  - 本对话框的宽高可以动态变化,脸图不影响对话框高度,
#      也因此本对话框允许脸图超出范围,这样方便显示更大规格的脸图(立绘)。
#    但一般大家都使用默认 96x96 规格的脸图,且默认的脸图恰好显示在默认对话框内,
#      此时如果不把脸图包裹住的话比较难看,因此增加该项设置:
#      开启时,当使用默认规格脸图,将强制对话框内容高度大于等于脸图高度
FORCE_WIN_H_BIGGER_THAN_DEFAULT_FACE = true
#
# 【常量设置:参数预设值】
FACE_PARAMS_INIT = {
# \face[]
  :lt => 30, # 循环时,每两帧之间的间隔
  :lw => 60, # 循环后,等待帧数
# \facep[]
  :dir => 0, # 脸图显示方向 1为右侧
  :m => 0, # 脸图镜像显示
  :it => 15, # 脸图移入所需帧数
  :ot => 10, # 脸图移出所需帧数
  :dx => 0, # 脸图x方向的偏移增量
  :dy => 0, # 脸图y方向的偏移增量
  :dw => 8, # 脸图显示宽度的补足量
  :z => 1, # 脸图z值增量
}
#
# 【示例】
#   - 对话编写
#       这是测试对话。\facep[dir1]\face[2]\facem[jump]
#   - 实际对话
#      (脸图显示到右侧,在对话最后切换成第1行第3列的脸图,且跳跃一次)
#
#----------------------------------------------------------------------------
#  \facep[param] 【预先】
#----------------------------------------------------------------------------
# 【功能】
#    脸图的预先设置
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    dir → 【默认】脸图的显示位置(0左侧,1右侧;默认0)
#    m → 是否镜像显示脸图(默认0,不开启镜像)
#    it → 脸图淡入时所用帧数
#    ot → 脸图淡出时所用帧数
#    dx → 脸图在x方向上的偏移增量(默认0)
#    dy → 脸图在y方向上的偏移增量(默认0)
#    dw → 当嵌入对话框内时,脸图宽度的补足增量(默认0)
#    z → 脸图的z值增量(默认1,在对话框上面)
#        (当传入 -1 时将显示在对话框下面,此时脸图不占用对话框宽度)
#
#----------------------------------------------------------------------------
#  \facem[str|param]
#----------------------------------------------------------------------------
# 【功能】
#    脸图的实时动作设置
#
# 【参数】
#    str → 脸图动作的字符串
#    param → 脸图动作的变量参数字符串,可省略
#
# (脸图动作一览)
#    in → 脸图移入(无参数)
#    out → 脸图移出(无参数)
#    jump → 脸图进行一次短暂跳跃(无参数)
#      使用示例: \facem[jump] 脸图小幅度跳跃一次
#    move → 脸图移动到指定位置
#      (变量一览)
#           x/y → 直接指定移动的目的地(以脸图移入后的位置为原点)
#         dx/dy → 指定在当前位置基础上的移动增量
#             t → 移动所需的时间(三次立方平滑)
#     使用示例: \facem[move|dx=50] 脸图朝右侧移动50像素
#
# 【注意】
#   - 脸图动作期间,对话框不会暂停绘制,请自行调用 wait 转义符进行等待。
#   - 若在执行动作时,又呼叫了新的动作,
#      则在当前动作执行完成后,只会继续执行最后传入的一个动作。
#
#----------------------------------------------------------------------------
#  \name[param] 【预先】
#----------------------------------------------------------------------------
# 【功能】
#    姓名框的设置
#
# 【注意】
#  - param 中用 | 分隔 姓名字符串(其中转义符用<>代替[])与 变量参数字符串
#    (若无变量参数的设置,可省略 | 符号)
#  - 若存在多个 \name ,只会取最后一个成功设置的姓名,但变量参数会依次生效覆盖
#
# 【姓名字符串】
#  - 特别的,如果只写了数字,且在数据库中有该ID号角色,则将替换为该角色的名称
#    如: \name[1] → 将在姓名框中显示 艾里克
#    如: \name[999] → 因为数据库中没有该ID号角色,姓名框中依然显示 999
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    o → 姓名框的显示原点(对应九宫格小键盘)(默认7,姓名框左上角为显示原点)
#    do → 基于对话框的九宫格位置,姓名框的显示原点的实际位置
#       (默认7,位于对话框左上角7)(0时为嵌入对话框中,且o自动设为7)
#    dx/dy → 姓名框x、y坐标的额外偏移量
#    opa → 姓名框背景的不透明度(默认255)(文字的不透明度锁定为255)
#    skin → 姓名框所用皮肤的index(默认nil,同win中设置)
#    size → 姓名框文字大小(默认nil,取Font默认值)
#    bg → 姓名框背景图片的index(按常量设置进行 index → 图片名称 映射)
#        (若读取成功,将不显示窗口皮肤;若读取失败,仍绘制窗口皮肤)
#    bgo → 姓名框背景图片与姓名框的对齐原点(对应九宫格小键盘)
#        (默认7,背景图片的左上角与姓名框左上角对齐)
#    cx/cy → 姓名框的文字起始绘制位置(默认为0,0)
#
# 【高级】
#  - 由于姓名框可能遮挡脸图,因此本对话框新增了自动调整,确保姓名框不遮挡脸图
#    可利用 $game_message.no_name_overlap_face = true/false
#      来设置是否开启该功能(默认 true 代表开启)
DEFAULT_NO_OVERLAP_FACE = true
#
# 【常量设置:参数预设值】
NAME_PARAMS_INIT = {
# \name[]
  :o => 1, # 自身的显示原点位置
  :do => 7, # 相较于对话框的显示原点位置
  :dx => 0, # 姓名框整体的偏移增量(如果为嵌入,则会自动增加占位的宽高)
  :dy => 0,
  :opa => 255, # 背景不透明度
  :skin => nil, # 姓名框所用windowskin的类型(nil为与对话框一致)
  :size => nil, # 姓名框文字大小
  :bg => nil, # 姓名框背景所用图片index(覆盖窗口皮肤)
  :bgo => 1,  # 姓名框背景图片与姓名框的对齐原点
  :cx => 0,   # 姓名框内部,绘制姓名时的初始位置
  :cy => 0,
}
#
# 【常量设置:全部姓名前统一增加的字符串】
# (支持draw_text_ex中的转义符)(\c[]转义符支持 TEXT_COLORS 扩展)
# (由于解析问题,字符串中请将 "\" 替换成 "\e" ,并用 <> 代替 [])
ESCAPE_STRING_NAME_PREFIX = "\ec<6>"
#
# 【常量设置:序号映射到姓名框背景图片文件名】
# (如果 bgo 为 7,则图片左上角会与姓名框左上角对齐)
# (其中 序号 必须为整数)
# (图片存储于 Graphics/System 目录下)
INDEX_TO_NAME_BG = { # 姓名框背景
  # 序号数字 => " 背景图片名称"
}
#
# 【示例】
#   - 对话编写
#       \name[主角]这是测试对话。\name[|o1do9]
#   - 实际对话
#      (显示姓名框,其中名称为“主角”,且姓名框左下角位于对话框右上角)
#
#----------------------------------------------------------------------------
#  \pause[param]
#----------------------------------------------------------------------------
# 【功能】
#    等待按键时的帧动画的设置
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    id → 【默认】所用的等待按键帧动画在INDEX_TO_PAUSE中的序号
#    o → 帧动画的显示原点位置类型(九宫格小键盘)(默认左上角7)
#    do → 基于对话框的九宫格位置,自身显示位置的类型(0时显示在文末)(默认2)
#    dx/dy → xy方向上的补足偏移值(默认0)
#    t → 每两帧之间的等待帧数(默认10)
#    v → 是否显示等待按键的帧动画(默认1,显示等待按键动画)
#
# 【常量设置:参数预设值】
PAUSE_PARAMS_INIT = {
# \pause[]
  :id => 0,
  :o => 4,  # 自身的显示原点类型
  :do => 0,  # 相对于对话框的显示位置(九宫格小键盘)(0时为在文末)
  :dx => 0,  # xy偏移值
  :dy => 0,
  :t => 7, # 每两帧之间的等待帧数
  :v => 1,  # 是否显示
}
#
# 【常量设置:序号映射到等待按键的帧动画】
# (其中 index 必须为整数)
# (图片存储于 Graphics/System 目录下)
# (帧动画统一从左上开始设为0号位置,并按行优先从左往右遍历)
INDEX_TO_PAUSE = {
#         文件名 范围(nil则为整张图) 一行中的帧数 一列中的帧数
#index=>[String, Rect, Integer, Integer]
-1 => ["", nil, 1, 1], # 不显示
  0 => ["Window", Rect.new(96,64,32,32), 2, 2], # 默认 使用皮肤窗口里的箭头
}
#
# 【常量设置:是否屏蔽默认的输入等待动画】
# (指默认位于对话框底部中央的4帧动画,推荐屏蔽,只用pause的帧动画)
NO_DEFAULT_PAUSE = true
#
# 【示例】
#   - 对话编写
#       这是测试对话。\pause[0o5do3dx-12dy-12]
#   - 实际对话
#      (等待按键的精灵位于对话框右下角)
#
#----------------------------------------------------------------------------
#  \shake[param]
#----------------------------------------------------------------------------
# 【功能】
#    进行对话框震动(与事件指令中的屏幕震动一致)并等待至结束
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    p → 震动的强度(默认5)
#    s → 震动的速度(默认5)
#    t → 【默认】震动的持续帧数(将补足平滑结束的帧数)(默认40)
#    y → 是否由 x 方向的震动修改为 y 方向的震动(默认0,即false,依然为x方向)
#
# 【示例】
#   - 对话编写
#       这是测试对话,\shake[p7]吓死我了,还以为地震了。
#   - 实际对话
#      (在显示完逗号后,开始对话框震动,并等待结束)
#
#----------------------------------------------------------------------------
#  \wait[t]
#----------------------------------------------------------------------------
# 【功能】
#    直接等待指定帧数
#
# 【参数】
#    t → 等待的帧数
#
# 【注意】
#   - 和默认的等待转义符保持一致,不会被快进、期间不处理按键
#
#----------------------------------------------------------------------------
#  \ins 【预先】
#----------------------------------------------------------------------------
# 【功能】
#    当前对话框不再处理绘制间隔的等待(先完成打开,再一次性绘制全部内容)
#
#----------------------------------------------------------------------------
#  \hold 或 \hold[str] 【结尾】
#----------------------------------------------------------------------------
# 【功能】
#    保留当前对话框,直至没有该指令的对话框关闭,关闭所有保留的对话框
#
# 【参数】
#    str →【可选】任意字符串,代表该保留对话框的唯一ID
#           如果设置,则对话框打开时,会关闭已保留对话框中全部是该ID的对话框
#           如果不设置,则会始终保留,直到没有\hold指令的对话框关闭
#
# 【示例】
#    显示文本:这是测试对话。\hold[1]
#    显示文本:这是第二句测试对话。\hold[2]
#    显示文本:这是第三句测试对话。\hold[1]
#
#    → 第一句对话保留,第二句对话和第一句对话会一起保留,
#       第三句对话前首先关闭第一句对话,再和第二句对话一起保留
#
#----------------------------------------------------------------------------
#  \close 【结尾】
#----------------------------------------------------------------------------
# 【功能】
#    当前对话框不再受 settings_changed? 影响,必定进行关闭处理
#
#----------------------------------------------------------------------------
#  \next 【结尾】
#----------------------------------------------------------------------------
# 【功能】
#    当前对话框在按键继续后不关闭,而是保留显示,
#    在处理下一条事件指令-显示文本时,文本将继续显示在对话框的新行。
#    (中途可以调用其它事件指令,对话框将一直保持开启状态。)
#
#----------------------------------------------------------------------------
#  \clc
#----------------------------------------------------------------------------
# 【功能】
#    将当前全部已经绘制的文字上移,直至移出对话框的显示范围,
#    然后在新的一行中继续绘制剩余文字。
#    (在输入等待时,依然可以通过方向键进行滚动,以浏览之前移出显示范围的文字)
#    (最好先固定对话框的显示高度,再使用本功能,否则下侧可能出现较多的空白区域,
#       因为在动态高度情况下,对话框高度由全部文字的高度和决定,与这些移动无关)
#
# 【常量设置:文字移出的所用帧数】
CLC_CHARAS_OUT_FRAME = 20

#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# ○ 文字特效类
#     以下 param 传入 任意非0非空字符(如 1)代表以预设值开启特效
#                只传入 0 代表关闭该特效
#     除非标注【叠加】,否则多特效同时执行可能会造成奇怪效果
#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# 【常量设置:初始激活的文字特效转义符及其变量参数】
CHARA_PARAMS_INIT = {
# 文字特效类转义符sym => 变量参数字符串"code_string"
  #:cin => "1",
}
#----------------------------------------------------------------------------
#  \cin[param] 【独占】(移入完成后才进行其余特效的更新)
#----------------------------------------------------------------------------
# 【功能】
#    开启文字移入的特效
#
# 【注意】
#    移入使用缓动函数,具体可以见 MESSAGE_EX.ease_value 方法
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    r → 是否将以下全部变量的值变更为正负范围内的随机数?
#         比如传入的 vx5,则实际的 vx 为 -5 ~ 5 中随机一个值
#    t → 移入所用帧数(即移动到最终位置所用帧数)(不透明度从0平滑增加到255)
#    vzt/vz → 每zvt(最小值1)帧zoom放缩值增加vz的值(整数)
#    va → 每帧内angle的增值
#    dx/dy → 移入前所在位置的偏移量
#  (以下参数不受 r 的影响)
#    do → 移入前所在位置
#        (大于0时为对话框的九宫格位置,小于0时为屏幕九宫格位置,0时最终显示位置)
#          如:do5dx0dy0 就是文字从对话框中心位置移入到最终显示位置
#    vo → 每帧不透明度的增量(默认0,将设置为 255/t)
#    rxt/rx → 每rxt(最小值1)帧src_rect的x增加rx像素的值(默认0)
#    ryt/ry → 每ryt(最小值1)帧src_rect的y增加ry像素的值(默认0)
#
# 【常量设置:参数预设值】
CIN_PARAMS_INIT = {
# \cin[]
  :r => 0, # 随机取值?
  :t => 15, # 移入所用帧数
  :do => 0, # 移入前所在位置
  :dx => 0, # 移入前所在位置的偏移量
  :dy => 0,
  :vz => 0,  # 每vzt帧zoom的增量
  :vzt => 1,
  :va => 0,  # 每帧角度增量
  :vo => 0,  # 每帧不透明度增量
  :rxt => 1,
  :rx => 0, # 每rxt帧src_rect.x的增量
  :ryt => 1,
  :ry => 0, # 每ryt帧src_rect.y的增值
}
#
# 【示例】
#   - 对话编写
#       \cin[dy20]这是测试对话,看看移入效果如何。
#   - 实际对话
#      (文字移入效果修改为由下方20像素处往目的地移入)
#
#----------------------------------------------------------------------------
#  \cout[param] 【独占】(移出时关闭其余特效更新)
#----------------------------------------------------------------------------
# 【功能】
#    开启文字移出的特效
#
# 【注意】
#    移出使用缓动函数,具体可以见 MESSAGE_EX.ease_value 方法
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    r → 是否将以下全部变量的值变更为正负范围内的随机数?
#         比如传入的 vx5,则实际的 vx 为 -5 ~ 5 中随机一个值
#    t → 移出所用帧数(即移动到最终位置所用帧数)(不透明度从255平滑减小到0)
#    vzt/vz → 每zvt(最小值1)帧zoom放缩值增加vz的值(整数)
#    va → 每帧内angle的增值
#    dx/dy → 移出后所在位置的偏移量
#  (以下参数不受 r 的影响)
#    do → 移出后所在位置
#        (大于0时为对话框的九宫格位置,小于0时为屏幕九宫格位置,0时当前位置)
#          如:do5dx0dy0 就是文字移出到对话框中心位置
#    vo → 每帧不透明度的减量(默认为 255/t)
#    rxt/rx → 每rxt(最小值1)帧src_rect的x增加rx像素的值(默认0)
#    ryt/ry → 每ryt(最小值1)帧src_rect的y增加ry像素的值(默认0)
#
# 【常量设置:参数预设值】
COUT_PARAMS_INIT = {
# \cout[]
  :r => 0, # 随机取值?
  :t => 15, # 移出所用帧数
  :do => 0, # 移出后所在位置
  :dx => 0, # 移出后所在位置的偏移量
  :dy => 0,
  :vz => 0,  # 每vzt帧zoom的增量
  :vzt => 1,
  :va => 0,  # 每帧角度增量
  :vo => 0,  # 每帧不透明度减量
  :rxt => 1, # 每rxt帧src_rect.x的增量
  :rx => 0,
  :ryt => 1, # 每ryt帧src_rect.y的增值
  :ry => 0,
}
#
# 【示例】
#   - 对话编写
#       \cout[do5]这是测试对话,看看移出效果如何。
#   - 实际对话
#      (文字移出效果修改为移到对话框中心处渐隐)
#
#----------------------------------------------------------------------------
#  \uout[param] 【独占】
#----------------------------------------------------------------------------
# 【功能】
#    【需要前置Unravel_Bitmap插件】利用消散移出文字
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    n → 消散的粒子总数(大约)
#    d → 单个粒子的大小(直径/边长)
#    o → 单个粒子消失时的透明度变更最小值
#    dir → 整体消散方向类型(同九宫格小键盘)(1379-四角;5-四方向;46-左右向上)
#    s → 粒子形状类型(0-正方向;1-圆形;2-三角形)
#
# 【常量设置:参数预设值】
UOUT_PARAMS_INIT = {
# \uout[]
  :n => 200, # 消散的粒子总数
  :d =>  2, # 消散的粒子的大小(直径/边长)
  :o =>  1, # 透明度变更量的最小值
  :dir => 4, # 消散方向类型
  :s =>  0, # 粒子的形状类型
}
#
# 【常量设置:类型序号映射到消散方向类型】
CU_PARAM_DIR = { # 定义消散方向类型的id
  1 => :LD, 3 => :RD, 4 => :LR, 5 => :LRUD, 6 => :LR, 7 => :LU, 9 => :RU
}
#
# 【常量设置:类型序号映射到粒子形状类型】
CU_PARAM_S = { # 定义粒子形状类型的id
  0 => :S, # 正方形
  1 => :C, # 圆形(耗时)
  2 => :T  # 三角形
}
#
# 【示例】
#   - 对话编写
#       \uout[1]这是测试对话,看看移出效果如何。
#   - 实际对话
#      (文字移出效果修改为消散)
#
#----------------------------------------------------------------------------
#  \csin[param]
#----------------------------------------------------------------------------
# 【功能】
#    开启正弦波浪扭曲特效
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    a → 正弦波浪的幅度(像素数)
#    l → 正弦波浪的频度(像素数)
#    s → 正弦波浪的动画速度(默认360)
#    p → 正弦波浪的相位角度(最大360°)(一般不需要设置)
#
# 【常量设置:参数预设值】
CSIN_PARAMS_INIT = {
  :a => 6, # 幅度
  :l => 10, # 频度
  :s => 30, # 速度
  :p => 0, # 相位
}
#
#----------------------------------------------------------------------------
#  \cwave[param]
#----------------------------------------------------------------------------
# 【功能】
#    开启文字上下浮动特效
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    h → 上下浮动的最大偏移像素值
#    t → 每隔t帧进行一次1像素的偏移
#    vy → 起始时的y方向移动速度(正数为向下)
#
# 【常量设置:参数预设值】
CWAVE_PARAMS_INIT = {
# \cwave[]
  :h  => 2,  # Y方向上的最大偏移值
  :t  => 4,  # 移动一像素所耗帧数
  :vy => -1, # Y方向速度(正数向下)
}
#
#----------------------------------------------------------------------------
#  \cswing[param]
#----------------------------------------------------------------------------
# 【功能】
#    开启左右摇摆特效(本质为精灵旋转)
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    d → 每次更新增加的角度值
#        (d为0时,角度值不缓慢增加,而是直接到最大值,但初始方向随机)
#    t → 每次角度更新后等待t帧
#    t2 → 每次角度到达最大值后等待t2帧
#    a → 角度可到达的最大值(左右对称)
#    o → 摇摆不动点所在位置类型(键盘九宫格)
#
# 【常量设置:参数预设值】
CSWING_PARAMS_INIT = {
# \cswing[]
  :d => 0, # 每次更新增加的角度值
  :t => 1, # 每次角度更新后等待t帧
  :t2 => 15, # 每次角度到达最大值后等待t2帧
  :a => 5, # 角度可到达的最大值(左右对称)
  :o => 8, # 摇摆不动点所在位置类型(键盘九宫格)(2为底部中心)
}
#
#----------------------------------------------------------------------------
#  \czoom[param]
#----------------------------------------------------------------------------
# 【功能】
#    开启文字缩放特效(本质为精灵缩放)
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    t → 在进行一次缩放变更后的等待帧数
#    dx → x方向的每次缩放增量(单位%,即 1 代表每t帧放大1%,zoom_x增加0.01)
#    dy → y方向的每次缩放增量(单位%,即 1 代表每t帧放大1%,zoom_y增加0.01)
#    o → 缩放不动点所在位置类型(键盘九宫格)
#    min → x和y方向上缩放总量的最小值
#          (在RGSS3中,负数的缩放量不会显示,推荐设置为自然数)
#          (在RGD中,负数的缩放量将反向显示,推荐设置为最大值的相反数)
#    max → x和y方向上缩放总量的最大值
#
# 【常量设置:参数预设值】
CZOOM_PARAMS_INIT = {
# \czoom[]
  :t => 0, # 在进行一次缩放变更后的等待帧数
  :dx => 2, # x方向的每次缩放增量(缩放总量在-100~100之间,整数)
  :dy => 0, # y方向的每次缩放增量
  :o => 5, # 缩放不动点所在位置类型(键盘九宫格)(5为中心)
  :min => 0, # xy缩放总量的最小值
  :max => 100, # xy缩放总量的最大值
}
#
#----------------------------------------------------------------------------
#  \cshake[param]
#----------------------------------------------------------------------------
# 【功能】
#    开启抖动特效
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    l/r/u/d → 设置 左右上下 四个方向的最大移动偏移值
#    vx/vy → 设置x、y方向上的初始移动速度(正数为向右、向下)(0为随机方向)
#    vxt/vyt → 设置x、y方向上移动一次后的等待帧数
#
# 【常量设置:参数预设值】
CSHAKE_PARAMS_INIT = {
# \cshake[]
  :l => 0,  # 距离所在原点的最大偏移量(左右上下)
  :r => 1,
  :u => 1,
  :d => 1,
  :vx  => 0,  # x的初始移动方向(0为随机方向)
  :vxt => 2,  # x方向移动一像素所耗帧数
  :vy  => 0,  # y的初始移动方向(0为随机方向)
  :vyt => 1,  # y方向移动一像素所耗帧数
}
#
#----------------------------------------------------------------------------
#  \cshake2[param]
#----------------------------------------------------------------------------
# 【功能】
#    开启抖动特效(频率更大的震动)
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    dx → 往左或往右的最大震动距离
#    dy → 往上或往下的最大震动距离
#    l → 震动幅度
#
# 【常量设置:参数预设值】
CSHAKE2_PARAMS_INIT = {
# \cshake[]
  :dx => 4,
  :dy => 4,
  :l  => 3,
}
#
#----------------------------------------------------------------------------
#  \cflash[param] 【叠加】
#----------------------------------------------------------------------------
# 【功能】
#    开启文字闪烁特效(本质为精灵flash)
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    r/g/b/a → 闪烁的颜色(红、绿、蓝、不透明度)(默认均255)
#            (如:r50g50b100a200)
#    d → 闪烁从开始到完成需要的帧数
#    t → 闪烁完成后的等待帧数
#
# 【常量设置:参数预设值】
CFLASH_PARAMS_INIT = {
# \cflash[]
  :r => 255, # 闪烁颜色RGBA
  :g => 255,
  :b => 255,
  :a => 255,
  :d => 60,  # 闪烁帧数
  :t => 60,  # 闪烁后的等待时间
}
#
#----------------------------------------------------------------------------
#  \cmirror[param] 【叠加】
#----------------------------------------------------------------------------
# 【功能】
#    开启横轴镜像绘制(本质为精灵mirror)
#
# 【参数】
#    param → 变量参数字符串(无任何设置参数)
#
# 【常量设置:参数预设值】
CMIRROR_PARAMS_INIT = {}
#
# 【示例】
#   - 对话编写
#       这是\cmirror[1]测试对话\cmirror[0],看看效果如何。
#   - 实际对话
#      (“测试对话”四个字将开启左右翻转)
#
#----------------------------------------------------------------------------
#  \cu[param] 【叠加】
#----------------------------------------------------------------------------
# 【功能】
#    【需要前置Unravel_Bitmap插件】开启字符消散特效
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    t → 每两次执行消散之间的间隔帧数(正整数)
#    (其余同 \uout 转义符)
#
# 【常量设置:参数预设值】
CU_PARAMS_INIT = {
# \cu[]
  :t => 10, # 每两次消散之间的时间间隔
  :n => 20, # 消散的粒子总数
  :d =>  2, # 消散的粒子的大小(直径/边长)
  :o =>  1, # 透明度变更量的最小值
  :dir => 4, # 消散方向类型
  :s =>  0, # 粒子的形状类型
}
#
#----------------------------------------------------------------------------
#  \ctog[param] 【叠加】
#----------------------------------------------------------------------------
# 【功能】
#    开启文字切换特效(本质为切换精灵的bitmap)
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    i → 使用 i 号对应的文字组(具体见 CTOG_CHARAS 常量,设置 i → 文字组)
#    n → 从文字组中挑选出 n 个字符作为切换文字(若为0,则取全部)
#    t → 文字切换一次后的等待帧数
#    r → 是否启用随机切换
#
# 【常量设置:参数预设值】
CTOG_PARAMS_INIT = {
# \ctog[]
  :i => 0,  # 选取i号文字组
  :n => 0,  # 从文字组中选取n个字符
  :t => 10,  # 文字切换的等待帧数
  :r => 1,  # 是否随机选择下一个文字?
}
#
# 【常量设置:序号映射到文字切换组】
CTOG_CHARAS = {
  # 定义文字切换特效的文字组(若为数字,则取 IconSet 中的图标)
  0 => ['▀', '▄', '█', '▌', '✖'],
  1 => ['0','1','2','3','4','5','6','7','8','9'],
  2 => [376,377,378,379,380,381,382,383],
}
#
#----------------------------------------------------------------------------
#  \cneon[param] 【叠加】
#----------------------------------------------------------------------------
# 【功能】
#    开启文字渐变更新特效(本质为精灵color变换)
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    t → 颜色之间的渐变帧数
#    c → 【可重复填写】指定渐变颜色的索引号
#        从 当前颜色开始(默认0号颜色),按照传入顺序依次变化
#
# 【常量设置:参数预设值】
CNEON_PARAMS_INIT = {
# \cneon[]
  :t => 60, # 颜色之间的切换帧数
}
#
# 【示例】
#   - 对话编写
#       这是\cneon[t60c1c10c17]测试对话\cneon[0]。
#   - 实际对话
#      (“测试对话”四个字将按照 0→1→10→17→1→10→17... 的颜色索引进行循环变色)
#
#----------------------------------------------------------------------------
#  \cmc[param]  【叠加】
#----------------------------------------------------------------------------
# 【功能】
#    叠加绘制文字
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    i → 使用 i 号对应的文字组(具体见 CMC_CHARAS 常量)
#    n → 从文字组中挑选出 n 个字符作为切换文字(若为0,则取全部)
#    c → 指定叠加绘制的文字的颜色索引号(-1时与原始文字颜色一致)
#
# 【常量设置:参数预设值】
CMC_PARAMS_INIT = {
# \cmc[]
  :i => 0,  # 选取i号文字组
  :n => 0,  # 从文字组中选取n个字符(若为0,则取全部)
  :c => 10,  # 叠加绘制的文字的颜色索引号
}
#
# 【常量设置:序号映射到叠加绘制文字组】
CMC_CHARAS = {
  # 定义文字叠加绘制的文字组(若为数字,则取 IconSet 中的图标)
  0 => ['✖'],
  1 => [4],
}
#
#----------------------------------------------------------------------------
#  \cjump[param]
#----------------------------------------------------------------------------
# 【功能】
#    开启文字跳跃特效
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    tc → 跳跃前的等待帧数
#    t → 跳跃的持续帧数
#    h → 跳跃的最大高度
#    w → 下一次跳跃前的等待帧数(nil时不再跳跃)
#
# 【常量设置:参数预设值】
CJUMP_PARAMS_INIT = {
# \cjump[]
  :tc => 0,  # 跳跃前的等待帧数
  :t => 20,  # 跳跃的持续帧数
  :h => 4,  # 跳跃的最大高度
  :w => 60,   # 下一次跳跃前的等待帧数(nil时不再跳跃)
}
#
#----------------------------------------------------------------------------
#  \cfk[param]
#----------------------------------------------------------------------------
# 【功能】
#    开启文字明灭闪烁特效
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    t → 明灭的持续帧数(不透明度从255到0的所用时间)
#       (0时直接显隐,负数时代表 0~数字 之间的随机数)
#    w → 下一次明灭前的等待帧数(nil时不再明灭)
#
# 【常量设置:参数预设值】
CFK_PARAMS_INIT = {
# \cfk[]
  :t => 30,
  :w => 60,  
}
#
# 【常量设置:当t为0时,完全隐藏后、显示前的等待帧数】
CFK_T0_WAIT = 20

#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# ○ 变量环境
#    此处放置环境处理,效仿python的conda,将对话框的设置状态进行打包存储与读取
#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# 【关于环境】
#    由于对话框可设置的变量众多,
#    当需要不同情况使用不同样式的对话框时,不得不编写一大串的转义符进行设置;
#    而转义符的设置在默认情况下是跨对话框的,想要改回之前的又要写一大串,非常不便。
#    因此引入了该类别的转义符,以简化不同设置间切换所需的操作。
#
#    环境本质上是 Game_Message 类实例的拷贝。
#
#----------------------------------------------------------------------------
#  \env[sym] 【预先】
#----------------------------------------------------------------------------
# 【功能】
#    读取名称为 sym 的环境,并立即应用于当前与之后的对话框
#    当前环境被设置为 sym
#
# 【注意】
#    - 默认激活的环境为 0,因此可以用 \env[0] 切回最原始的环境
#    - 环境的命名没有限制,但最好为字母和数字的组合
#    - 当不存在 sym 环境时,将会把当前对话框执行前的环境存储为 sym
#
# 【示例】
#   - 对话编写
#       \env[测试]这是测试用对话。
#   - 实际对话
#      (进入 "测试" 对应的环境,之后的转义符修改都将只修改 "测试" 环境中的设置)
#      (若不存在 "测试" 环境,则将当前的环境存储为 "测试")
#
# 【常量设置:环境预设】
# (此处环境的初始状态为脚本中预设的全部值,之后再依据此处设置进行覆盖)
# (出于便捷考虑,读档后此处设置的环境均会被重置,即不保存游戏中途对这些环境的修改)
# (因此,此处设置的环境请不要与游戏中途的可保存的环境发生名称冲突)
# (和文本预设一致,此处转义符需要将 \ 替换成 \\ )
DEFAULT_ENVS = {
  # 环境名称 => "字符串"
  "0" => "", # 默认初始环境,全部为预设值,推荐不要修改
  "底部" => "\\win[o2do-2dy-30w200h3]", # 对话框居下显示
}
#
# 【常量设置:设置当对话框关闭时,是否把环境重置为默认的0】
# (若设置为 true,则每次对话框关闭时,环境重置为 0)
# (若设置为 false ,则不会自动重置环境,请手动写 \env[0])
# (若设置为 数字,则该序号的开关 $game_switches[id] 开启时,才自动重置环境 )
S_ID_RESET_ENV = true
#
# (对话框关闭的情形:
#     1.下一个事件指令不为显示文本
#     2.下一个事件指令为显示文本,但对话框的背景与当前的不同
#     3.当前对话中有\close转义符)
#----------------------------------------------------------------------------
#  \env[sym|save] 【结尾】
#----------------------------------------------------------------------------
# 【功能】
#    将当前对话框绘制完成时的环境,保存为 sym
#    同时当前环境设置为 sym
#
# 【注意】
#    - 在每一次对话框开启前,以及env生效并切换环境后,都会更新一次当前环境
#
# 【示例】
#   - 对话编写
#       \env[底部对话|save]\win[o2do-2dy-30]这是测试用对话。
#   - 实际对话
#      (将当前对话框的环境存为 "底部对话" ,若环境已经存在则覆盖,
#        之后进入 "底部对话" 的环境)
#
#----------------------------------------------------------------------------
#  \temp 【结尾】
#----------------------------------------------------------------------------
# 【功能】
#    当前对话框结束时,将环境重置回对话框开启前的环境
#
# 【注意】
#   - 在当前对话框结束前,转义符参数的修改仍然生效
#     因此可能导致中途生成的 并行对话 等,继承了当前修改后的参数
#
#   - 当同时存在 \temp、\env[sym2]、\env[sym3|save] 时,执行顺序如下(当前环境为 sym1):
#     1、在绘制开始前,存储了一次 sym1 环境
#     2、执行预先转义符:temp记录下最初环境为 sym1;env将当前激活环境修改为sym2并应用
#     3、对话框关闭后:当前对话框修改后的环境存入 sym3;
#                   temp生效,将环境重置为 sym1,继续下一个对话
#

#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# ○ 高级
#    此处放置高级处理,需要结合一定的脚本知识才能灵活运用
#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#----------------------------------------------------------------------------
#  \func[param] 【预先】
#----------------------------------------------------------------------------
# 【功能】
#    设置一些进阶的功能,将在对话框打开前生效
#
# 【参数】
#    param → 变量参数字符串
# (变量一览)
#    open → 使用 i 号对应的对话框打开方式
#    close → 使用 i 号对应的对话框关闭方式
#         0 为默认的上下打开关闭,1 为淡入淡出,2 为动态缩放展开,3 为动态滑入滑出
#
#    aw → 是否开启对话框的自动换行(auto wrap)
#        若开启,当文字绘制到对话框边界padding处时,将进行自动换行
#        只是非常基础的按单个文字切割的自动换行方式,会切割单词,不考虑标点符号
#
#    para → 是否并行处理子窗口
#        若开启,则对话框的下一条指令为 选择支/数字输入/物品选择 时,
#        会在对话框打开、开始绘制文字时,同步打开并激活选择框/数字输入框/物品选择
#
#        当子窗口的按键处理结束并关闭时,对话框将同步强制关闭
#       (警告:对话框中未被绘制的转义符可能不会生效!)
#       (注意:子窗口的嵌入对话框会失效!)
#
# 【常量设置:参数预设值】
FUNC_PARAMS_INIT = {
# \func[]
  :open => 2,   # 打开方式
  :close => 2,  # 关闭方式
  :aw => 1,  # 自动换行
  :para => 0, # 并行处理子窗口
}
#
# 【示例】
#   - 对话编写
#       这是测试用对话。\func[para=1]
#   - 实际对话
#      (若下条指令为选择框,将并行打开)
#
#----------------------------------------------------------------------------
#  \eval{string} 或 {{string}}
#----------------------------------------------------------------------------
# 【功能】
#    当绘制到该转义符时,执行 eval(string),并丢弃返回值
#
# 【参数】
#    string → Ruby语句的字符串,可以用 ; 进行分割多句
#
# 【注意】
#   - 该转义符使用花括号,同时 string 中不可以出现花括号
#
#   - 可用 s 代替 $game_switches ,用 v 代替 $game_variables
#     可用 msg 代表当前对话(Window_Message类的实例)
#      如 \eval{s[1]=true} 或 {{s[1]=true}}
#       就是当文字绘制到该转义符时,打开1号开关,并继续绘制
#
#   - 由于文本替换类的转义符优先级最高,实际绘制的内容为对话框打开时的状态
#      即如果存在 \eval{v[1]=5}\v[1],其中1号变量初值为0,
#      尽管绘制时其值变为了5,但实际绘制的\v[1]仍然为对话框打开时的值0
#
#----------------------------------------------------------------------------
#  \set[sym]
#----------------------------------------------------------------------------
# 【功能】
#    打开标识符为 sym 字符串的分组(初始时为空分组),
#    之后绘制的文字均会被存入该分组中
#
# 【注意】
#   - 单个文字可以被存入多个分组中
#   - 约定 0 分组为全部文字所在分组,因此可以传入 0 来回到无分组状态
#
# 【参数】
#    sym → 分组的唯一标识符(字符串)
#
# 【高级】
#    Window_EagleMessage 类新增方法 chara_set(sym) { |s| do_something }
#     该方法能够用于对 sym 分组中的文字精灵s进行逐个操作,
#     若 不传入sym 或 传入 0 或 传入 '0' 或传入 "",则转为对全部文字精灵进行操作
#
# 【示例】
#   - 对话编写
#       \set[1]这是测试用\set[0]对话。
#   - 实际对话
#      (“这是测试用”被存入标识符为 "1" 的分组,全部文字被存入标识符为 "0" 的分组)
#
#----------------------------------------------------------------------------
#  \setm[sym|effect|param]
#----------------------------------------------------------------------------
# 【功能】
#    对sym分组中的文字精灵,执行effect特效,并传入param变量参数字符串作为特效的参数
#
# 【参数】
#    sym → 分组的唯一标识符,同 \set 中的注释
#    effect → 文字特效类转义符中的转义符,比如 ctog
#    param → 对应文字特效的变量参数字符串,与转义符自己的帮助保持一致
#            若只传入 0,则为关闭该特效
#
# 【示例】
#   - 对话编写
#       \ctog[1]这是测试用\ctog[0]对话。\wait[60]\setm[0|ctog|0]
#   - 实际对话
#      (文本显示完的1s后,关闭全部文字精灵的ctog特效)
#      (“这是测试用” 的 ctog 特效在对话结尾处被关闭)
#
# 【示例】
#   - 对话编写
#       \set[1]\ctog[1]测试用文字\set[0],\!第二句话。\setm[1|ctog|0]
#   - 实际对话
#      (在对话结尾时,仅关闭 1 分组中 “测试用文字” 的 ctog 特效)
#

#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# ○ 扩展类
#     此处放置整合其他插件效果的转义符,统一放置于 $game_message.ex_params 中
#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# 【常量设置:定义初始激活的扩展类转义符的预设变量参数字符串】
EX_PARAMS_INIT = {
# 渐变绘制转义符 \cg => "颜色索引字符串",
  :cg => "",
}
#----------------------------------------------------------------------------
#  \cg[c1..]
#----------------------------------------------------------------------------
# 【功能】
#    【需要前置Sion_GradientText插件】之后的文本开启纵轴渐变描绘
#
# 【参数】
#    按序排列的 c + Windowskin颜色索引号,可以重复填写
#    不传入任何参数时,代表关闭渐变绘制
#
# 【示例】
#   - 对话编写
#       \cg[c1c2c1]测试用\cg[]文字。
#   - 实际对话
#      (用 1号 2号 1号颜色由上至下渐变绘制“测试用”三个字)
#

#=============================================================================
# ● 其它设置
#=============================================================================
#----------------------------------------------------------------------------
# ○ 新游戏开始时,对话框预定将要显示的文本
# (由于解析问题,请将 "\" 替换成 "\\")
ESCAPE_STRING_INIT = ""

#--------------------------------------------------------------------------
# ○ 切换显示/隐藏
# (每帧判定,返回 true 时,会切换对话框的显示/隐藏)
# (文字将执行其移入移出效果)
def self.toggle_visible?
  false #Input.trigger?(:A)
end

#----------------------------------------------------------------------------
# ○ 内容滚动
#
# - 当对话框的宽度或高度被固定时,
#     若所绘制的文字超出对话框范围,将自动开启内容滚动效果
#
# - 当进入 等待按键 状态时,按住 方向键 即可朝对应方向滚动浏览对话框全部内容

#----------------------------------------------------------------------------
# ○ 当文字绘制超出对话框区域,导致内容需要滚动时,新增了一个缓动动画来获得空行
#  该项设置缓动动画的持续帧数
CHARAS_SCROLL_OUT_FRAME = 12

#----------------------------------------------------------------------------
# ○ 当对话框内容无需滚动浏览时,若该常量设置为 true,则使用方向键也可以继续对话
# 如果设置为 false,则无改变(仍然需要按确定键或取消键继续对话)
INPUT_NEXT_WITH_DIR4 = false

#----------------------------------------------------------------------------
# ○ 预定绘制文本
#
# - 利用该脚本存储文本,在下次打开对话框时,全部存储文本将按序插入到对话开头
#
#        $game_message.add_escape(param_string)
#
#   param_string 解析: 【String】型常量
#
# - 示例:
#     $game_message.add_escape("\\win[ali1]")
#        → 之后的对话框中 文本居中 绘制
#     $game_message.add_escape("\\pop[0]")
#        → 下一次的对话框为pop类型,绑定在当前执行对话框的事件上
#
# - 注意:
#   · 由于解析问题,在 param_string 中请将 "\" 替换成 "\\"
#   · 当存在多条预定文本,将按照预定时间的先后顺序,依次放入下一次对话框的开头
#   · 预定的字符串只会被放入对话框一次
#   · 不会去除 param_string 中的非转义符文本
#   · 为了确保win、pop等转义符能够顺利更改对话框位置等属性,
#     位于开头的转义符将会在对话框打开前执行完毕
#

#=============================================================================
# ● 特别感谢
#=============================================================================
# - 葱兔(http://onira.lofter.com/)

#=============================================================================
# - 请不要轻易修改以下内容!
#=============================================================================
end
module MESSAGE_EX
  #--------------------------------------------------------------------------
  # ● 【计算】获取缓动函数的返回值
  #  type 为该函数被调用的场景
  #  x 为缓动函数的 当前时间/总时间 的比值(0~1之间小数)
  #  若使用了【组件-缓动函数 by老鹰】,则可以调用其中的缓动函数
  #--------------------------------------------------------------------------
  def self.ease_value(type, x)
    if $imported["EAGLE-EasingFunction"]
      case type
      when :msg_open  # 对话框打开 - 动态缩放展开
        return EasingFuction.call("easeOutBack", x)
      when :msg_close # 对话框关闭 - 动态缩放闭合
        return EasingFuction.call("easeInBack", x)
      when :msg_xywh  # 对话框通常情况下变更位置和大小
        return EasingFuction.call("easeOutQuart", x)
      when :msg_vp    # 视图ox和oy变动时(文字绘制在可视范围外,或clc清屏)
        return EasingFuction.call("easeOutQuart", x)
      when :face_xy   # 脸图移动处理
        return EasingFuction.call("easeOutElastic", x)
      when :chara_xy  # 文字移动处理
        return EasingFuction.call("easeOutExpo", x)
      when :msg_open_slide # 对话框打开 - 滑入
        return EasingFuction.call("easeOutBack", x)
      when :msg_close_slide # 对话框关闭 - 滑出
        return EasingFuction.call("easeInBack", x)
      end
    end
    return 1 - 2**(-10 * x)
  end
  #--------------------------------------------------------------------------
  # ● 获取指定转义符的基础设置
  #--------------------------------------------------------------------------
  def self.get_default_params(param_sym)
    MESSAGE_EX.const_get("#{param_sym.to_s.upcase}_PARAMS_INIT".to_sym) rescue {}
  end
  #--------------------------------------------------------------------------
  # ● 读取指定文字组
  #--------------------------------------------------------------------------
  def self.get_charas_array(sym, index, num)
    h = MESSAGE_EX.const_get("#{sym.to_s.upcase}_CHARAS".to_sym) rescue {}
    array = h[index]
    return [] if array.nil?
    return array if num == 0
    return array.sample(num)
  end
  #--------------------------------------------------------------------------
  # ● 获取\conv[string]的替换字符串
  #--------------------------------------------------------------------------
  def self.get_conv(s)
    CONVERT_ESCAPE || ""
  end
  #--------------------------------------------------------------------------
  # ● 获取姓名框绘制内容的前缀
  #--------------------------------------------------------------------------
  def self.get_name_prefix
    ESCAPE_STRING_NAME_PREFIX
  end
  #--------------------------------------------------------------------------
  # ● 读取对应的 windowskin 位图
  #--------------------------------------------------------------------------
  def self.windowskin(index)
    begin
      return Cache.system(INDEX_TO_WINDOWSKIN[index])
    rescue
      return Cache.system(INDEX_TO_WINDOWSKIN[0])
    end
  end
  #--------------------------------------------------------------------------
  # ● 读取对应的 bg 位图
  #--------------------------------------------------------------------------
  def self.windowbg(index, w = nil, h = nil)
    Cache.system(INDEX_TO_WINDOW_BG[index]) rescue nil
  end
  def self.namebg(index, w = nil, h = nil)
    Cache.system(INDEX_TO_NAME_BG[index]) rescue nil
  end
  #--------------------------------------------------------------------------
  # ● 播放对应的SE
  #--------------------------------------------------------------------------
  def self.se(index)
    params = INDEX_TO_SE[index] || INDEX_TO_SE[0]
    return if params[0] == ""
    volume = params[1] || INDEX_TO_SE[0][1]
    pitch = params[2] || INDEX_TO_SE[0][2]
    Audio.se_play("Audio/SE/" + params[0], volume, pitch)
  end
  #--------------------------------------------------------------------------
  # ● 读取对应的 tag 位图
  #--------------------------------------------------------------------------
  def self.windowtag(index)
    begin
      return Cache.system(INDEX_TO_WINDOWTAG[index])
    rescue
      return Cache.empty_bitmap
    end
  end
  #--------------------------------------------------------------------------
  # ● 重设tag
  #  i => 具体在tag位图上的帧序号,九宫格顺序排列
  #  o => tag位图的显示原点,理论上为pop对话框的do
  #  _do => tag位图的显示位置原点,理论上为pop对话框的o
  #--------------------------------------------------------------------------
  def self.set_windowtag(window, sprite, i, o, _do)
    w = sprite.src_rect.width; h = sprite.src_rect.height # 单个tag的宽度和高度
    sprite.src_rect.x = w * (2 - (9 - i) % 3)
    sprite.src_rect.y = h * ((9 - i) / 3)
    self.reset_xy_dorigin(sprite, window, _do)
    self.reset_xy_origin(sprite, o)
  end
  #--------------------------------------------------------------------------
  # ● 读取 pause 按键等待精灵的信息组
  #--------------------------------------------------------------------------
  def self.pause_params(index)
    INDEX_TO_PAUSE[index] || INDEX_TO_PAUSE[0]
  end
  #--------------------------------------------------------------------------
  # ● 读取绘制图片时的图片名称
  #--------------------------------------------------------------------------
  def self.get_pic_file(filename)
    filename
  end
#==============================================================================
# ○ 共享方法
#==============================================================================
  #--------------------------------------------------------------------------
  # ● 判定当前场景
  #--------------------------------------------------------------------------
  def self.in_scene?(s)
    return SceneManager.scene_is?(Scene_Map) if s == :map  # 地图场景中?
    return SceneManager.scene_is?(Scene_Battle) if s == :battle # 战斗场景中?
    return false
  end
  #--------------------------------------------------------------------------
  # ● 获取指定对象的信息文本
  #--------------------------------------------------------------------------
  def self.get_data_info(type, id, n ='0')
    case type
    when 's'; obj = $data_skills[id]
    when 'i'; obj = $data_items[id]
    when 'w'; obj = $data_weapons[id]
    when 'a'; obj = $data_armors[id]
    end
    if obj
      case n
      when 0, '0'; return "\ei[#{obj.icon_index}]#{obj.name}"
      when 1, '1'; return "\ei[#{obj.icon_index}]"
      when 2, '2'; return "#{obj.name}"
      end
    end
    return ""
  end
  #--------------------------------------------------------------------------
  # ● 解析字符串参数
  #--------------------------------------------------------------------------
  def self.parse_param(param_hash, param_text, default_type = "default")
    param_text = param_text.downcase rescue ""
    # 只有首位是省略名字的参数设置
    param_text.slice!(/ */)
    t = param_text.slice!(/^\-?\d+/)
    param_hash[default_type.to_sym] = t.to_i if t && t != ""
    while(param_text != "")
      param_text.slice!(/ */)
      t = param_text.slice!(/^[a-z]+/)
      param_text.slice!(/ */)
      if param_text[0] == '='
        param_text[0] = ''
        param_text.slice!(/ */)
      end
      if param_text[0] == "$"
        param_text[0] = ''
        next param_hash[t.to_sym] = nil
      end
      param_hash[t.to_sym] = (param_text.slice!(/^\-?\d+/)).to_i
    end
  end
  #--------------------------------------------------------------------------
  # ● 获取文本颜色
  #--------------------------------------------------------------------------
  def self.text_color(n, windowskin = Cache.system("Window"))
    return TEXT_COLORS[n] if TEXT_COLORS[n]
    n_ = n.to_i
    n_ = DEFAULT_COLOR_INDEX if n_ < 0
    windowskin.get_pixel(64 + (n_ % 8) * 8, 96 + (n_ / 8) * 8)
  end
  #--------------------------------------------------------------------------
  # ● 将指定值变更为布尔量
  #--------------------------------------------------------------------------
  def self.check_bool(v)
    return true if v == true
    return false if v.nil? || v == false || v == 0
    return true
  end
  #--------------------------------------------------------------------------
  # ● 应用font参数到font对象上
  #--------------------------------------------------------------------------
  def self.apply_font_params(font, ps)
    font.name = INDEX_TO_FONT[ps[:name]] if ps[:name]
    font.size = ps[:size] || Font.default_size
    font.color.alpha = ps[:ca]
    font.italic = ps[:i] ? MESSAGE_EX.check_bool(ps[:i]) : Font.default_italic
    font.bold = ps[:b] ? MESSAGE_EX.check_bool(ps[:b]) : Font.default_bold
    font.shadow = ps[:s] ? MESSAGE_EX.check_bool(ps[:s]) : Font.default_shadow
    font.outline = ps[:o] ? MESSAGE_EX.check_bool(ps[:o]) : Font.default_outline
    if ps[:or]
      font.out_color.set(ps[:or],ps[:og],ps[:ob],ps[:oa])
    else
      font.out_color = Font.default_out_color
    end
    ps[:l] = MESSAGE_EX.check_bool(ps[:l])
    ps[:lc] ||= 0
    ps[:lp] ||= 2
    ps[:d] = MESSAGE_EX.check_bool(ps[:d])
    ps[:dc] ||= 0
    ps[:u] = MESSAGE_EX.check_bool(ps[:u])
    ps[:uc] ||= 0
    ps[:k] = MESSAGE_EX.check_bool(ps[:k])
    ps[:kv] ||= 50
  end
  #--------------------------------------------------------------------------
  # ● 重置指定精灵的显示原点
  #--------------------------------------------------------------------------
  def self.reset_sprite_oxy(obj, o)
    case o # 固定不动点的位置类型 以九宫格小键盘察看
    when 1
      obj.ox = 0
      obj.oy = obj.height
    when 2
      obj.ox = obj.width / 2
      obj.oy = obj.height
    when 3
      obj.ox = obj.width
      obj.oy = obj.height
    when 4
      obj.ox = 0
      obj.oy = obj.height / 2
    when 5
      obj.ox = obj.width / 2
      obj.oy = obj.height / 2
    when 6
      obj.ox = obj.width
      obj.oy = obj.height / 2
    when 7 # 【默认】显示原点为左上角
      obj.ox = 0
      obj.oy = 0
    when 8
      obj.ox = obj.width / 2
      obj.oy = 0
    when 9
      obj.ox = obj.width
      obj.oy = 0
    end
  end
  #--------------------------------------------------------------------------
  # ● 重置指定对象的显示原点位置
  #--------------------------------------------------------------------------
  def self.reset_xy_origin(obj, o)
    case o  # 固定不动点的位置类型 以九宫格小键盘察看
    when 1
      obj.y = obj.y - obj.height
    when 2
      obj.x = obj.x - obj.width / 2
      obj.y = obj.y - obj.height
    when 3
      obj.x = obj.x - obj.width
      obj.y = obj.y - obj.height
    when 4
      obj.y = obj.y - obj.height / 2
    when 5
      obj.x = obj.x - obj.width / 2
      obj.y = obj.y - obj.height / 2
    when 6
      obj.x = obj.x - obj.width
      obj.y = obj.y - obj.height / 2
    when 7; return # 【默认】显示原点为左上角
    when 8
      obj.x = obj.x - obj.width / 2
    when 9
      obj.x = obj.x - obj.width
    end
  end
  #--------------------------------------------------------------------------
  # ● 重置指定对象依据另一对象小键盘位置的新位置
  #--------------------------------------------------------------------------
  def self.reset_xy_dorigin(obj, obj2, o) # 左上角和左上角对齐
    if o < 0 # o小于0时,将obj2重置为全屏
      obj2 = Rect.new(0,0,Graphics.width,Graphics.height)
      o = o.abs
    end
    case o
    when 1,4,7; obj.x = obj2.x
    when 2,5,8; obj.x = obj2.x + obj2.width / 2
    when 3,6,9; obj.x = obj2.x + obj2.width
    end
    case o
    when 1,2,3; obj.y = obj2.y + obj2.height
    when 4,5,6; obj.y = obj2.y + obj2.height / 2
    when 7,8,9; obj.y = obj2.y
    end
  end
  #--------------------------------------------------------------------------
  # ● 基于指定位图,计算文本块所占宽高
  # (只进行 \\ 到 \e 的文本替换、忽略除\i以外的全部转义符、未考虑字号变化)
  #  k → 字符间距  ld → 行间距
  #--------------------------------------------------------------------------
  def self.calculate_text_wh(bitmap, text, k = 0, ld = 0)
    text_clone = text.dup; array_width = []; array_height = []
    # 转义符替换
    text_clone.gsub!(/\\/)      { "\e" }
    text_clone.gsub!(/\e\e/)    { "\\" }
    text_clone.gsub!(/\e[\.\|\^\!\$<>\{|\}]/i) { "" }
    # 每一行计算宽度高度
    text_clone.each_line do |line|
      icon_count = 0
      # 获取 \i[] 数目
      line.gsub!(/\ei\[\d+\]/i){ icon_count += 1; "" }
      # 清除掉全部的\w[wd]格式转义符
      line.gsub!(/\e\w+\[(\d|\w)+\]/i) { "" }
      r = bitmap.text_size(line)
      w = r.width + icon_count * 24 + (line.length - 1 + icon_count) * k
      array_width.push(w)
      h = icon_count > 0 ? [r.height, 24].max : r.height
      array_height.push(h)
    end
    return [array_width.max, array_height.inject{|sum, v| sum = sum + v + ld}]
  end
  #--------------------------------------------------------------------------
  # ● 基于指定文本设置game_message的参数
  #--------------------------------------------------------------------------
  def self.set_game_message(game_message, text)
    @window_clone_env ||= Window_EagleMessage_CloneEnv.new(game_message)
    @window_clone_env.set_game_message(game_message, text)
  end
  #--------------------------------------------------------------------------
  # ● 计算最小包围盒
  #--------------------------------------------------------------------------
  def self.get_smallest_box(rects)
    return Rect.new if rects.empty?
    r = rects.pop
    rects.each do |r2|
      r.width = [r.x + r.width, r2.x + r2.width].max - [r.x, r2.x].min
      r.height = [r.y + r.height, r2.y + r2.height].max - [r.y, r2.y].min
      r.x = r2.x if r2.x < r.x  # 新矩形在现有矩形的左外侧,更新x位置
      r.y = r2.y if r2.y < r.y  # 新矩形在现有矩形的上外侧,更新y位置
    end
    return r
  end
  #--------------------------------------------------------------------------
  # ● 矩形之间碰撞?
  #--------------------------------------------------------------------------
  def self.rect_collide_rect?(rect1, rect2)
    if((rect1.x > rect2.x && rect1.x > rect2.x + rect2.width-1) ||
       (rect1.x < rect2.x && rect1.x + rect1.width-1 < rect2.x) ||
       (rect1.y > rect2.y && rect1.y > rect2.y + rect2.height-1) ||
       (rect1.y < rect2.y && rect1.y + rect1.height-1 < rect2.y))
      return false
    end
    return true
  end
end # end of MESSAGE_EX

#==============================================================================
# ○ 定义能够响应的文字特效
#==============================================================================
module MESSAGE_EX::CHARA_EFFECTS
  def eagle_chara_effect_cin(param = ''); end
  def eagle_chara_effect_cout(param = ''); end
  if defined?(Unravel_Bitmap)
    def eagle_chara_effect_uout(param = ''); end
    def eagle_chara_effect_cu(param = ''); end
  end
  def eagle_chara_effect_csin(param = ''); end
  def eagle_chara_effect_cwave(param = ''); end
  def eagle_chara_effect_cswing(param = ''); end
  def eagle_chara_effect_czoom(param = ''); end
  def eagle_chara_effect_cshake(param = ''); end
  def eagle_chara_effect_cshake2(param = ''); end
  def eagle_chara_effect_cflash(param = ''); end
  def eagle_chara_effect_cmirror(param = ''); end
  def eagle_chara_effect_ctog(param = ''); end
  def eagle_chara_effect_cneon(param = ''); end
  def eagle_chara_effect_cmc(param = ''); end
  def eagle_chara_effect_cjump(param = ''); end
  def eagle_chara_effect_cfk(param = ''); end
end

#=============================================================================
# ○ DataManager
#=============================================================================
class << DataManager
  #--------------------------------------------------------------------------
  # ● 设置新游戏
  #--------------------------------------------------------------------------
  alias eagle_message_ex_setup_new_game setup_new_game
  def setup_new_game
    eagle_message_ex_setup_new_game
    $game_message.add_escape(MESSAGE_EX::ESCAPE_STRING_INIT)
    $game_system.reset_game_message_envs
  end
end

#=============================================================================
# ○ Game_Message
#=============================================================================
class Game_Message
  attr_reader   :params_need_apply # 存储需要被预定应用的符号(调用对应处理方法)
  attr_accessor :escape_strings # 存储预定要添加的字符串
  attr_accessor :chara_params # 存储文字特效预设 code_symbol => param_string
  attr_accessor :font_params, :win_params, :pop_params
  attr_accessor :face_params, :name_params, :pause_params
  attr_accessor :func_params, :ex_params, :env, :default_env
  attr_accessor :event_id, :child_window_w_des, :child_window_h_des
  attr_accessor :no_name_overlap_face, :no_input_pause, :active # 对话框在显示中
  attr_accessor :eagle_text # 存储实际绘制的文本(去除了预处理的转义符)
  attr_accessor :eagle_message # 兼容模式
  #--------------------------------------------------------------------------
  # ● 初始化对象
  #--------------------------------------------------------------------------
  alias eagle_message_ex_init initialize
  def initialize
    eagle_message_ex_init
    check_flags
    set_default_params
    reset_child_window_wh_add
  end
  #--------------------------------------------------------------------------
  # ● 检查flags
  #--------------------------------------------------------------------------
  def check_flags
    # 当前环境(默认 '0')
    @env = '0' if @env.nil?
    # 姓名框不遮挡脸图?
    if @no_name_overlap_face == nil
      @no_name_overlap_face = MESSAGE_EX::DEFAULT_NO_OVERLAP_FACE
    end
    # 不进入按键等待?(扩展用)
    @no_input_pause = false if @no_input_pause.nil?
    # 如果处于兼容模式,且变量值为nil,确保变量值为false
    if EAGLE_MSG_EX_COMPAT_MODE == true
      @eagle_message = false if @eagle_message.nil?
    end
  end
  #--------------------------------------------------------------------------
  # ● 获取全部可保存params的符号的数组
  #--------------------------------------------------------------------------
  def eagle_params
    [:chara, :font, :win, :pop, :face, :name, :pause, :func, :ex]
  end
  #--------------------------------------------------------------------------
  # ● 获取params的初始值
  #--------------------------------------------------------------------------
  def set_default_params
    eagle_params.each do |sym|
      send("#{sym}_params=".to_sym, MESSAGE_EX.get_default_params(sym).dup)
    end
    @params_need_apply = eagle_params.dup # 添加修改预定,用于应用初始值
    @escape_strings = [] # 存储等待执行的转义符字符串
  end
  #--------------------------------------------------------------------------
  # ● 检查params是否都存在(读档后调用,确保新增变量写入旧存档中)
  #--------------------------------------------------------------------------
  def check_params
    check_flags
    eagle_params.each do |sym|
      params = self.send("#{sym}_params".to_sym)
      def_params = MESSAGE_EX.get_default_params(sym).dup
      if params == nil
        self.send("#{sym}_params=".to_sym, def_params)
        add_apply(sym)
      else
        params = def_params.merge(params)
        self.send("#{sym}_params=".to_sym, params)
      end
    end
  end
  #--------------------------------------------------------------------------
  # ● 清除(每次对话框关闭前被调用)
  #--------------------------------------------------------------------------
  alias eagle_message_ex_clear clear
  def clear
    eagle_message_ex_clear
    @eagle_text = ""
    @event_id = 0 # 存储当前执行的Game_Interpreter的事件ID
    if EAGLE_MSG_EX_COMPAT_MODE == false # 如果不处于兼容模式,确保变量恒为true
      @eagle_message = true
    end
  end
  #--------------------------------------------------------------------------
  # ● 重置额外增加的宽高(new_page时调用)
  #--------------------------------------------------------------------------
  def reset_child_window_wh_add
    @child_window_w_des = 0 # 因子窗口嵌入而额外增加的宽高
    @child_window_h_des = 0
  end
  #--------------------------------------------------------------------------
  # ● 下一次对话时要解析的转义符
  # 【由于解析问题,字符串中请将 "\" 替换成 "\e" 】
  #--------------------------------------------------------------------------
  def add_escape(string)
    @escape_strings.push(string)
  end
  #--------------------------------------------------------------------------
  # ● 判定是否需要进入等待按键状态
  #--------------------------------------------------------------------------
  def input_pause?
    return false if @no_input_pause
    !(choice? || num_input? || item_choice?)
  end
  #--------------------------------------------------------------------------
  # ● 使用了各个对话框组件?
  #--------------------------------------------------------------------------
  def pop?;     @pop_params[:type] != nil; end
  def pop_tag?; @pop_params[:tag] > 0;     end
  def face?;    @face_params[:name] != ""; end
  def name?;    @name_params[:name] != ""; end
  #--------------------------------------------------------------------------
  # ● 添加修改预定
  #--------------------------------------------------------------------------
  def add_apply(param_sym)
    return if @params_need_apply.include?(param_sym)
    @params_need_apply.push(param_sym)
  end
  #--------------------------------------------------------------------------
  # ● 清空预定修改
  #--------------------------------------------------------------------------
  def clear_applys
    @params_need_apply.clear
  end
  #--------------------------------------------------------------------------
  # ● 重置指定参数
  #--------------------------------------------------------------------------
  def reset_params(param_sym, code_string = nil)
    return eagle_params.each{|sym| reset_params(sym)} if param_sym.nil?
    return reset_params_c(code_string) if param_sym == :chara
    return reset_params_ex(code_string) if param_sym == :ex
    default_params = MESSAGE_EX.get_default_params(param_sym)
    if code_string.nil? # 直接清除全部参数
      new_params = default_params.dup
    else
      new_params = method( param_sym.to_s + "_params" ).call.dup # 获取旧的hash
      code_string.split("|").each { |c|
        new_params[c.to_sym] = default_params[c.to_sym]
      }
    end
    self.send((param_sym.to_s+"_params=").to_sym, new_params)
    add_apply(param_sym) # 添加修改预定,用于应用结果
  end
  #--------------------------------------------------------------------------
  # ● 重置指定文字特效
  #--------------------------------------------------------------------------
  def reset_params_c(code_string = nil)
    default_params = MESSAGE_EX.get_default_params(:chara)
    return @chara_params = default_params if code_string.nil?
    code_string.split("|").each { |c|
      if default_params[c.to_sym]
        @chara_params[c.to_sym] = default_params[c.to_sym]
      else
        @chara_params.delete(c.to_sym)
      end
    }
  end
  #--------------------------------------------------------------------------
  # ● 重置扩展转义符
  #--------------------------------------------------------------------------
  def reset_params_ex(code_string = nil)
    default_params = MESSAGE_EX.get_default_params(:ex)
    return @ex_params = default_params if code_string.nil?
    code_string.split("|").each { |c|
      if default_params[c.to_sym]
        @ex_params[c.to_sym] = default_params[c.to_sym]
      else
        @ex_params.delete(c.to_sym)
      end
    }
  end
  #--------------------------------------------------------------------------
  # ● 参数拷贝
  #--------------------------------------------------------------------------
  def clone2
    t = Game_Message.new
    clone_rgss(t)
    t.visible = true
    eagle_params.each do |sym|
      m = "#{sym}_params"
      t.send("#{m}=".to_sym, method(m.to_sym).call.clone)
    end
    clone_ex(t)
    t
  end
  #--------------------------------------------------------------------------
  # ● 参数拷贝(RGSS中的变量)
  #--------------------------------------------------------------------------
  def clone_rgss(t)
    t.background = @background
    t.position = @position
  end
  #--------------------------------------------------------------------------
  # ● 参数拷贝(扩展)
  #--------------------------------------------------------------------------
  def clone_ex(t)
    t.no_name_overlap_face = @no_name_overlap_face
  end
  #--------------------------------------------------------------------------
  # ● 保存当前环境
  #--------------------------------------------------------------------------
  def save_env(sym = '0')
    if sym == '0' # 如果是默认环境,则保存到自己,防止与子窗口的默认环境相互干扰
      @default_env = self.clone2
      return
    end
    $game_system.message_envs[sym] = self.clone2
  end
  #--------------------------------------------------------------------------
  # ● 读取指定环境
  #--------------------------------------------------------------------------
  def load_env(sym = '0')
    t = $game_system.message_envs[sym]
    t = @default_env if sym == '0'
    return false if t.nil?
    # 应用params
    eagle_params.each do |s|
      m = "#{s}_params"
      self.send("#{m}=".to_sym, t.method(m.to_sym).call.clone)
    end
    # 新增转义符的应用预定
    @params_need_apply = eagle_params
    # 应用扩展数据
    t.clone_ex(self)
    return true
  end
  #--------------------------------------------------------------------------
  # ● 将临时环境存入指定环境,并返回临时环境
  #--------------------------------------------------------------------------
  def load_temp_env(sym = '0', temp = :eagle_temp)
    if sym == '0'
      @default_env = $game_system.message_envs[temp]
    else
      $game_system.message_envs[sym] = $game_system.message_envs[temp]
    end
    return $game_system.message_envs[temp]
  end
end
#=============================================================================
# ○ Game_System
#=============================================================================
class Game_System
  attr_reader   :message_envs  # 对话框环境
  #--------------------------------------------------------------------------
  # ● 初始化对象
  #--------------------------------------------------------------------------
  alias eagle_message_ex_initialize initialize
  def initialize
    eagle_message_ex_initialize
    @message_envs = {} # sym => game_message
  end
  #--------------------------------------------------------------------------
  # ● 读档后的处理
  #--------------------------------------------------------------------------
  alias eagle_message_ex_on_after_load on_after_load
  def on_after_load
    eagle_message_ex_on_after_load
    reset_game_message_envs
    $game_message.check_params # 确保新增AddOn时,参数hash存在
    @message_envs.each { |sym, g| g.check_params }
  end
  #--------------------------------------------------------------------------
  # ● 重置初始环境
  #--------------------------------------------------------------------------
  def reset_game_message_envs
    MESSAGE_EX::DEFAULT_ENVS.each do |sym, text|
      g = Game_Message.new
      MESSAGE_EX.set_game_message(g, text)
      @message_envs[sym] = g
    end
    $game_message.save_env if $game_message.default_env == nil
  end
end
#=============================================================================
# ○ Game_Interpreter
#=============================================================================
class Game_Interpreter
  #--------------------------------------------------------------------------
  # ● 显示文字
  #--------------------------------------------------------------------------
  alias eagle_message_ex_command_101 command_101
  def command_101
    $game_message.event_id = @event_id
    eagle_message_ex_command_101
  end
end

#=============================================================================
# ○ Window_EagleMessage
#=============================================================================
class Window_EagleMessage < Window_Base
  include MESSAGE_EX::CHARA_EFFECTS
  attr_reader :eagle_charas_w, :eagle_charas_h, :eagle_chara_viewport
  #--------------------------------------------------------------------------
  # ● 获取主参数组(方便子窗口修改为自己的game_message)
  #--------------------------------------------------------------------------
  def game_message;     $game_message;      end
  def game_message=(g); $game_message = g;  end
  #--------------------------------------------------------------------------
  # ● 【通用方法】
  #--------------------------------------------------------------------------
  def parse_param(param_hash, param_text, default_type = "default")
    MESSAGE_EX.parse_param(param_hash, param_text, default_type)
  end
  def text_color(n)
    MESSAGE_EX.text_color(n, self.windowskin)
  end

  #--------------------------------------------------------------------------
  # ● 初始化对象
  #--------------------------------------------------------------------------
  def initialize
    super(0, 0, window_width, window_height)
    create_all_windows
    eagle_message_init_assets
    eagle_message_init_params
    eagle_message_reset
    self.openness = 0
  end
  #--------------------------------------------------------------------------
  # ● 生成所有子窗口
  #--------------------------------------------------------------------------
  def create_all_windows
    @gold_window = Window_EagleMsgGold.new(self)
    @choice_window = Window_EagleChoiceList.new(self) rescue Window_ChoiceList.new(self)
    @number_window = Window_EagleNumberInput.new(self) rescue Window_NumberInput.new(self)
    @item_window = Window_EagleKeyItem.new(self) rescue Window_KeyItem.new(self)
  end
  #--------------------------------------------------------------------------
  # ● 初始化组件
  # (在进行 clone 时,需要将该方法中的组件直接赋值给拷贝窗口)
  #--------------------------------------------------------------------------
  def eagle_message_init_assets
    @back_sprite = Sprite.new  # 显示背景图片的精灵
    @eagle_chara_viewport = Viewport.new # 文字精灵的显示区域
    @eagle_chara_sprites = []  # 存储全部的文字精灵
    @eagle_sprite_pop_tag = Sprite.new # pop状态下的tag精灵
    @eagle_sprite_face = nil   # 脸图精灵
    @eagle_window_name = Window_EagleMsgName.new(self) # 姓名框
    @eagle_sprite_pause = Sprite_EaglePauseTag.new(self) # 等待按键的精灵
  end
  #--------------------------------------------------------------------------
  # ● 初始化参数(只会在 initialize 时执行一次)
  # (推荐将一些不需要传递给拷贝窗口的数据置于此处)
  #--------------------------------------------------------------------------
  def eagle_message_init_params
    self.back_opacity = 255     # 背景不透明度
    self.arrows_visible = false # 内容位图未完全显示时出现的箭头
    @fiber = nil                # 纤程
    @background = 0             # 背景类型
    @position = 2               # 显示位置
    @flag_draw = true           # true 代表进行实际的绘制
    @flag_open_close = false    # 当正在进行打开/关闭时,置为 true
    @flag_input_pause = false   # 当正在进行按键等待时,置为 true
    @flag_need_open = true      # true 代表需要执行open_and_wait
    @flag_need_change_wh = false # true 代表需要进行宽高的动态变化
    @flag_need_close = false    # true 代表当前对话框必需处理关闭
    @flag_hold = false          # true 代表当前对话框会被拷贝保留,并同步更新
    @flag_instant = false       # true 代表当前对话框不再处理文字显示时的等待
    @flag_next = false          # true 代表当前对话框不关闭,下一显示文字将继续使用
    @flag_temp = false          # true 代表当前对话框的任何修改将不被保存
    @flag_temp_env = nil        # 存储当前对话框开启前的环境
    @flag_save_env = nil        # 当不为 nil 时,当前对话框状态保存到对应环境中
    @flag_no_fix = false        # 关闭位置修正功能
    @eagle_dup_windows ||= []   # 存储生成的全部拷贝对话框
    @eagle_evals = []           # 当前对话中的待执行脚本 [eval_str, eval_str...]
    @eagle_chara_sets = {}      # 存储文字的全部分组 {分组名 => [文字精灵]}
    @eagle_current_set = nil    # 当前分组的名称
    # 对话框动态移动偏移量(此处新增定义,防止子类覆盖了 eagle_message_reset)
    @eagle_move_x = @eagle_move_y = 0
    # 存储对话框在上一帧的位置(此处新增定义,防止子类覆盖了 eagle_message_reset)
    @eagle_last_x = @eagle_last_y = nil
    # 对话框位置的强制增量
    @eagle_offset_x = @eagle_offset_y = 0
    eagle_check_func("")        # 预先执行一遍func,确保它们都是布尔量
  end
  #--------------------------------------------------------------------------
  # ● 拷贝自身
  #--------------------------------------------------------------------------
  def clone(window = Window_EagleMessage_Clone)
    t = window.new(game_message.clone2)
    t.game_message.win_params[:z] = 0
    t.move(self.x, self.y, self.width, self.height)
    t.z = self.z - 5
    t.windowskin = self.windowskin
    # 拷贝背景精灵
    t.back_bitmap = @back_bitmap
    t.back_sprite = @back_sprite
    @back_bitmap = nil
    # 拷贝视图
    t.eagle_chara_viewport = @eagle_chara_viewport
    # 拷贝文字组
    t.eagle_chara_sprites = @eagle_chara_sprites
    t.eagle_chara_sprites.each { |s| s.bind_window(t) }
    # 复制文本宽高
    t.eagle_set_chara_wh(@eagle_charas_w, @eagle_charas_h,
      @eagle_charas_w_final, @eagle_charas_h_final)
    # 拷贝pop对象
    t.eagle_pop_obj = @eagle_pop_obj
    # 拷贝pop的tag
    t.eagle_sprite_pop_tag = @eagle_sprite_pop_tag
    # 拷贝脸图
    if @eagle_sprite_face
      t.eagle_sprite_face = @eagle_sprite_face
      t.eagle_sprite_face.bind_window(t)
    end
    # 拷贝姓名框
    t.eagle_window_name = @eagle_window_name
    t.eagle_window_name.bind_window(t)
    # 拷贝pause精灵
    t.eagle_sprite_pause = @eagle_sprite_pause
    t.eagle_sprite_pause.bind_window(t)
    # 扩展
    t = clone_ex(t)
    # 集体更新z值
    t.eagle_reset_z
    # 自身初始化组件与重置
    eagle_message_init_assets
    # 更新自身的z值
    eagle_reset_z
    t
  end
  #--------------------------------------------------------------------------
  # ● 拷贝自身(扩展用处理)
  #--------------------------------------------------------------------------
  def clone_ex(t)
    t
  end
  #--------------------------------------------------------------------------
  # ● 释放
  #--------------------------------------------------------------------------
  def dispose
    dispose_all_windows
    dispose_back_bitmap
    @back_sprite.dispose
    @eagle_chara_sprites.each { |s| s.dispose }
    @eagle_window_name.dispose
    @eagle_sprite_face.dispose if @eagle_sprite_face
    @eagle_sprite_pop_tag.dispose
    @eagle_sprite_pause.dispose
    @eagle_dup_windows.each { |w| w.dispose if !w.disposed? }
    @eagle_chara_viewport.dispose
    super
  end
  #--------------------------------------------------------------------------
  # ● 释放所有窗口
  #--------------------------------------------------------------------------
  def dispose_all_windows
    @gold_window.dispose
    @choice_window.dispose
    @number_window.dispose
    @item_window.dispose
  end
  #--------------------------------------------------------------------------
  # ● 释放背景位图
  #--------------------------------------------------------------------------
  def dispose_back_bitmap
    @back_bitmap.dispose if @back_bitmap
    @back_bitmap = nil
  end

  #--------------------------------------------------------------------------
  # ● 重置对话框
  #--------------------------------------------------------------------------
  def eagle_message_reset
    eagle_message_reset_continue
    eagle_move_out_assets    # 移出全部组件
    @eagle_last_x = @eagle_last_y = nil  # 重置存储的对话框的上一个位置
    @eagle_move_x = @eagle_move_y = 0    # 重置对话框动态移动位置
    @eagle_win_des_w = @eagle_win_des_h = 0  # 重置对话框最终显示宽高
    @eagle_charas_w = @eagle_charas_h = 0    # 重置文字区域的宽高
    @eagle_charas_w_final = @eagle_charas_h_final = 0
    face_params[:width] = 0  # 重置脸图的宽度(用于文字偏移)
    @eagle_pop_obj = nil     # 重置pop对象,防止报错
    eagle_reset_z            # 重置z值
    clear_flags              # 清除显示标志
  end
  #--------------------------------------------------------------------------
  # ● 重置对话框(对话框不关闭,清空全部设置,并继续显示)
  #--------------------------------------------------------------------------
  def eagle_message_reset_continue
    show if !self.visible    # 确保对话框显示
    if @flag_next  # 如果继续显示在当前对话框里,则不移出文字
    else
      eagle_message_sprites_move_out  # 移出全部文字精灵
      eagle_reset_charas_oxy  # 重置文字显示区域
      @eagle_chara_sets.clear  # 清空文字分组
    end
    eagle_process_env        # 处理环境的存储或读取
    eagle_process_temp       # 处理临时环境
    @eagle_sprite_pop_tag.visible = false # 隐藏pop的tag
    @eagle_sprite_pause.visible = false   # 隐藏等待按键pause精灵
    @eagle_sprite_pause.bind_last_chara(nil)  # 重置pause精灵的文末位置
    @eagle_sprite_pause_width_add = 0     # 因pause精灵而扩展的窗口宽度
    @eagle_next_chara_x = 0     # 重置下一个文字的绘制坐标x(左对齐、不考虑换行)
    @eagle_force_close = false  # 重置强制关闭
  end
  #--------------------------------------------------------------------------
  # ● 移出全部组件
  #--------------------------------------------------------------------------
  def eagle_move_out_assets
    pop_params[:with_tag] = false
    @eagle_sprite_pop_tag.visible = false  # 隐藏pop的tag
    @eagle_sprite_pause.visible = false    # 隐藏pause精灵
    face_params[:name] = ""   # 移出显示的脸图
    eagle_move_out_face
    # 关闭姓名框(因为对话框关闭后,姓名框不再更新,调小openness确保先关闭)
    @eagle_window_name.close
    @eagle_window_name.openness -= 15
  end
  #--------------------------------------------------------------------------
  # ● 更新全部组件的不透明度
  #--------------------------------------------------------------------------
  def update_assets_opacity(opa)
    @eagle_chara_sprites.each { |c| c.opacity = opa }
    if game_message.pop? && pop_params[:with_tag]
      @eagle_sprite_pop_tag.opacity = opa
    end
    @eagle_sprite_face.opa = opa if @eagle_sprite_face
    if game_message.name?
      @eagle_window_name.opacity = opa if @eagle_window_name.back_opacity > 0
      @eagle_window_name.contents_opacity = opa
    end
    update_back_sprite_opa(opa)
  end
  #--------------------------------------------------------------------------
  # ● 重设z值
  #--------------------------------------------------------------------------
  def eagle_reset_z
    self.z = win_params[:z] if win_params[:z] > 0
    @back_sprite.z = self.z
    @eagle_chara_viewport.z = self.z + 1
    @eagle_sprite_pop_tag.z = self.z + 1
    @eagle_sprite_pause.z = self.z + 2
    @eagle_window_name.z = self.z + 3
  end
  #--------------------------------------------------------------------------
  # ● 显示窗口
  #--------------------------------------------------------------------------
  def show
    self.visible = true
    @back_sprite.visible = true if self.opacity == 0
    @eagle_chara_sprites.each { |s| s.move_in }
    if game_message.pop? && pop_params[:with_tag]
      @eagle_sprite_pop_tag.visible = true
    end
    @eagle_sprite_face.visible = true if @eagle_sprite_face
    @eagle_window_name.show if game_message.name?
    @eagle_sprite_pause.visible = true if @flag_input_pause
    show_ex
    self
  end
  #--------------------------------------------------------------------------
  # ● 显示(扩展用)
  #--------------------------------------------------------------------------
  def show_ex
    @eagle_dup_windows.each { |w| w.show }
  end
  #--------------------------------------------------------------------------
  # ● 隐藏窗口
  #--------------------------------------------------------------------------
  def hide
    self.visible = false
    @back_sprite.visible = false
    @eagle_chara_sprites.each { |s| s.move_out_temp }
    @eagle_sprite_pop_tag.visible = false
    @eagle_sprite_face.visible = false if @eagle_sprite_face
    @eagle_window_name.hide
    @eagle_sprite_pause.visible = false
    hide_ex
    self
  end
  #--------------------------------------------------------------------------
  # ● 隐藏(扩展用)
  #--------------------------------------------------------------------------
  def hide_ex
    @eagle_dup_windows.each { |w| w.hide }
  end

  #--------------------------------------------------------------------------
  # ● 更新画面
  #--------------------------------------------------------------------------
  def update
    super
    eagle_update_before_fiber
    update_fiber
    eagle_update_after_fiber
  end
  #--------------------------------------------------------------------------
  # ● 更新fiber
  #--------------------------------------------------------------------------
  def update_fiber
    if @fiber
      @fiber.resume
    elsif game_message.busy? && !game_message.scroll_mode
      @fiber = Fiber.new { fiber_main }
      @fiber.resume
    else
      game_message.visible = false
    end
  end
  #--------------------------------------------------------------------------
  # ● 更新(在 @fiber 更新之前)
  #--------------------------------------------------------------------------
  def eagle_update_before_fiber
    update_all_windows
    update_back_sprite
    if self.openness > 0
      eagle_update_assets_after_open
      eagle_update_func_after_open
    end
  end
  #--------------------------------------------------------------------------
  # ● 更新所有窗口
  #--------------------------------------------------------------------------
  def update_all_windows
    @gold_window.update
    @choice_window.update
    @number_window.update
    @item_window.update
  end
  #--------------------------------------------------------------------------
  # ● 对话框打开后进行组件更新
  #--------------------------------------------------------------------------
  def eagle_update_assets_after_open
    eagle_pop_update if game_message.pop?
    @eagle_window_name.update
    @eagle_sprite_face.update if @eagle_sprite_face
    @eagle_sprite_pause.update if @eagle_sprite_pause.visible
  end
  #--------------------------------------------------------------------------
  # ● 对话框打开后进行扩展功能更新
  #--------------------------------------------------------------------------
  def eagle_update_func_after_open
    (self.visible ? hide : show) if MESSAGE_EX.toggle_visible?
  end
  #--------------------------------------------------------------------------
  # ● 更新(在 @fiber 更新之后)
  #--------------------------------------------------------------------------
  def eagle_update_after_fiber
    @eagle_chara_sprites.each { |s| s.update }
    eagle_update_dup_windows
  end
  #--------------------------------------------------------------------------
  # ● 更新拷贝窗口
  #--------------------------------------------------------------------------
  def eagle_update_dup_windows
    if @eagle_dup_windows.size > 0
      @eagle_dup_windows.each { |w| w.update }
      if @eagle_dup_windows[-1].openness <= 0
        t = @eagle_dup_windows.pop
        t.dispose
      end
    end
  end

  #--------------------------------------------------------------------------
  # ● 打开直至完成(当有文字绘制完成时执行)
  #--------------------------------------------------------------------------
  def open_and_wait
    @flag_open_close = true
    eagle_process_open_and_wait
    @eagle_window_name.start if game_message.name? # 对话框打开后再打开姓名框
    @flag_open_close = false
    @flag_need_open = false
  end
  #--------------------------------------------------------------------------
  # ● 处理打开方式(打开直至完成)
  #--------------------------------------------------------------------------
  def eagle_process_open_and_wait
    case func_params[:open]
    when 3
      eagle_open_type_slide
    when 2
      eagle_open_type_ease
    when 1
      eagle_open_type_fade
    else # :default
      eagle_open_type_default
    end
  end
  #--------------------------------------------------------------------------
  # ● 打开-滑动移入
  #--------------------------------------------------------------------------
  def eagle_open_type_slide
    self.openness = 0
    @eagle_chara_sprites.each { |c| c.visible = false }
    # 临时屏蔽位置修改功能,让对话框能显示到屏幕外
    @flag_no_fix = true
    h = {:ins => true, :open => true}
    eagle_set_wh(h)
    self.openness = 255
    # 直接指定初始位置在屏幕外
    @eagle_last_x = self.x
    @eagle_last_y = self.y > Graphics.height/2 ? (Graphics.height+20) : (0-self.height-20)
    # 移动回真实显示位置
    eagle_set_wh({:ease_type => :msg_open_slide})
    # 解除屏蔽
    @flag_no_fix = false
    @eagle_chara_sprites.each { |c| c.move_in; c.visible = true }
  end
  #--------------------------------------------------------------------------
  # ● 打开-动态展开
  #--------------------------------------------------------------------------
  def eagle_open_type_ease
    @eagle_chara_sprites.each { |c| c.visible = false }
    eagle_set_wh({:w => 1, :h => 1, :ins => true, :open => true}) if self.openness == 0
    self.openness = 255
    eagle_set_wh({:open => true})
    @eagle_chara_sprites.each { |c| c.move_in; c.visible = true }
  end
  #--------------------------------------------------------------------------
  # ● 打开-淡入
  #--------------------------------------------------------------------------
  def eagle_open_type_fade
    eagle_set_wh({:ins => true, :open => true})
    update_back_sprite_zoom(nil, nil) # 将背景精灵的缩放重置为 1.0
    @eagle_chara_sprites.each { |c| c.move_in }
    self.openness = 255
    @eagle_window_name.openness = 255 if game_message.name?
    _opa = 0
    while ( _opa < 255 )
      _opa += 26
      self.opacity = _opa if @background == 0 && !@back_sprite.visible
      update_assets_opacity(_opa)
      Fiber.yield
      eagle_win_update # 因为update中不更新基础对话框的位置,故加入一次更新
    end
  end
  #--------------------------------------------------------------------------
  # ● 打开-默认openness
  #--------------------------------------------------------------------------
  def eagle_open_type_default
    eagle_set_wh({:ins => true, :open => true})
    update_back_sprite_zoom(nil, nil) # 将背景精灵的缩放重置为 1
    @eagle_chara_sprites.each { |c| c.visible = false }
    open
    until open?
      update_back_sprite_opa(self.openness)
      Fiber.yield
    end
    @eagle_chara_sprites.each { |c| c.move_in; c.visible = true }
  end
  #--------------------------------------------------------------------------
  # ● 关闭直至完成
  #--------------------------------------------------------------------------
  def close_and_wait
    @flag_open_close = true
    show if !self.visible
    Fiber.yield # 关闭前等待1帧,用于截图等
    eagle_message_sprites_move_out
    eagle_process_close_and_wait
    @flag_open_close = false
    @flag_need_open = true
    @back_sprite.visible = false
  end
  #--------------------------------------------------------------------------
  # ● 处理关闭方式(关闭直至完成)
  #--------------------------------------------------------------------------
  def eagle_process_close_and_wait
    case func_params[:close]
    when 3
      eagle_close_type_slide
    when 2
      eagle_close_type_ease
    when 1
      eagle_close_type_fade
    else # :default
      eagle_close_type_default
    end
  end
  #--------------------------------------------------------------------------
  # ● 关闭-滑出
  #--------------------------------------------------------------------------
  def eagle_close_type_slide
    eagle_move_out_assets
    @flag_no_fix = true
    # 直接指定目的位置在屏幕外
    h = {:ease_type => :msg_close_slide, :close => true,
      :w => self.width, :h => self.height}
    h[:x_init] = self.x
    h[:y_init] = self.y
    h[:x] = self.x
    h[:y] = self.y > Graphics.height/2 ? (Graphics.height+20) : (0-self.height-20)
    # 由于 eagle_set_wh 原理是把基于目前xywh的增量逐渐减小至0,然后完成更新,
    #  需要先强制把当前y改成目的地处,才能正常依靠增量减小而逼近目的地
    @eagle_offset_y = h[:y] - h[:y_init]
    eagle_set_wh(h)
    @flag_no_fix = false
    self.openness = 0
    close
    @eagle_offset_y = 0
  end
  #--------------------------------------------------------------------------
  # ● 关闭-动态缩小
  #--------------------------------------------------------------------------
  def eagle_close_type_ease
    eagle_move_out_assets
    eagle_set_wh({:w => 1, :h => 1, :close => true}) if self.openness > 0
    self.openness = 0
    close
    if @eagle_window_name
      # 如果是透明对话框,没有缩小过程,姓名框来不及关闭
      @eagle_window_name.contents_opacity = 0
      @eagle_window_name.update
    end
  end
  #--------------------------------------------------------------------------
  # ● 关闭-淡出
  #--------------------------------------------------------------------------
  def eagle_close_type_fade
    _opa = [self.opacity, @back_sprite.opacity].max
    while ( _opa > 0 )
      @eagle_offset_y -= 2
      _opa -= 30
      self.opacity = _opa if @background == 0 && !@back_sprite.visible
      update_assets_opacity(_opa)
      Fiber.yield
      eagle_win_update # 因为update中不更新基础对话框的位置,故加入一次更新
    end
    self.openness = 0
    close
    @eagle_offset_y = 0
  end
  #--------------------------------------------------------------------------
  # ● 关闭-默认openness
  #--------------------------------------------------------------------------
  def eagle_close_type_default
    close
    until all_close?
      update_back_sprite_opa(self.openness)
      Fiber.yield
    end
  end
  #--------------------------------------------------------------------------
  # ● 关闭
  #--------------------------------------------------------------------------
  def close
    super
    eagle_message_reset
    eagle_reset_env # 特殊:重置环境
  end
  #--------------------------------------------------------------------------
  # ● 判定是否所有窗口已全部关闭
  #--------------------------------------------------------------------------
  def all_close?
    close? && @choice_window.close? &&
    @number_window.close? && @item_window.close?
  end
  #--------------------------------------------------------------------------
  # ● 移出全部文字精灵
  #--------------------------------------------------------------------------
  def eagle_message_sprites_move_out
    while(!@eagle_chara_sprites.empty?)
      c = eagle_take_out_a_chara
      ensure_character_visible(c, true) # 不要聚焦当前文字的缓动动画,太慢
      c.move_out # 已经交由文字池进行后续更新释放
      if @eagle_force_close  # 如果强制关闭,则不等待移出了
      else
        win_params[:cwo].times { Fiber.yield }
        if @flag_need_change_wh || # 如果预定了宽高变化,则这里不需要再逐个变化了
           win_params[:cwo] == 0  # 如果根本没有逐个移出,也不需要动态变化宽高
        else
          eagle_recalc_charas_wh_when_out
        end
      end
    end
    @eagle_chara_sets.clear
  end
  #--------------------------------------------------------------------------
  # ● 取出一个文字精灵
  #--------------------------------------------------------------------------
  def eagle_take_out_a_chara
    case win_params[:cor]
    when 0, false; return @eagle_chara_sprites.shift
    when 1, true; return @eagle_chara_sprites.pop
    when 2;
      i = rand(@eagle_chara_sprites.size)
      return @eagle_chara_sprites.delete_at(i)
    end
  end
  #--------------------------------------------------------------------------
  # ● 取出一个文字精灵后,重新计算宽高
  #--------------------------------------------------------------------------
  def eagle_recalc_charas_wh_when_out
    rects = @eagle_chara_sprites.collect { |s| s.screen_rect }
    r = MESSAGE_EX.get_smallest_box(rects)
    @eagle_charas_w = r.width
    @eagle_charas_h = r.height
    eagle_set_wh({:ins => true})
  end

  #--------------------------------------------------------------------------
  # ● 设置对话框大小位置,并等待更新结束
  #--------------------------------------------------------------------------
  def eagle_set_wh(_p = {})
    eagle_before_set_xywh(_p)
    eagle_set_params_xywh(_p)
    eagle_apply_params_xywh(_p)
    wait_until_des_wh(_p)
    eagle_after_set_xywh(_p)
  end
  #--------------------------------------------------------------------------
  # ● 对话框位置大小更新前的处理
  #--------------------------------------------------------------------------
  def eagle_before_set_xywh(_p)
  end
  #--------------------------------------------------------------------------
  # ● 设置对话框xywh的参数Hash
  #--------------------------------------------------------------------------
  def eagle_set_params_xywh(_p = {})
    _p[:update] = [] # 将会进行更新的属性
    _p[:ins] = false if _p[:ins].nil? # 如果需要立刻更新完成,置为 true
    _p[:ins] = true if @background == 2 # 透明背景时直接更新完成
    _p[:open] = false if _p[:open].nil? # 如果目标是打开窗口,置为 true
    _p[:t] ||= 20 # 更新所需时间(帧)
    # _p[:ease_type] # 指定预设所用的缓动函数类别,见 MESSAGE_EX.ease_value()
   
    # 先计算目标宽高(该变量与其它参数均无关,只需要预绘制后的文字区域宽高)
    #_p[:w] 与 _p[:h] 为更新结束时的宽高,如果为 nil,将自动计算
    _p[:w_init] = self.width
    if _p[:w].nil?
      _p[:w]  = eagle_window_width
      _p[:w] += eagle_window_width_add(_p[:w])
    end
    _p[:w_d] = _p[:w] - _p[:w_init]
    _p[:update].push(:w) if _p[:w_d] != 0

    _p[:h_init] = self.height
    if _p[:h].nil?
      _p[:h]  = eagle_window_height
      _p[:h] += eagle_window_height_add(_p[:h])
    end
    _p[:h_d] = _p[:h] - _p[:h_init]
    _p[:update].push(:h) if _p[:h_d] != 0
   
    # 如果为打开/预定变更宽高,记录最终的宽高
    if _p[:open] || @flag_need_change_wh
      @eagle_win_des_w = _p[:w]
      @eagle_win_des_h = _p[:h]
    end
    # 需要先计算出目标宽高,再更新该设置,才能保证更新后的xy为真正的移动目标位置
    eagle_win_update

    #_p[:x] 与 _p[:y] 为更新结束时的位置,如果为 nil,且对话框未关闭,将自动计算
    if _p[:open] || @eagle_last_x.nil? || @eagle_last_y.nil?
      # 如果为打开,则已经直接移到了目的地,此处不再变更xy
      _p[:x] = nil
      _p[:y] = nil
    elsif self.openness == 255
      _p[:x_init] = @eagle_last_x if _p[:x_init].nil?
      _p[:y_init] = @eagle_last_y if _p[:y_init].nil?
      _p[:x] = self.x if _p[:x].nil?
      _p[:y] = self.y if _p[:y].nil?
    end
    if _p[:x]
      _p[:x_d] = _p[:x] - _p[:x_init]
      _p[:update].push(:x) if _p[:x_d] != 0
    end
    if _p[:y]
      _p[:y_d] = _p[:y] - _p[:y_init]
      _p[:update].push(:y) if _p[:y_d] != 0
    end
  end
  #--------------------------------------------------------------------------
  # ● 应用对话框xywh的参数Hash的预修改
  #--------------------------------------------------------------------------
  def eagle_apply_params_xywh(_p = {})
    if _p[:x_d] # 如果设置了移动,则覆盖移动增量来进行对话框的移动
      @eagle_move_x = - _p[:x_d]
    end
    if _p[:y_d]
      @eagle_move_y = - _p[:y_d]
    end
    if _p[:ins] # 处理立即完成宽高的更新
      @eagle_move_x = 0
      @eagle_move_y = 0
      self.width = _p[:w]
      self.height = _p[:h]
      _p[:update].clear
    end
  end
  #--------------------------------------------------------------------------
  # ● 更新对话框宽高直至完成
  #--------------------------------------------------------------------------
  def wait_until_des_wh(_p = {})
    return eagle_after_wh_change if _p[:update].empty?
    max_w = [_p[:w], _p[:w_init]].max
    max_h = [_p[:h], _p[:h_init]].max
    _i = 0; _t = _p[:t]
    _t = 0 if _t < 0
    while(true)
      break if self.openness == 0
      break if _i > _t
      per = _i * 1.0 / _t
      if _i == _t
        per = 1
      else
        f = :msg_xywh
        f = :msg_open if _p[:open]
        f = :msg_close if _p[:close]
        f = _p[:ease_type] if _p[:ease_type]
        per = MESSAGE_EX.ease_value(f, per)
      end
      _p[:update].each do |sym|
        case sym
        when :x
          _x = _p[:x_init] + _p[:x_d] * per
          @eagle_move_x = (_x - _p[:x]).round
        when :y
          _y = _p[:y_init] + _p[:y_d] * per
          @eagle_move_y = (_y - _p[:y]).round
        when :w
          self.width = (_p[:w_init] + _p[:w_d] * per).round
        when :h
          self.height = (_p[:h_init] + _p[:h_d] * per).round
        end
      end
      eagle_after_wh_change
      update_back_sprite_zoom(max_w, max_h)
      Fiber.yield
      _i += 1
    end
  end
  #--------------------------------------------------------------------------
  # ● 在宽度高度变化后的处理
  # 此时 self.width 与 self.height 为实时的宽高,非最终完成时的宽高
  #--------------------------------------------------------------------------
  def eagle_after_wh_change
    eagle_win_update  # 先更新对话框位置
    eagle_recreate_back_bitmap(self.width, self.height)  # 再生成新背景位图
  end
  #--------------------------------------------------------------------------
  # ● 对话框进行位置大小更新后的处理
  #--------------------------------------------------------------------------
  def eagle_after_set_xywh(_p)
    @eagle_last_x = self.x  # 存储对话框的新位置,之后将作为移动前的初始值
    @eagle_last_y = self.y
  end
  #--------------------------------------------------------------------------
  # ● 获取窗口的初始宽度高度
  #--------------------------------------------------------------------------
  def window_width;   Graphics.width;     end
  def window_height;  fitting_height(4);  end
  #--------------------------------------------------------------------------
  # ● 获取窗口的实际宽度高度
  #--------------------------------------------------------------------------
  def eagle_window_width
    w = eagle_window_charas_width
    if w
      w = [w, eagle_name_width].max  # 确保姓名框的嵌入
      w += eagle_face_width          # 确保脸图有地方显示
      w += standard_padding * 2      # 增加边框
      return w
    end
    return window_width
  end
  def eagle_window_height
    h = eagle_window_charas_height
    if h
      h += eagle_name_height         # 确保姓名框的嵌入
      h = [h, eagle_face_height].max if eagle_face_in_window? # 确保脸图完整显示
      h += standard_padding * 2      # 增加边框
      return h
    end
    return window_height
  end
  #--------------------------------------------------------------------------
  # ● 获取窗口的文字的宽度高度
  #--------------------------------------------------------------------------
  def eagle_window_charas_width
    return pop_params[:w] if game_message.pop? && pop_params[:w] > 0
    return win_params[:w] if win_params[:w] > 0
    w = nil
    w = @eagle_charas_w       if eagle_dynamic_w?
    w = @eagle_charas_w_final if eagle_dyn_fit_w?
    if w
      w = win_params[:wmin] if win_params[:wmin] > 0 && w < win_params[:wmin]
      w = win_params[:wmax] if win_params[:wmax] > 0 && w > win_params[:wmax]
    end
    return w
  end
  def eagle_window_charas_height
    if game_message.pop? && pop_params[:h] > 0
      return eagle_check_param_h(pop_params[:h])
    end
    return eagle_check_param_h(win_params[:h]) if win_params[:h] > 0
    h = nil
    h = [@eagle_charas_h, line_height].max if eagle_dynamic_h?
    h = @eagle_charas_h_final if eagle_dyn_fit_h?
    if h
      h = win_params[:hmin] if win_params[:hmin] > 0 && h < win_params[:hmin]
      h = win_params[:hmax] if win_params[:hmax] > 0 && h > win_params[:hmax]
    end
    return h
  end
  #--------------------------------------------------------------------------
  # ● 检查内容高度参数
  #  如果h小于行高,则判定其为行数
  #--------------------------------------------------------------------------
  def eagle_check_param_h(h)
    return 0 if h <= 0
    h = line_height * h + win_params[:ld] * (h - 1) if h < line_height
    return h
  end
  #--------------------------------------------------------------------------
  # ● 直接指定宽度高度?
  #--------------------------------------------------------------------------
  def eagle_fix_w?
    return pop_params[:w] > 0 if game_message.pop?
    return win_params[:w] > 0
  end
  def eagle_fix_h?
    return pop_params[:h] > 0 if game_message.pop?
    return win_params[:h] > 0
  end
  #--------------------------------------------------------------------------
  # ● 动态调整宽度高度?
  #--------------------------------------------------------------------------
  def eagle_dynamic_w?
    game_message.pop? ? pop_params[:dw] : win_params[:dw]
  end
  def eagle_dynamic_h?
    game_message.pop? ? pop_params[:dh] : win_params[:dh]
  end
  #--------------------------------------------------------------------------
  # ● 预计算完整文字区域的宽度高度?
  #--------------------------------------------------------------------------
  def eagle_dyn_fit_w?
    game_message.pop? ? pop_params[:fw] : win_params[:fw]
  end
  def eagle_dyn_fit_h?
    game_message.pop? ? pop_params[:fh] : win_params[:fh]
  end
  #--------------------------------------------------------------------------
  # ● 可由子窗口增加对话框的宽度高度?
  #--------------------------------------------------------------------------
  def eagle_add_w_by_child_window?
    return false if eagle_fix_w?
    return true  if eagle_dynamic_w? || eagle_dyn_fit_w?
    return false
  end
  def eagle_add_h_by_child_window?
    return false if eagle_fix_h?
    return true  if eagle_dynamic_h? || eagle_dyn_fit_h?
    return false
  end
  #--------------------------------------------------------------------------
  # ● 额外增加的窗口宽度高度
  #--------------------------------------------------------------------------
  def eagle_window_width_add(cur_width)
    eagle_window_w_empty + game_message.child_window_w_des
  end
  def eagle_window_height_add(cur_height)
    eagle_window_h_empty + game_message.child_window_h_des
  end
  #--------------------------------------------------------------------------
  # ● 窗口内容中,无法被用于文本绘制的宽高
  #--------------------------------------------------------------------------
  def eagle_window_w_empty
    win_params[:cdx] + @eagle_sprite_pause_width_add + win_params[:cdw]
  end
  def eagle_window_h_empty
    win_params[:cdy] + win_params[:cdh]
  end

  #--------------------------------------------------------------------------
  # ● 更新win参数组(初始化/一页绘制完成时调用)
  #--------------------------------------------------------------------------
  def eagle_win_update
    return eagle_pop_update if game_message.pop? && @eagle_pop_obj
    eagle_change_windowskin
    self.x = win_params[:x] || default_init_x
    self.y = win_params[:y] || default_init_y
    MESSAGE_EX.reset_xy_dorigin(self, nil, win_params[:do]) if win_params[:do] < 0
    MESSAGE_EX.reset_xy_origin(self, win_params[:o])
    eagle_set_xy_ex
    self.x = self.x + win_params[:dx] + @eagle_move_x + @eagle_offset_x
    self.y = self.y + win_params[:dy] + @eagle_move_y + @eagle_offset_y
    eagle_fix_position if win_params[:fix]
    eagle_after_update_xy
  end
  #--------------------------------------------------------------------------
  # ● 获取对话框的初始位置
  #--------------------------------------------------------------------------
  def default_init_x;  0;  end
  def default_init_y; (@position*(Graphics.height-@eagle_win_des_h)/2); end
  #--------------------------------------------------------------------------
  # ● 重定义对话框的位置(此时已经处理完了xy或do的初始指定位置)(扩展用)
  #--------------------------------------------------------------------------
  def eagle_set_xy_ex
  end
  #--------------------------------------------------------------------------
  # ● 修正位置(确保对话框完整显示)
  #--------------------------------------------------------------------------
  def eagle_fix_position
    return if @flag_no_fix
    self.x = [[self.x, 0].max, Graphics.width - self.width].min
    self.y = [[self.y, 0].max, Graphics.height - self.height].min
  end
  #--------------------------------------------------------------------------
  # ● 更新xy后的操作
  #--------------------------------------------------------------------------
  def eagle_after_update_xy
    eagle_set_charas_viewport
    eagle_name_update if game_message.name?
    if game_message.pop? && pop_params[:with_tag]
      eagle_pop_tag_fix_position  # 可能修改了对话框位置,检查下tag要不要显示
    end
  end
  #--------------------------------------------------------------------------
  # ● 设置文字显示区域的矩形(屏幕坐标)
  #--------------------------------------------------------------------------
  def eagle_set_charas_viewport
    @eagle_chara_viewport.rect.set(eagle_charas_x0, eagle_charas_y0,
      eagle_charas_max_w + eagle_window_w_empty,
      eagle_charas_max_h + eagle_window_h_empty)
  end
  #--------------------------------------------------------------------------
  # ● 更新pop参数组
  #--------------------------------------------------------------------------
  def eagle_pop_update
    eagle_change_windowskin(pop_params[:skin])
    eagle_pop_init_xy
    o = pop_params[:o] # 设置对话框的显示原点
    o ||= 10 - pop_params[:do]  # 显示原点恰好与绑定对象的位置相反
    MESSAGE_EX.reset_xy_origin(self, o)
    # 将对话框移动到绑定对象的对应方向上,并加上偏移量
    case pop_params[:do]
    when 1,4,7; self.x -= (pop_params[:chara_w] / 2 + pop_params[:d])
    when 3,6,9; self.x += (pop_params[:chara_w] / 2 + pop_params[:d])
    end
    case pop_params[:do]
    when 1,2,3; self.y += (pop_params[:d])
    when 4,5,6; self.y -= (pop_params[:chara_h] / 2)
    when 7,8,9; self.y -= (pop_params[:chara_h] + pop_params[:d])
    end
    self.x += @eagle_move_x + @eagle_offset_x
    self.y += @eagle_move_y + @eagle_offset_y
    eagle_pop_tag_update  # 更新pop的tag的位置
    self.x += pop_params[:dx]  # 坐标的补足偏移量
    self.y += pop_params[:dy]
    eagle_fix_position if pop_params[:fix]
    eagle_after_update_xy
  end
  #--------------------------------------------------------------------------
  # ● 更新pop的初始位置
  #--------------------------------------------------------------------------
  def eagle_pop_init_xy
    # 对话框左上角定位到绑定对象位图的对应o位置
    case pop_params[:type]
    when :map_chara
      # 如果对象使用的是行走图,则为Game_Chacter(行走图底部中心为显示原点)
      self.x = @eagle_pop_obj.screen_x
      self.y = @eagle_pop_obj.screen_y
    when :battle_sprite
      # 如果对象为战斗者精灵,则为Sprite_Battler(底部中心为显示原点)
      self.x = @eagle_pop_obj.x
      self.y = @eagle_pop_obj.y
    when :map_grid
      # 如果为地图格子,则格子底部中心为显示原点
      self.x = $game_map.adjust_x(@eagle_pop_obj[0]) * 32 + 16
      self.y = $game_map.adjust_y(@eagle_pop_obj[1]) * 32 + 32
    end
  end
  #--------------------------------------------------------------------------
  # ● 更新pop的tag
  #--------------------------------------------------------------------------
  def eagle_pop_tag_update
    return if !pop_params[:with_tag]
    i = 10 - pop_params[:do] # tag的显示帧恰好与pop对话框的do值相对
    # tag的显示原点,是pop对话框的do,即目标物体的九宫格位置
    o = pop_params[:do]
    #  但注意,需要调整到 2468 的位置
    o = 2 if o == 1 || o == 3
    o = 8 if o == 7 || o == 9
    _do = pop_params[:o] # 对话框的显示原点是tag的显示位置
    _do ||= 10 - pop_params[:do]
    MESSAGE_EX.set_windowtag(self, @eagle_sprite_pop_tag, i, o, _do)
    case i # 坐标距离事件格子中心的偏移量
    when 1,4,7; @eagle_sprite_pop_tag.x -= pop_params[:td]
    when 3,6,9; @eagle_sprite_pop_tag.x += pop_params[:td]
    end
    case i
    when 1,2,3; @eagle_sprite_pop_tag.y -= pop_params[:td]
    when 7,8,9; @eagle_sprite_pop_tag.y += pop_params[:td]
    end
  end
  #--------------------------------------------------------------------------
  # ● 修正pop的tag的位置
  #--------------------------------------------------------------------------
  def eagle_pop_tag_fix_position
    # 若tag因为对话框的fix position而位于对话框内,则隐藏
    s = @eagle_sprite_pop_tag
    return if !pop_params[:with_tag]
    s.visible = true
    d = pop_params[:td]
    rect1 = Rect.new(s.x+d, s.y+d, s.width-2*d, s.height-2*d)
    rect2 = Rect.new(self.x, self.y, self.width, self.height)
    if MESSAGE_EX.rect_collide_rect?(rect1, rect2)
      s.visible = false
    end
  end
  #--------------------------------------------------------------------------
  # ● 更新name参数组(随win/pop参数组更新)
  #--------------------------------------------------------------------------
  def eagle_name_update
    if name_params[:do] == 0  # 嵌入
      # 考虑到姓名框自己也有边框,此处 standard_padding 一加一减抵消了
      @eagle_window_name.x = self.x +  \
        @eagle_window_name.rect_real.x + eagle_face_left_width
      @eagle_window_name.y = self.y +  \
        @eagle_window_name.rect_real.y
    else
      MESSAGE_EX.reset_xy_dorigin(@eagle_window_name, self, name_params[:do])
      MESSAGE_EX.reset_xy_origin(@eagle_window_name, name_params[:o])
      # 若姓名框遮挡了脸图,则移动到不遮挡的地方
      if game_message.no_name_overlap_face && eagle_face_width > 0
        lx = self.x + eagle_face_left_width
        rx = self.x + self.width - eagle_face_right_width
        w = @eagle_window_name.width
        @eagle_window_name.x = lx if @eagle_window_name.x < lx
        @eagle_window_name.x = rx-w if @eagle_window_name.x+w > rx
      end
    end
    @eagle_window_name.x += name_params[:dx]  # 坐标的补足偏移量
    @eagle_window_name.y += name_params[:dy]
    @eagle_window_name.update_with_msg
  end

  #--------------------------------------------------------------------------
  # ● 变更窗口皮肤
  #--------------------------------------------------------------------------
  def eagle_change_windowskin(index = nil)
    index = win_params[:skin] if index.nil?
    eagle_set_pop_tag_by_windowskin(index)
    return if @win_skin_draw == index
    @win_skin_draw = index
    self.windowskin = MESSAGE_EX.windowskin(index)
    change_color(text_color(font_params[:c]))
  end
  #--------------------------------------------------------------------------
  # ● 获取当前窗口皮肤的序号
  #--------------------------------------------------------------------------
  def get_cur_windowskin_index(index = nil)
    return index if index
    return pop_params[:skin] if game_message.pop? && !pop_params[:skin].nil?
    return win_params[:skin]
  end
  #--------------------------------------------------------------------------
  # ● 依据窗口皮肤设置tag序号
  #--------------------------------------------------------------------------
  def eagle_set_pop_tag_by_windowskin(index = nil)
    tag_id = MESSAGE_EX::WINDOWSKIN_TO_WINDOWTAG[index]
    return if tag_id.nil?
    eagle_reset_pop_tag_bitmap(tag_id)
  end

  #--------------------------------------------------------------------------
  # ● 重新生成背景位图
  #--------------------------------------------------------------------------
  def eagle_recreate_back_bitmap(w = self.width, h = self.height)
    case @background
    when 1 # 暗色背景
      if @back_bitmap && @back_bitmap.width == w && @back_bitmap.height == h
      else # 重绘
        @back_bitmap.dispose if @back_bitmap
        @back_bitmap = Bitmap.new(w, h)
        rect1 = Rect.new(0, 0, w, 12)
        rect2 = Rect.new(0, 12, w, h - 24)
        rect3 = Rect.new(0, h - 12, w, 12)
        @back_bitmap.gradient_fill_rect(rect1, back_color2, back_color1, true)
        @back_bitmap.fill_rect(rect2, back_color1)
        @back_bitmap.gradient_fill_rect(rect3, back_color1, back_color2, true)
      end
      @back_sprite.bitmap = @back_bitmap
      update_back_sprite
      @back_sprite.visible = true
      @back_sprite.opacity = 255
      self.opacity = 0
    when 2 # 透明背景
      @back_sprite.visible = false
      @back_sprite.opacity = 255  # 依靠该值来进行关闭-淡出
      self.opacity = 0
    else # 普通
      return if win_params[:bg] && eagle_draw_bg_pic(w, h)
      @back_sprite.visible = false
      self.opacity = 255
    end
  end
  #--------------------------------------------------------------------------
  # ● 获取暗色背景的背景色
  #--------------------------------------------------------------------------
  def back_color1; Color.new(0, 0, 0, 160); end
  def back_color2; Color.new(0, 0, 0,   0); end
  #--------------------------------------------------------------------------
  # ● 绘制背景图片
  #--------------------------------------------------------------------------
  def eagle_draw_bg_pic(w, h)
    @back_sprite.visible = true
    self.opacity = 0
    _bitmap = MESSAGE_EX.windowbg(win_params[:bg], w, h)
    if _bitmap != nil
      @back_sprite.bitmap = _bitmap
      return true
    end
    return false
  end
  #--------------------------------------------------------------------------
  # ● 更新背景精灵
  #--------------------------------------------------------------------------
  def update_back_sprite
    _o = win_params[:bgo]
    _o = win_params[:o] if @background == 1  # 暗色背景下,与对话框的原点一致
    MESSAGE_EX.reset_xy_dorigin(@back_sprite, self, _o)
    MESSAGE_EX.reset_sprite_oxy(@back_sprite, _o)
    @back_sprite.update
  end
  #--------------------------------------------------------------------------
  # ● 更新背景精灵的缩放
  #--------------------------------------------------------------------------
  def update_back_sprite_zoom(max_w = nil, max_h = nil)
    if @background != 0  # 正常背景下,使用了图片背景,才进行缩放
      @back_sprite.zoom_x = @back_sprite.zoom_y = 1
      return
    end
    if max_w == nil
      @back_sprite.zoom_x = 1
    else
      @back_sprite.zoom_x = self.width * 1.0 / max_w
    end
    if max_h == nil
      @back_sprite.zoom_y = 1
    else
      @back_sprite.zoom_y = self.height * 1.0 / max_h
    end
  end
  #--------------------------------------------------------------------------
  # ● 更新背景精灵的不透明度
  #--------------------------------------------------------------------------
  def update_back_sprite_opa(opa); @back_sprite.opacity = opa; end

  #--------------------------------------------------------------------------
  # ● 处理纤程的主逻辑
  #--------------------------------------------------------------------------
  def fiber_main
    game_message.visible = true
    eagle_process_before_start
    loop do
      eagle_process_before_draw
      process_all_text
      process_input
      eagle_process_before_close
      Fiber.yield
      break unless text_continue?
      eagle_process_after_check_continue
    end
    close_and_wait if !@flag_next
    game_message.visible = false
    @fiber = nil
  end
  #--------------------------------------------------------------------------
  # ● 首次开启对话框时的处理
  #--------------------------------------------------------------------------
  def eagle_process_before_start
    @background = game_message.background
    @position   = game_message.position
  end
  #--------------------------------------------------------------------------
  # ● 绘制全部文本前
  #--------------------------------------------------------------------------
  def eagle_process_before_draw
    @flag_temp_env = game_message.env  # 保存处理文本前的环境名(用于temp)
    game_message.save_env(:eagle_temp) # 保存处理文本前的环境(用于temp)
    game_message.active = true   # 对话框开始处理文字,该flag置为true用于判定
  end
  #--------------------------------------------------------------------------
  # ● 关闭前
  #--------------------------------------------------------------------------
  def eagle_process_before_close
    game_message.clear
    @gold_window.close if @gold_window
    game_message.active = false   # 对话框不再显示了,将该flag置为false
  end
  #--------------------------------------------------------------------------
  # ● 判定文字是否继续显示(覆盖)
  #--------------------------------------------------------------------------
  def text_continue?
    return false if @flag_need_close
    return game_message.has_text? && !settings_changed?
  end
  #--------------------------------------------------------------------------
  # ● 判定对话框设置是否被更改(覆盖)
  #  此时 $game_message 中存储了下一条指令的对话信息
  #  若返回 false,则会保留当前对话框不关闭,继续显示下一个指令的文本
  #--------------------------------------------------------------------------
  def settings_changed?
    # 因为有动态移动,不再关注位置的改变
    @background != game_message.background #|| @position != game_message.position
  end
  #--------------------------------------------------------------------------
  # ● 当前对话框继续显示,一些变量重置的处理
  #--------------------------------------------------------------------------
  def eagle_process_after_check_continue
    if @flag_hold  # 保留当前对话框时
      # 如果想要对话框从当前位置继续移动,则需要该处理,否则是在新位置打开新的
      #self.openness = 255
      #@flag_need_open = false
    end
    eagle_message_reset_continue
    @flag_need_change_wh = true  # 由于没有打开关闭,需要执行一次更新宽高
  end

  #--------------------------------------------------------------------------
  # ● 获取即将绘制的所有文本内容
  #--------------------------------------------------------------------------
  def eagle_all_text
    text = game_message.all_text
    if !game_message.escape_strings.empty? # 如果存在待处理的转义符串,加到开头
      text = game_message.escape_strings.inject("") { |sum, s| sum = sum + s } + text
      game_message.escape_strings.clear
    end
    text = convert_escape_characters(text)
    text
  end
  #--------------------------------------------------------------------------
  # ● 进行控制符的事前变换
  #    在实际绘制前、将控制符替换为实际的内容。
  #    为了减少歧异,文字「\」会被首先替换为转义符(\e)。
  #--------------------------------------------------------------------------
  def convert_escape_characters(text)
    result = text.to_s.clone
    result = eagle_process_conv(result)
    result = eagle_process_rb(result)
    result = super(result) # 此处将 \\ 替换成了 \e
    result = eagle_process_extra(result)
    result
  end
  #--------------------------------------------------------------------------
  # ● 替换转义符(此时依旧是 \\ 开头的转义符)
  #--------------------------------------------------------------------------
  def eagle_process_conv(text)
    text.gsub!(/\\(conv|M)\[(.*?)\]/i) { MESSAGE_EX.get_conv($2) }
    text
  end
  #--------------------------------------------------------------------------
  # ● 处理脚本转义符(此时依旧是 \\ 开头的转义符)
  #--------------------------------------------------------------------------
  def eagle_process_rb(text)
    s = $game_switches; v = $game_variables
    event = $game_map.events[game_message.event_id] rescue nil
    text.gsub!(/\\RB\{(.*?)\}/i) { eval($1).to_s }
    text
  end
  #--------------------------------------------------------------------------
  # ● 处理其它转义符(此时是 \e 开头的转义符)
  #--------------------------------------------------------------------------
  def eagle_process_extra(text)
    text.gsub!(/\eINFO\[(\w)[ ,]*(\d+)[ ,]*(\d+)?\]/i) {
      MESSAGE_EX.get_data_info($1, $2.to_i, $3 || '0')
    }
    text.gsub!(/\eNL/i) { "\n" } # 换行转义符
    text
  end

  #--------------------------------------------------------------------------
  # ● (覆盖)处理所有文本内容
  #--------------------------------------------------------------------------
  def process_all_text
    return if !game_message.has_text?
    text = eagle_all_text; pos = {}
    new_page(text, pos)
    if func_params[:para] && !input_pause?
      @fiber_para = Fiber.new { process_all_text_para(text, pos);
        @fiber_para = nil }
      return
    end
    process_all_text_para(text, pos)
  end
  #--------------------------------------------------------------------------
  # ● 处理所有文本内容(并行用)
  #--------------------------------------------------------------------------
  def process_all_text_para(text, pos)
    if @flag_need_open # 打开窗口
      open_and_wait
    elsif @flag_need_change_wh # 如果需要更新宽高
      eagle_set_wh
      @flag_need_change_wh = false
    end
    loop do # 循环绘制每个文字
      break if text.empty?
      process_character(text.slice!(0, 1), text, pos)
      break @pause_skip = true if @eagle_force_close
    end
    eagle_process_draw_update if !@eagle_chara_sprites.empty?
  end
  #--------------------------------------------------------------------------
  # ● 强制中断绘制全部文本
  #--------------------------------------------------------------------------
  def force_close
    @eagle_force_close = true  # 若还在绘制,则直接结束绘制并跳过最后的等待按键
    @eagle_auto_continue_c = 0 # 若进入了按键等待,则将计数置0,并依靠auto跳过按键
  end
  #--------------------------------------------------------------------------
  # ● 翻页处理(删去了翻页功能)
  #--------------------------------------------------------------------------
  def new_page(text, pos)
    game_message.reset_child_window_wh_add  # 重置子窗口嵌入时占据的宽高
    @eagle_force_close = false  # 重置强制关闭
    pos[:x] = 0; pos[:y] = 0    # 重置pos参数
    pos[:new_x] = 0; pos[:height] = line_height

    if @flag_next  # 旧页面不清空,继续绘制
      pos[:y] = @eagle_charas_h_final + win_params[:ld]  # 不要忘记加个行间距
      @flag_need_change_wh = true  # 如果是旧页面继续绘制,则需要更新宽高
      eagle_after_set_xywh({})  # 重置记录的当前xy,作为窗口移动的初始位置
    end

    reset_font_settings
    eagle_check_pre_settings(text)  # 处理预先内容(脸图、姓名等)
    # 存储实际绘制的文本,预先转义符已删去(扩展用,获取显示的对话文本)
    game_message.eagle_text = text.clone
    eagle_apply_params_changes  # 执行预定的转义符修改
    clear_flags
    pre_calc_charas_wh(text, pos)  # 预绘制,计算最终宽高
    clear_flags  # 重置绘制会用到的变量

    # 打开窗口前执行位于文本开头的转义符,确保pop等修改对话框位置
    loop do
      break if text[0] == nil or text[0] != "\e"
      text[0] = ''
      process_escape_character(obtain_escape_code(text), text, pos)
    end
  end
  #--------------------------------------------------------------------------
  # ● 清除标志(此处放置每次绘制前需要清空的数据)
  #--------------------------------------------------------------------------
  def clear_flags
    @show_fast = false          # 快进的标志
    @line_show_fast = false     # 行单位快进的标志
    @pause_skip = false         # “不等待输入”的标志
    pop_params[:type] = nil     # pop绑定对象的类型
  end
  #--------------------------------------------------------------------------
  # ● 应用预定的转义符更新
  #--------------------------------------------------------------------------
  def eagle_apply_params_changes
    game_message.params_need_apply.each do |sym|
      m_c = ("eagle_text_control_#{sym}").to_sym
      method(m_c).call("") if respond_to?(m_c)
    end
    game_message.clear_applys
  end
  #--------------------------------------------------------------------------
  # ● 预先绘制一次,获取文字区域绘制完成时的宽高
  #--------------------------------------------------------------------------
  def pre_calc_charas_wh(text, pos)
    text_ = text.clone; pos_ = pos.clone
    @flag_draw = false  # 不进行实际绘制
    game_message.save_env(:pre_draw)  # 保存预绘制前的环境
    last_c_w = @eagle_charas_w  # 保存预绘制前的文字区域宽高
    last_c_h = @eagle_charas_h
    @eagle_charas_w = @eagle_charas_h = 0
    process_character(text_.slice!(0, 1), text_, pos_) until text_.empty?
    @eagle_charas_w_final = [@eagle_charas_w, last_c_w].max # 文字绘制后的宽高
    @eagle_charas_h_final = @eagle_charas_h
    before_input_pause unless @pause_skip  # 此处追加对pause精灵占用宽度的处理
    @eagle_charas_w = last_c_w
    @eagle_charas_h = last_c_h  # 复原当前文字区域宽高
    self.ox = self.oy = 0
    game_message.load_env(:pre_draw)  # 复原转义符环境
    game_message.clear_applys
    MESSAGE_EX.apply_font_params(self.contents.font, font_params) # 重置字体设置
    @flag_draw = true
  end

  #--------------------------------------------------------------------------
  # ● 文字显示区域的左上角位置(屏幕坐标系)
  #--------------------------------------------------------------------------
  def eagle_charas_x0
    self.x + standard_padding + eagle_face_left_width + win_params[:cdx]
  end
  def eagle_charas_y0
    self.y + standard_padding + eagle_name_height + win_params[:cdy]
  end
  #--------------------------------------------------------------------------
  # ● 计算文字显示区域的宽度和高度
  #--------------------------------------------------------------------------
  def eagle_charas_max_w
    self.width - standard_padding*2 - eagle_face_width - eagle_window_w_empty
  end
  def eagle_charas_max_h
    self.height - standard_padding*2 - eagle_name_height - eagle_window_h_empty
  end
  #--------------------------------------------------------------------------
  # ● 文字显示区域的显示原点
  #--------------------------------------------------------------------------
  def eagle_charas_ox; self.ox; end
  def eagle_charas_oy; self.oy; end
  #--------------------------------------------------------------------------
  # ● 更新正在移入移出文字的显示区域的显示原点
  #--------------------------------------------------------------------------
  def update_moving_charas_oxy
    @eagle_chara_sprites.each { |c|
      c.reset_window_oxy(self.ox, self.oy) if c.move_updating?
    }
  end
  #--------------------------------------------------------------------------
  # ● 重置文字显示区域
  #--------------------------------------------------------------------------
  def eagle_reset_charas_oxy
    # 重置初始区域
    self.ox = self.oy = 0
    # 重置显示区域
    @eagle_chara_viewport.rect.set(0,0,Graphics.width,Graphics.height)
    # 重置文字宽高
    @eagle_charas_w = @eagle_charas_h = 0
    @eagle_charas_w_final = @eagle_charas_h_final = 0
  end
  #--------------------------------------------------------------------------
  # ● 重新生成适合全部文字的位图
  #--------------------------------------------------------------------------
  def recreate_contents_for_charas
    w = @eagle_charas_w + eagle_window_w_empty
    h = @eagle_charas_h + eagle_window_h_empty
    w = 1 if w == 0
    h = 1 if h == 0
    f = self.contents.font.dup
    self.contents.dispose if self.contents
    self.contents = Bitmap.new(w, h)
    self.contents.font = f
  end
  #--------------------------------------------------------------------------
  # ● (覆盖)普通文字的处理
  #--------------------------------------------------------------------------
  def process_normal_character(c, pos)
    c_rect = text_size(c); c_w = c_rect.width; c_h = c_rect.height
    eagle_auto_new_line(c_w, pos)
    if @flag_draw
      s = eagle_new_chara_sprite(pos[:x], pos[:y], c_w, c_h)
      s.eagle_font.draw(s.bitmap, 0, 0, c_w, c_h, c, 0)
    end
    eagle_process_draw_end(c_w, c_h, pos)
  end
  #--------------------------------------------------------------------------
  # ● (覆盖)处理控制符指定的图标绘制
  #--------------------------------------------------------------------------
  def process_draw_icon(icon_index, pos)
    eagle_auto_new_line(24, pos)
    if @flag_draw
      s = eagle_new_chara_sprite(pos[:x], pos[:y], 24, 24)
      s.eagle_font.draw_icon(s.bitmap, 0, 0, icon_index)
    end
    eagle_process_draw_end(24, 24, pos)
  end
  #--------------------------------------------------------------------------
  # ● 处理控制符指定的图片绘制
  #--------------------------------------------------------------------------
  def process_draw_pic(text, pos)
    param = text.slice!(/^\[.*?\]/)[1..-2]
    params = param.split('|') # [filename, param_str]
    begin
      _bitmap = Cache.picture(MESSAGE_EX.get_pic_file(params[0]))
    rescue
      p "【对话框扩展】未找到\\pic所需的Graphics/Pictures/#{params[0]}图片!"
      return
    end
    h = {}
    parse_param(h, params[1], :opa) if params[1]
    h[:w] ||= _bitmap.width
    h[:h] ||= _bitmap.height
    h[:opa] ||= 255
    eagle_auto_new_line(h[:w], pos)
    if @flag_draw
      s = eagle_new_chara_sprite(pos[:x], pos[:y], h[:w], h[:h])
      s.eagle_font.draw_pic(s.bitmap, _bitmap, h)
    end
    eagle_process_draw_end(h[:w], h[:h], pos)
  end
  #--------------------------------------------------------------------------
  # ● (封装)生成一个新的文字精灵
  #--------------------------------------------------------------------------
  def eagle_new_chara_sprite(c_x, c_y, c_w, c_h)
    f = Font_EagleCharacter.new(font_params)
    f.set_param(:skin, win_params[:skin])
    f.set_param(:ex_cg, ex_params[:cg])
    s = MESSAGE_EX.charapool_new(self, f, c_x, c_y, c_w, c_h, @eagle_chara_viewport)
    s.start_effects(game_message.chara_params)
    @eagle_chara_sprites.push(s) # 存入实时更新的文字数组
    @eagle_chara_sets[@eagle_current_set].push(s) if @eagle_current_set # 分组
    s
  end
  #--------------------------------------------------------------------------
  # ● 检查自动换行
  #--------------------------------------------------------------------------
  def eagle_auto_new_line(c_w, pos)
    return if func_params[:aw] == false
    return if @flag_draw == false
    return if !eagle_fix_w? && eagle_dynamic_w?
    max_w = eagle_charas_max_w
    return if max_w <= 0
    return if pos[:x] + c_w <= max_w # 若当前文字绘制完成后会超出边界,则换行
    process_new_line('', pos)
  end
  #--------------------------------------------------------------------------
  # ● 绘制完成时的处理
  #--------------------------------------------------------------------------
  def eagle_process_draw_end(c_w, c_h, pos)
    # 处理下一次绘制的参数
    pos[:x] += (c_w + game_message.win_params[:ck])
    pos[:height] = [pos[:height], c_h].max
    # 记录下一个文字绘制位置x
    @eagle_next_chara_x = pos[:x]
    # 处理文字区域大小更改
    @eagle_charas_w = pos[:x] if @eagle_charas_w < pos[:x]
    @eagle_charas_h = pos[:y] + pos[:height] if @eagle_charas_h < pos[:y] + pos[:height]
    return if !@flag_draw
    return if show_fast? # 如果是立即显示,则不更新
    eagle_process_draw_update
    wait_for_one_character
  end
  #--------------------------------------------------------------------------
  # ● 绘制完成时的更新
  #--------------------------------------------------------------------------
  def eagle_process_draw_update
    # 重设对话框宽高,并更新对话框位置
    eagle_set_wh({:ins => true})
    # 对齐需要用到对话框的宽高,因此在更新xywh后执行
    eagle_charas_reset_alignment(win_params[:ali])
    # 确保最后绘制的文字在视图区域内
    ensure_character_visible(@eagle_chara_sprites[-1])
  end
  #--------------------------------------------------------------------------
  # ● 重排列全部文字精灵
  #--------------------------------------------------------------------------
  def eagle_charas_reset_alignment(align)
    return if @eagle_chara_sprites.empty?
    charas = [] # 存储当前迭代行的全部文字精灵
    # 存储当前迭代行的y值(同y的为同一行)(未考虑列排文字)
    charas_y = @eagle_chara_sprites[0].origin_y  # 初始为第一行
    # 最大宽度 = [可供文字绘制区域的最大宽度, 文字占据宽度].max
    max_w = [eagle_charas_max_w, @eagle_charas_w].max
    @eagle_chara_sprites.each do |s|
      next charas.push(s) if s.origin_y == charas_y # 第一行的首字符会存入
      # 对同一行的字符重排
      eagle_charas_realign_line(charas, align, max_w)
      charas.clear
      # 将当前迭代的 下一行的首字符 存入
      charas.push(s)
      charas_y = s.origin_y
    end
    # 对最后一行进行重排列
    eagle_charas_realign_line(charas, align, max_w) if !charas.empty?
  end
  #--------------------------------------------------------------------------
  # ● 重排列同一行上的文字精灵
  #--------------------------------------------------------------------------
  def eagle_charas_realign_line(charas, align, max_w)
    w_line = charas[-1].origin_x - charas[0].origin_x + charas[-1].width
    h_line = charas.collect{ |c| c.height }.max
    charas.each do |c|
      case align
      when 0 # 左对齐(默认对齐方式)
        _x = c.origin_x
      when 1 # 居中排列
        _x = c.origin_x + (max_w - w_line) / 2
      when 2 # 右排列
        _x = c.origin_x + max_w - w_line
      end
      _y = c.origin_y + h_line - c.height # 底部对齐
      c.reset_xy(_x, _y)
    end
  end
  #--------------------------------------------------------------------------
  # ● 确保指定文字在视图内
  #--------------------------------------------------------------------------
  def ensure_character_visible(c, no_anim = false)
    return if c.nil?
    ox_1 = self.ox
    ox_d = 0
    ox_d = c._x - self.ox if c._x < self.ox
    d = c._x + c.width - @eagle_chara_viewport.rect.width - self.ox
    ox_d = d if d > 0

    oy_1 = self.oy
    oy_d = 0
    oy_d = c._y - self.oy if c._y < self.oy
    d = c._y + c.height - @eagle_chara_viewport.rect.height - self.oy
    oy_d = d if d > 0

    if !no_anim && @flag_draw && !@flag_open_close && (ox_d != 0 || oy_d != 0)
      # 因为是在新行的首字符绘制完成后调用该方法,因此先把这个字符隐藏了
      c.visible = false
      t = MESSAGE_EX::CHARAS_SCROLL_OUT_FRAME
      (t+1).times do |i|
        per = i * 1.0 / t
        per = MESSAGE_EX.ease_value(:msg_vp, per)
        self.ox = (ox_1 + ox_d * per).round if ox_d != 0
        self.oy = (oy_1 + oy_d * per).round if oy_d != 0
        update_moving_charas_oxy
        Fiber.yield
      end
      c.visible = true
    end
    self.ox = ox_1 + ox_d
    self.oy = oy_1 + oy_d
    update_moving_charas_oxy # 保证文字跟着contents一起移动
  end
  #--------------------------------------------------------------------------
  # ● 处于快进显示?
  #--------------------------------------------------------------------------
  def show_fast?
    @flag_instant || @show_fast || @line_show_fast
  end
  #--------------------------------------------------------------------------
  # ● 输出一个字符后的等待
  #--------------------------------------------------------------------------
  def wait_for_one_character
    MESSAGE_EX.se(game_message.win_params[:se])
    win_params[:cwi].times do
      return if show_fast?
      update_show_fast if win_params[:cfast]
      Fiber.yield
    end
  end
  #--------------------------------------------------------------------------
  # ● 监听“确定”键的按下,更新快进的标志
  #--------------------------------------------------------------------------
  def update_show_fast
    @show_fast = true if Input.trigger?(:C)
  end
  #--------------------------------------------------------------------------
  # ● 换行文字的处理(删去翻页)
  #  由于自动对齐的存在,无需预先计算当前行高,text参数无效
  #--------------------------------------------------------------------------
  def process_new_line(text, pos)
    pos[:height] += win_params[:ld] # 当前行增加一个行间距
    @line_show_fast = false
    pos[:x] = pos[:new_x]
    pos[:y] += pos[:height]
    pos[:height] = line_height
  end

  #--------------------------------------------------------------------------
  # ● 设置【预先】指令的参数
  #--------------------------------------------------------------------------
  def eagle_check_pre_settings(text)
    eagle_check_temp(text)
    eagle_check_env(text)
    eagle_check_hold(text)
    eagle_check_instant(text)
    eagle_check_close(text)
    eagle_check_next(text)
    eagle_check_eval(text)
    eagle_draw_face(text)
    eagle_draw_name(text)
    eagle_check_func(text)
  end
  #--------------------------------------------------------------------------
  # ● 设置/执行temp指令
  #--------------------------------------------------------------------------
  def eagle_check_temp(text)
    text.gsub!(/\e(temp)/i) { "" }
    @flag_temp = true if $1
  end
  def eagle_process_temp
    return if @flag_temp == false
    @flag_temp = false
    game_message.load_temp_env(@flag_temp_env, :eagle_temp)
    game_message.load_env(@flag_temp_env)
    game_message.env = @flag_temp_env
  end
  #--------------------------------------------------------------------------
  # ● 设置env指令
  #--------------------------------------------------------------------------
  def eagle_check_env(text)
    text.gsub!(/\eenv\[(.*?)\]/m) {
      t = $1; sym = t
      t.include?('|') ? sym = t.slice!(/.*?\|/).chop : t = "load"
      eagle_check_env_method(sym, t)
      ""
    }
  end
  def eagle_check_env_method(sym, m = "load")
    case m
    when "save"
      @flag_save_env = sym
    when "load"
      game_message.env = sym
      game_message.save_env(sym) if !game_message.load_env(sym)
    else
      p "对话框的 env 转义符参数错误!环境 #{sym} 与未定义的 #{m} 动作。"
    end
  end
  def eagle_process_env
    return if @flag_save_env == nil
    game_message.save_env(@flag_save_env)
    game_message.env = @flag_save_env
    @flag_save_env = nil
  end
  def eagle_reset_env  # 在 close 方法中调用
    v = MESSAGE_EX::S_ID_RESET_ENV
    if v == true || (v.is_a?(Integer) && $game_switches[v] == true)
      game_message.env = '0'
      game_message.load_env('0')
    end
  end
  #--------------------------------------------------------------------------
  # ● 设置/执行hold指令
  #--------------------------------------------------------------------------
  def eagle_check_hold(text)
    @flag_hold = false
    @flag_hold_str = ""
    text.gsub!(/\ehold(\[(.*?)\])/i) {
      @flag_hold = true
      @flag_hold_str = $1 ? $1.to_s : ""
      ""
    }
    # 移出已存在的具有该 flag_hold_id 的保留对话框
    if @flag_hold_str != ""
      @eagle_dup_windows.each_with_index do |w, i|
        w.close_clone if w.flag_hold_id == @flag_hold_str
      end
    end
  end
  def eagle_process_hold
    if @flag_hold
      t = self.clone
      t.flag_hold_id = @flag_hold_str
      # 重置之前存储的窗口的z值(确保最近的显示在最上面)
      @eagle_dup_windows.each_with_index do |w, i|
        next if t.close_clone?
        w.z = t.z - (i+1) * 5; w.eagle_reset_z
      end
      @eagle_dup_windows.unshift(t)
      self.openness = 0
      @flag_need_open = true
    else
      eagle_release_hold
    end
  end
  def eagle_release_hold # 所有暂存窗口关闭
    @eagle_dup_windows.each { |w| w.close_clone }
  end
  #--------------------------------------------------------------------------
  # ● 设置instant指令
  #--------------------------------------------------------------------------
  def eagle_check_instant(text)
    text.gsub!(/\e(ins)/i) { "" }
    @flag_instant = $1 ? true : false
  end
  #--------------------------------------------------------------------------
  # ● 设置close指令
  #--------------------------------------------------------------------------
  def eagle_check_close(text)
    text.gsub!(/\e(close)/i) { "" }
    @flag_need_close = $1 ? true : false
  end
  #--------------------------------------------------------------------------
  # ● 设置next指令
  #--------------------------------------------------------------------------
  def eagle_check_next(text)
    text.gsub!(/\e(next)/i) { "" }
    @flag_next = $1 ? true : false
  end
  #--------------------------------------------------------------------------
  # ● 设置eval指令
  #--------------------------------------------------------------------------
  def eagle_check_eval(text)
    @eagle_evals.clear # 清除旧的
    text.gsub!(/\eeval\{(.*?)\}/m) {
      @eagle_evals.push($1) # ID 从 1 开始,防止之后param传入nil出错
      "\eeval[#{@eagle_evals.size}]"
    }
    text.gsub!(/\{\{(.*?)\}\}/m) {
      @eagle_evals.push($1)
      "\eeval[#{@eagle_evals.size}]"
    }
  end

  #--------------------------------------------------------------------------
  # ● 分析【预先】转义符的全部参数(按出现顺序处理)
  #--------------------------------------------------------------------------
  def parse_pre_params(text, sym, hash, default_type = :default)
    params = []
    text.gsub!(/\e#{sym}\[(.*?)\]/i) { params.push($1); "" }
    params.push("") if params.empty?
    params.each { |param| parse_param(hash, param, default_type) }
  end
  #--------------------------------------------------------------------------
  # ● 初始化脸图
  #--------------------------------------------------------------------------
  def eagle_draw_face(text)
    face_params[:ls] = -1 # 设置循环开始编号(递增至le,再从ls循环)
    face_params[:le] = -1 # 设置循环结束编号
    parse_pre_params(text, 'facep', face_params, :dir)
    face_params[:dir] = MESSAGE_EX.check_bool(face_params[:dir])
    face_params[:m] = MESSAGE_EX.check_bool(face_params[:m])
    face_params[:name] = game_message.face_name
    face_params[:i] = game_message.face_index
    return eagle_move_out_face if face_params[:name] == ""
    if @eagle_sprite_face
      return if @eagle_sprite_face.no_change?
      eagle_move_out_face
    end
    @eagle_sprite_face = MESSAGE_EX.facepool_new
    @eagle_sprite_face.reset(self)
    @eagle_sprite_face.motion(:fade_in)
  end
  #--------------------------------------------------------------------------
  # ● 初始化姓名框
  #--------------------------------------------------------------------------
  def name_params; game_message.name_params; end
  def eagle_draw_name(text)
    # 用 | 分隔需要绘制的name字符串(其中转义符用<>代替[])和参数组
    str_name = ""
    text.gsub!(/\ename\[(.*?)\]/i) {
      t = $1.dup
      if t.include?('|')
        s = t.slice!(/.*?\|/).chop
        str_name = s if !s.empty?
        "\ename[#{t}]"
      else
        str_name = t
        ""
      end
    }
    parse_pre_params(text, 'name', name_params, :o)
    name_params[:name] = process_name_string(str_name)
    process_draw_name
  end
  def process_name_string(str)  # 预处理姓名字符串
    return "" if str == ""
    t = str
    # 特别的:如果str仅为数字,且为数据库中有效的角色ID,则将替换为其名称
    v = str.to_i
    t = $game_actors[v].name if v > 0 and $game_actors[v] != nil
    # 读取统一的前置文本
    t = MESSAGE_EX.get_name_prefix + t
    # 将转义符中的 <> 替换回 []
    t.gsub!(/<(.*?)>/) { "[" + $1 + "]" }
    # 处理转义符
    t = convert_escape_characters(t)
    return t
  end
  def process_draw_name
    @eagle_window_name.reset if @eagle_window_name
  end
  #--------------------------------------------------------------------------
  # ● 姓名框占用的宽度
  #--------------------------------------------------------------------------
  def eagle_name_width
    return 0 if !game_message.name?
    return 0 if name_params[:do] != 0  # 不是嵌入,则不占用
    return @eagle_window_name.contents.width + \
      @eagle_window_name.rect_real.x + @eagle_window_name.rect_real.width + \
      name_params[:dx]
  end
  #--------------------------------------------------------------------------
  # ● 姓名框占用的高度
  #--------------------------------------------------------------------------
  def eagle_name_height
    return 0 if !game_message.name?
    return 0 if name_params[:do] != 0  # 不是嵌入,则不占用
    return @eagle_window_name.contents.height + \
      @eagle_window_name.rect_real.y + @eagle_window_name.rect_real.height + \
      name_params[:dy] + standard_padding
  end
  #--------------------------------------------------------------------------
  # ● 设置func参数
  #--------------------------------------------------------------------------
  def func_params; game_message.func_params; end
  def eagle_check_func(text)
    parse_pre_params(text, 'func', func_params)
    func_params[:aw] = MESSAGE_EX.check_bool(func_params[:aw])
    func_params[:para] = MESSAGE_EX.check_bool(func_params[:para])
  end
  #--------------------------------------------------------------------------
  # ● 子窗口可以嵌入?
  #--------------------------------------------------------------------------
  def child_window_embed_in?
    open? && func_params[:para] == false
  end

  #--------------------------------------------------------------------------
  # ● 控制符的处理
  #     code : 控制符的实际形式(比如“\C[1]”是“C”)
  #     text : 绘制处理中的字符串缓存(字符串可能会被修改)
  #     pos  : 绘制位置 {:x, :y, :new_x, :height}
  #--------------------------------------------------------------------------
  def process_escape_character(code, text, pos)
    return if eagle_call_process_escape(code, text, pos)
    temp_code = code.downcase
    m_c = ("eagle_text_control_" + temp_code).to_sym
    m_e = ("eagle_chara_effect_" + temp_code).to_sym
    if respond_to?(m_c)
      param = obtain_escape_param_string(text)
      method(m_c).call(param)
    elsif respond_to?(m_e)
      param = obtain_escape_param_string(text)
      # 当只传入 0 时,代表关闭该特效
      return eagle_chara_effect_clear(temp_code.to_sym) if param == '0'
      game_message.chara_params[temp_code.to_sym] = param
      method(m_e).call(param)
    else
      super(code, text, pos)
    end
  end
  #--------------------------------------------------------------------------
  # ● 控制符的处理(方便扩展的编写方式)
  #     code : 控制符的实际形式(比如“\C[1]”是“C”)
  #     text : 绘制处理中的字符串缓存(字符串可能会被修改)
  #     pos  : 绘制位置 {:x, :y, :new_x, :height}
  #  返回 true 则代表转义符执行成功,否则继续匹配组件和默认转义符
  #--------------------------------------------------------------------------
  def eagle_call_process_escape(code, text, pos)
    c = code.upcase
    if c == '$' && @gold_window
      @gold_window.open? ? @gold_window.close : @gold_window.open
      return true
    end
    if c == '.'
      wait(15); return true
    end
    if c == '|'
      wait(60); return true
    end
    if c == '!'
      input_pause; return true
    end
    if c == '>'
      @line_show_fast = true; return true
    end
    if c == '<'
      @line_show_fast = false; return true
    end
    if c == '^'
      @pause_skip = true; return true
    end
    if c == 'C'
      font_params[:c] = obtain_escape_param_string(text)
      change_color(text_color(font_params[:c]))
      return true
    end
    if c == "PIC"
      process_draw_pic(text, pos)
      return true
    end
    if c == "CLC"
      process_pos_clc(text, pos)
      return true
    end
    return false
  end
  #--------------------------------------------------------------------------
  # ● (覆盖)获取控制符的实际形式(这个方法会破坏原始数据)
  #--------------------------------------------------------------------------
  def obtain_escape_code(text)
    text.slice!(/^[\$\.\|\^!><\{\}\\]|^[\d\w]+/i)
  end
  #--------------------------------------------------------------------------
  # ● 获取控制符的参数(变量参数字符串形式)(这个方法会破坏原始数据)
  #--------------------------------------------------------------------------
  def obtain_escape_param_string(text)
    text.slice!(/^\[[ =\|\$\-\d\w]+\]/)[1..-2] rescue ""
  end
  #--------------------------------------------------------------------------
  # ● 清除暂存的指定文字特效
  #--------------------------------------------------------------------------
  def eagle_chara_effect_clear(code_sym)
    game_message.chara_params.delete(code_sym)
  end
  #--------------------------------------------------------------------------
  # ● 设置\clc清屏效果
  #--------------------------------------------------------------------------
  def process_pos_clc(text, pos)
    text[0] = '' if text[0] == "\n"
    process_new_line(text, pos)

    oy_1 = self.oy
    oy_d = pos[:y]
    if @flag_draw
      t = MESSAGE_EX::CLC_CHARAS_OUT_FRAME
      (t+1).times do |i|
        per = i * 1.0 / t
        per = MESSAGE_EX.ease_value(:msg_vp, per)
        self.oy = (oy_1 + oy_d * per).round
        update_moving_charas_oxy
        Fiber.yield
      end
    end
    self.oy = oy_1 + oy_d
    update_moving_charas_oxy
  end

  #--------------------------------------------------------------------------
  # ● 设置font参数
  #--------------------------------------------------------------------------
  def font_params; game_message.font_params; end
  def eagle_text_control_font(param = "")
    parse_param(font_params, param, :size)
    change_color(text_color(font_params[:c]))
    MESSAGE_EX.apply_font_params(self.contents.font, font_params)
  end
  #--------------------------------------------------------------------------
  # ● (覆盖)重置字体设置
  #--------------------------------------------------------------------------
  def reset_font_settings
    font_params[:c] = MESSAGE_EX::DEFAULT_COLOR_INDEX
    font_params[:ca] = 255
    change_color(text_color(font_params[:c]))
  end
  #--------------------------------------------------------------------------
  # ● (覆盖)放大字体尺寸
  #--------------------------------------------------------------------------
  def make_font_bigger
    self.contents.font.size += 4 if self.contents.font.size <= 64
    font_params[:size] = self.contents.font.size
  end
  #--------------------------------------------------------------------------
  # ● (覆盖)缩小字体尺寸
  #--------------------------------------------------------------------------
  def make_font_smaller
    self.contents.font.size -= 4 if self.contents.font.size >= 16
    font_params[:size] = self.contents.font.size
  end

  #--------------------------------------------------------------------------
  # ● 设置win参数
  #--------------------------------------------------------------------------
  def win_params; game_message.win_params; end
  def eagle_text_control_win(param = "")
    parse_param(win_params, param, :o)
    win_params[:hmin] = eagle_check_param_h(win_params[:hmin])
    win_params[:hmax] = eagle_check_param_h(win_params[:hmax])
    win_params[:dw] = MESSAGE_EX.check_bool(win_params[:dw])
    win_params[:fw] = MESSAGE_EX.check_bool(win_params[:fw])
    win_params[:dh] = MESSAGE_EX.check_bool(win_params[:dh])
    win_params[:fh] = MESSAGE_EX.check_bool(win_params[:fh])
    win_params[:cwi] = 0 if win_params[:cwi] < 0
    win_params[:cwo] = 0 if win_params[:cwo] < 0
    win_params[:cfast] = MESSAGE_EX.check_bool(win_params[:cfast])
    win_params[:fix] = MESSAGE_EX.check_bool(win_params[:fix])
    eagle_reset_z
  end
  #--------------------------------------------------------------------------
  # ● (覆盖)获取基础行高
  #--------------------------------------------------------------------------
  def line_height
    h = win_params[:lh] || Font.default_size
    return h if h > 0
    return super
  end

  #--------------------------------------------------------------------------
  # ● 设置pop参数
  #--------------------------------------------------------------------------
  def pop_params; game_message.pop_params; end
  def eagle_text_control_pop(param = "")
    pop_params[:id] = nil # 重置绑定对象
    pop_params[:mx] = nil
    pop_params[:my] = nil
    parse_param(pop_params, param, :id)
    pop_params[:type] = nil # 清除可能的误设置
    @eagle_pop_obj = eagle_get_pop_obj # 获取所绑定的对象
    return pop_params[:type] = nil if @eagle_pop_obj.nil?
    s = eagle_get_pop_sprite # 获取所绑定对象的精灵
    return pop_params[:type] = nil if s.nil?
    eagle_set_pop_sprite_info(s)
    pop_params[:dw] = MESSAGE_EX.check_bool(pop_params[:dw])
    pop_params[:fw] = MESSAGE_EX.check_bool(pop_params[:fw])
    pop_params[:dh] = MESSAGE_EX.check_bool(pop_params[:dh])
    pop_params[:fh] = MESSAGE_EX.check_bool(pop_params[:fh])
    pop_params[:fix] = MESSAGE_EX.check_bool(pop_params[:fix])
    pop_params[:with_tag] = show_pop_tag? # 设置pop的tag
    eagle_reset_pop_tag_bitmap if pop_params[:with_tag]
    eagle_pop_update
    @eagle_sprite_pop_tag.opacity = 255
    @eagle_sprite_pop_tag.visible = pop_params[:with_tag]
  end
  #--------------------------------------------------------------------------
  # ● 获取pop的弹出对象(需要有x、y、width、height方法)
  #--------------------------------------------------------------------------
  def eagle_get_pop_obj
    if pop_params[:id]
      return eagle_get_pop_obj_m if MESSAGE_EX.in_scene?(:map)
      return eagle_get_pop_obj_b if MESSAGE_EX.in_scene?(:battle)
    end
    return eagle_get_pop_obj_ex
  end
  #--------------------------------------------------------------------------
  # ● 获取pop的对象(地图场景中)(Game_Character的实例)
  #--------------------------------------------------------------------------
  def eagle_get_pop_obj_m
    pop_params[:type] = :map_chara
    id = pop_params[:id]
    if id == 0 # 当前事件
      return $game_map.events[game_message.event_id]
    elsif id > 0 # 第id号事件
      chara = $game_map.events[id]
      chara ||= $game_map.events[game_message.event_id]
      return chara
    elsif id < 0 # 队伍中数据库id号角色(不存在则取队长)
      id = id.abs
      $game_player.followers.each { |f|
        return f if f.actor && f.actor.actor.id == id
      }
      return $game_player
    end
  end
  #--------------------------------------------------------------------------
  # ● 获取pop的对象(战斗场景中)(Sprite_Battler的实例)
  #--------------------------------------------------------------------------
  def eagle_get_pop_obj_b
    pop_params[:type] = :battle_sprite
    id = pop_params[:id]
    return nil if id.nil?
    if id > 0 # 敌人index
      SceneManager.scene.spriteset.battler_sprites.each do |s|
        return s if s.battler && s.battler.enemy? && s.battler.index == id-1
      end
    elsif id < 0 # 我方数据库id
      id = id.abs
      SceneManager.scene.spriteset.battler_sprites.each do |s|
        return s if s.battler && s.battler.actor? && s.battler.id == id
      end
    end
    return nil
  end
  #--------------------------------------------------------------------------
  # ● 获取pop的对象(无 id 设置时)
  #--------------------------------------------------------------------------
  def eagle_get_pop_obj_ex
    if MESSAGE_EX.in_scene?(:map) && pop_params[:mx] && pop_params[:my]
      pop_params[:type] = :map_grid
      return [pop_params[:mx], pop_params[:my]]
    end
    return nil
  end
  #--------------------------------------------------------------------------
  # ● 获取pop对象的精灵(用于计算偏移值)
  #--------------------------------------------------------------------------
  def eagle_get_pop_sprite
    # 地图场景中,所存储的并非精灵,需要再次检索
    if pop_params[:type] == :map_chara
      begin
        SceneManager.scene.spriteset.character_sprites.each do |s|
          return s if s.character == @eagle_pop_obj
        end
      rescue
        p "【对话框扩展 by老鹰】未找到pop转义符绑定事件的精灵!可能存在兼容问题!"
      end
      return nil
    end
    return @eagle_pop_obj
  end
  #--------------------------------------------------------------------------
  # ● 存储用于pop更新的精灵对象的信息
  #--------------------------------------------------------------------------
  def eagle_set_pop_sprite_info(s)
    if s.is_a?(Sprite)
      pop_params[:chara_w] = s.width
      pop_params[:chara_h] = s.height
    end
    if pop_params[:type] == :map_grid
      pop_params[:chara_w] = 32
      pop_params[:chara_h] = 32
    end
  end
  #--------------------------------------------------------------------------
  # ● 可以显示pop的tag?
  #--------------------------------------------------------------------------
  def show_pop_tag?
    game_message.pop_tag? && @background == 0 &&
    pop_params[:do] > 0 && pop_params[:do] < 10
  end
  #--------------------------------------------------------------------------
  # ● 重置tag的位图
  #--------------------------------------------------------------------------
  def eagle_reset_pop_tag_bitmap(tag_id = pop_params[:tag])
    @eagle_sprite_pop_tag.bitmap = MESSAGE_EX.windowtag(tag_id)
    w = @eagle_sprite_pop_tag.bitmap.width
    h = @eagle_sprite_pop_tag.bitmap.height
    @eagle_sprite_pop_tag.src_rect.width = w / 3
    @eagle_sprite_pop_tag.src_rect.height = h / 3
  end

  #--------------------------------------------------------------------------
  # ● 设置face参数
  #--------------------------------------------------------------------------
  def face_params; game_message.face_params; end
  def eagle_text_control_face(param = "")
    return if !@flag_draw
    face_params[:ls] = -1 # 设置循环开始编号(+1直至le,再从ls循环)
    face_params[:le] = -1 # 设置循环结束编号
    parse_param(face_params, param, :i)
    @eagle_sprite_face.apply_face_params if @eagle_sprite_face
  end
  #--------------------------------------------------------------------------
  # ● 执行facem
  #--------------------------------------------------------------------------
  def eagle_text_control_facem(param = "")
    return if !@flag_draw
    return if @eagle_sprite_face == nil
    params = param.split('|')
    @eagle_sprite_face.motion(params[0], params[1] || "")
  end
  #--------------------------------------------------------------------------
  # ● 移出脸图
  #--------------------------------------------------------------------------
  def eagle_move_out_face
    return if @eagle_sprite_face.nil?
    @eagle_sprite_face.motion(:fade_out)
    MESSAGE_EX.facepool_push(@eagle_sprite_face) # 由精灵池接管
    @eagle_sprite_face = nil
  end
  #--------------------------------------------------------------------------
  # ● 脸图占用的宽度
  #--------------------------------------------------------------------------
  def eagle_face_width
    return 0 if !game_message.face?
    return 0 if face_params[:z] < 0
    face_params[:width] + face_params[:dw]
  end
  #--------------------------------------------------------------------------
  # ● 脸图在左侧占用的宽度(用于调整文字区域的左侧起始位置)
  #--------------------------------------------------------------------------
  def eagle_face_left_width
    return 0 if face_params[:dir] # 显示在右侧时
    v = eagle_face_width + face_params[:dx]
    [v, 0].max
  end
  #--------------------------------------------------------------------------
  # ● 脸图在右侧占用的宽度
  #--------------------------------------------------------------------------
  def eagle_face_right_width
    return 0 if !face_params[:dir] # 显示在左侧时
    v = eagle_face_width - face_params[:dx]
    [v, 0].max
  end
  #--------------------------------------------------------------------------
  # ● 脸图占用的高度
  #--------------------------------------------------------------------------
  def eagle_face_height
    face_params[:height] || 0
  end
  #--------------------------------------------------------------------------
  # ● 保证脸图完全显示在对话框内?
  #--------------------------------------------------------------------------
  def eagle_face_in_window?
    return false if @eagle_sprite_face == nil
    return MESSAGE_EX::FORCE_WIN_H_BIGGER_THAN_DEFAULT_FACE &&
      @eagle_sprite_face.face_default_size?
  end

  #--------------------------------------------------------------------------
  # ● 设置pause参数
  #--------------------------------------------------------------------------
  def pause_params; game_message.pause_params; end
  def eagle_text_control_pause(param = "")
    parse_param(pause_params, param, :id)
    @eagle_sprite_pause.reset if @eagle_sprite_pause
  end
  #--------------------------------------------------------------------------
  # ● 设置wait参数
  #--------------------------------------------------------------------------
  def eagle_text_control_wait(param = '0')
    wait(param.to_i)
  end
  #--------------------------------------------------------------------------
  # ● (覆盖)等待
  #--------------------------------------------------------------------------
  def wait(duration)
    return if !@flag_draw
    # 如果在对话快速显示的中途进行等待,则执行一次绘制后处理,以进行排版等
    eagle_process_draw_update if show_fast?
    duration.times { break if @eagle_force_close; Fiber.yield }
  end
  #--------------------------------------------------------------------------
  # ● 设置auto参数
  #--------------------------------------------------------------------------
  def eagle_text_control_auto(param = '0')
    win_params[:auto_t] = param.to_i
  end
  #--------------------------------------------------------------------------
  # ● 执行shake
  #--------------------------------------------------------------------------
  def eagle_text_control_shake(param = '0')
    return if !@flag_draw
    h = {}
    h[:p] = 5 # shake power
    h[:s] = 5 # shake speed
    h[:t] = 40 # shake duration
    h[:y] = 0 # 震动方向
    parse_param(h, param, :t)
    # 等待震动至结束
    shake = 0 # 对话框的偏移值
    shake_direction = 1 # 下一次位移量
    while h[:t] > 0
      delta = (h[:p] * h[:s] * shake_direction) / 10.0
      shake += delta
      shake_direction = -1 if shake > h[:p] * 2
      shake_direction = 1 if shake < - h[:p] * 2
      h[:t] -= 1
      if h[:y] == 0
        self.x += shake
      else
        self.y += shake
      end
      eagle_after_update_xy
      Fiber.yield
    end
    shake = shake.to_i # 平滑移动回初始位置
    d = shake > 0 ? -1 : 1
    while shake != 0
      shake += d
      if h[:y] == 0
        self.x += shake
      else
        self.y += shake
      end
      eagle_after_update_xy
      Fiber.yield
    end
  end
  #--------------------------------------------------------------------------
  # ● 执行move指令
  #--------------------------------------------------------------------------
  def eagle_text_control_move(param = '0')
    return if !@flag_draw
    return if win_params[:do] < 0 || game_message.pop?
    win_params[:move_init_x] ||= (win_params[:x] || default_init_x)
    win_params[:move_init_y] ||= (win_params[:y] || default_init_y)

    h = { :x => nil, :y => nil, :dx => nil, :dy => nil, :t => 20 }
    parse_param(h, param, :t)
    _x = nil; _y = nil
    if h[:x]
      _x = (h[:x] == 0 ? win_params[:move_init_x] : h[:x])
    else
      _x = self.x + h[:dx] if h[:dx]
    end
    if h[:y]
      _y = (h[:y] == 0 ? win_params[:move_init_y] : h[:y])
    else
      _y = self.y + h[:dy] if h[:dy]
    end
    # 直接重设目的地xy
    win_params[:x] = _x if _x
    win_params[:y] = _y if _y
    # 执行移动
    h2 = {}
    h2[:x] = _x if _x
    h2[:y] = _y if _y
    h2[:t] = h[:t]
    eagle_set_wh(h2)
  end
  #--------------------------------------------------------------------------
  # ● 执行eval指令
  #--------------------------------------------------------------------------
  def eagle_text_control_eval(param = '0')
    return if !@flag_draw
    id_ = param.to_i
    if id_ > 0 && @eagle_evals[id_ - 1]
      s = $game_switches; v = $game_variables; msg = self
      eval( @eagle_evals[id_ - 1] )
    end
  end
  #--------------------------------------------------------------------------
  # ● 设置set参数
  #--------------------------------------------------------------------------
  def eagle_text_control_set(param = '0')
    return if !@flag_draw
    return @eagle_current_set = nil if param == '0'
    @eagle_current_set = param
    @eagle_chara_sets[@eagle_current_set] ||= []
  end
  def eagle_text_control_setm(param = '0')
    return if !@flag_draw
    params = param.split('|') # [set_sym, effect_sym, param]
    if params[2] == "0"
      chara_set(params[0]) { |s| s.finish_effect(params[1].to_sym) }
    else
      chara_set(params[0]) { |s| s.start_effect(params[1].to_sym, params[2]) }
    end
  end
  #--------------------------------------------------------------------------
  # ● 对指定分组内的文字执行block
  #   传入空字符串或 0 时为全部文字
  #--------------------------------------------------------------------------
  def chara_set(set_sym = nil) # block
    if set_sym.nil? || set_sym.empty? || set_sym == 0 || set_sym == '0'
      @eagle_chara_sprites.each { |s| yield(s) }
      return
    end
    return if @eagle_chara_sets[set_sym].nil?
    @eagle_chara_sets[set_sym].each { |s| yield(s) }
  end
  #--------------------------------------------------------------------------
  # ● 设置扩展参数
  #--------------------------------------------------------------------------
  def ex_params; game_message.ex_params; end
  #--------------------------------------------------------------------------
  # ● 设置cg参数 / 渐变绘制预定
  #--------------------------------------------------------------------------
  if defined?(Sion_GradientText)
  def eagle_text_control_cg(param = '0')
    ex_params[:cg].clear
    ex_params[:cg] = param if param != '' && param != '0'
  end
  end

  #--------------------------------------------------------------------------
  # ● 输入处理(此处为全部绘制完成后,判定接下来的输入类型)
  #--------------------------------------------------------------------------
  def process_input
    if game_message.choice?
      input_choice
    elsif game_message.num_input?
      input_number
    elsif game_message.item_choice?
      input_item
    elsif input_pause?
      input_pause
    end
    eagle_process_hold
  end
  #--------------------------------------------------------------------------
  # ● 需要输入等待?
  #--------------------------------------------------------------------------
  def input_pause?
    game_message.input_pause? && !@pause_skip
  end
  #--------------------------------------------------------------------------
  # ● 处理输入等待
  #--------------------------------------------------------------------------
  def input_pause
    return if !@flag_draw
    before_input_pause
    eagle_process_draw_update # 统一更新一次
    @eagle_sprite_pause.bind_last_chara(@eagle_chara_sprites[-1])
    @eagle_sprite_pause.show
    @flag_input_pause = true
    self.pause = true unless MESSAGE_EX::NO_DEFAULT_PAUSE
    process_input_pause
    self.pause = false
    @flag_input_pause = false
    @eagle_sprite_pause.hide
  end
  #--------------------------------------------------------------------------
  # ● 输入等待前的操作
  #--------------------------------------------------------------------------
  def before_input_pause
    # 当pause精灵位于句末且紧靠边界时
    #  增加对话框宽度保证它在对话框内部(不可占用padding)
    if pause_params[:v] != 0 && pause_params[:do] <= 0 &&
       input_pause? && eagle_add_w_by_child_window?
      # 最大可用于文字绘制的宽度 eagle_charas_max_w
      # 全部文字实际绘制的宽度 @eagle_charas_w_final + win_params[:cdw]
      # 最后一行所需的绘制宽度 @eagle_next_chara_x
      if @eagle_next_chara_x >= @eagle_charas_w_final
        @eagle_sprite_pause_width_add = @eagle_sprite_pause.width
      else
        d = @eagle_charas_w_final + win_params[:cdw] - @eagle_next_chara_x
        d -= @eagle_sprite_pause.width
        @eagle_sprite_pause_width_add = -d if d <= 0
      end
    else
      @eagle_sprite_pause_width_add = 0
    end
  end
  #--------------------------------------------------------------------------
  # ● 执行输入等待
  #--------------------------------------------------------------------------
  def process_input_pause
    @eagle_auto_continue_c = win_params[:auto_t] || MESSAGE_EX::WIN_AUTO_T
    recreate_contents_for_charas
    ox_max = [self.ox, @eagle_charas_w - @eagle_chara_viewport.rect.width].max
    oy_max = self.oy
    flag_move = ox_max != 0 || oy_max != 0
    self.arrows_visible = true
    d_oxy = 1; last_input = nil; last_input_c = 0
    @flag_input_loop = true
    while @flag_input_loop
      Fiber.yield
      process_input_pause_auto # 处理自动继续
      process_input_pause_key # 处理按键继续
      if flag_move # 处理内容滚动
        if last_input == Input.dir4  # 先处理变速,防止同时按多个方向,导致速度不对
          last_input_c += 1
          d_oxy += 1 if last_input_c % 10 == 0
        else
          d_oxy = 1
          last_input_c = 0
        end
        last_input = Input.dir4
        _ox = self.ox; _oy = self.oy
        if Input.press?(:UP)
          self.oy -= d_oxy
          self.oy = 0 if self.oy < 0
        elsif Input.press?(:DOWN)
          self.oy += d_oxy
          self.oy = oy_max if self.oy > oy_max
        elsif Input.press?(:LEFT)
          self.ox -= d_oxy
          self.ox = 0 if self.ox < 0
        elsif Input.press?(:RIGHT)
          self.ox += d_oxy
          self.ox = ox_max if self.ox > ox_max
        end
        update_moving_charas_oxy if _ox != self.ox || _oy != self.oy
      elsif MESSAGE_EX::INPUT_NEXT_WITH_DIR4
        @flag_input_loop = false if Input.dir4 > 0
      end
    end
    self.arrows_visible = false
    Fiber.yield
  end
  #--------------------------------------------------------------------------
  # ● 等待按键时,自动继续的处理
  #--------------------------------------------------------------------------
  def process_input_pause_auto
    if @eagle_auto_continue_c
      return @flag_input_loop = false if @eagle_auto_continue_c <= 0
      process_while_auto_wait_input_pause(@eagle_auto_continue_c)
      @eagle_auto_continue_c -= 1
    end
  end
  #--------------------------------------------------------------------------
  # ● 等待按键时,启用auto时的额外处理
  #  c 为自动继续的剩余帧数,从 win_params[:auto_t] 获取总等待帧数
  #--------------------------------------------------------------------------
  def process_while_auto_wait_input_pause(c)
    @eagle_sprite_pause.redraw_auto_countdown(c)
  end
  #--------------------------------------------------------------------------
  # ● 等待按键时,按键继续的处理
  #--------------------------------------------------------------------------
  def process_input_pause_key
    return @flag_input_loop = false if check_input?
  end
  #--------------------------------------------------------------------------
  # ● 检查输入等待的按键
  #--------------------------------------------------------------------------
  def check_input?
    Input.trigger?(:B) || Input.trigger?(:C)
  end
  #--------------------------------------------------------------------------
  # ● 处理选项的输入(覆盖)
  #--------------------------------------------------------------------------
  def input_choice
    input_wait_until_msg_wh(@choice_window)
    input_wait_while_active(@choice_window)
  end
  #--------------------------------------------------------------------------
  # ● 处理数值的输入(覆盖)
  #--------------------------------------------------------------------------
  def input_number
    input_wait_until_msg_wh(@number_window)
    input_wait_while_active(@number_window)
  end
  #--------------------------------------------------------------------------
  # ● 处理物品的选择(覆盖)
  #--------------------------------------------------------------------------
  def input_item
    input_wait_until_msg_wh(@item_window)
    input_wait_while_active(@item_window)
  end
  #--------------------------------------------------------------------------
  # ● 等待对话框宽高处理结束
  #--------------------------------------------------------------------------
  def input_wait_until_msg_wh(child_window)
    child_window.hide.start
    eagle_set_wh if child_window_embed_in? # 执行因子窗口嵌入而变更的窗口大小
    child_window.show.open.activate
  end
  #--------------------------------------------------------------------------
  # ● 并行等待子窗口处理结束
  #--------------------------------------------------------------------------
  def input_wait_while_active(child_window)
    add_w = game_message.child_window_w_des
    add_h = game_message.child_window_h_des
    while child_window.active
      break child_window.deactivate.close if @eagle_force_close
      @fiber_para.resume if @fiber_para
      if add_w != game_message.child_window_w_des ||
         add_h != game_message.child_window_h_des
        eagle_set_wh # 执行因子窗口嵌入而变更的窗口大小
        add_w = game_message.child_window_w_des
        add_h = game_message.child_window_h_des
      end
      Fiber.yield
    end
    @fiber_para = nil
  end
end # end of class Window_EagleMessage

#=============================================================================
# ○ 对话框拷贝
#=============================================================================
class Window_EagleMessage_Clone < Window_EagleMessage
  attr_accessor :back_bitmap, :back_sprite
  attr_accessor :eagle_chara_viewport
  attr_accessor :eagle_chara_sprites, :eagle_sprite_pop_tag
  attr_accessor :eagle_sprite_face, :eagle_window_name, :eagle_sprite_pause
  attr_accessor :eagle_pop_obj
  attr_accessor :flag_hold_id
  #--------------------------------------------------------------------------
  # ● 初始化对象
  #--------------------------------------------------------------------------
  def initialize(game_message)
    @game_message = game_message
    super()
    self.openness = 255
    @fin = false # 结束显示?
    @flag_hold_id = nil
  end
  #--------------------------------------------------------------------------
  # ● 获取主参数
  #--------------------------------------------------------------------------
  def game_message; @game_message; end
  #--------------------------------------------------------------------------
  # ● 初始化组件(覆盖,不再初始化,防止在赋值前还要dispose)
  #--------------------------------------------------------------------------
  def eagle_message_init_assets; end
  #--------------------------------------------------------------------------
  # ● (覆盖)去除全部子窗口
  #--------------------------------------------------------------------------
  def create_all_windows; end
  def dispose_all_windows; end
  def update_all_windows; end
  #--------------------------------------------------------------------------
  # ● (覆盖)判定是否所有窗口已全部关闭
  #--------------------------------------------------------------------------
  def all_close?; close?; end
  #--------------------------------------------------------------------------
  # ● 设置xywh
  #--------------------------------------------------------------------------
  def move(x, y, w, h)
    super
    @eagle_win_des_w = w  # 同时记录该变量,防止nil时 default_init_y 报错
    @eagle_win_des_h = h
    @eagle_last_x = x # 同时设置上次更新的位置,确保 eagle_set_wh 能用
    @eagle_last_y = y
  end
  #--------------------------------------------------------------------------
  # ○ 记录文本的宽高(用于更新大小)
  #--------------------------------------------------------------------------
  def eagle_set_chara_wh(w, h, w_final, h_final)
    @eagle_charas_w = w
    @eagle_charas_h = h
    @eagle_charas_w_final = w_final
    @eagle_charas_h_final = h_final
  end
  #--------------------------------------------------------------------------
  # ○ 关闭对话框
  #--------------------------------------------------------------------------
  def close_clone
    @fin = true
  end
  #--------------------------------------------------------------------------
  # ○ 已经计划关闭?
  #--------------------------------------------------------------------------
  def close_clone?
    @fin == true
  end
  #--------------------------------------------------------------------------
  # ● 重置单页对话框(覆盖,防止过早移出组件)
  #--------------------------------------------------------------------------
  def eagle_message_reset
    @eagle_sprite_pause_width_add = 0 # 拷贝窗口中不存在pause精灵
  end
  #--------------------------------------------------------------------------
  # ● 更新纤程
  #--------------------------------------------------------------------------
  def update_fiber
    if @fiber
      @fiber.resume
    elsif self.openness >= 255 && !@fin
      @fiber = Fiber.new { fiber_main }
      @fiber.resume
    end
  end
  #--------------------------------------------------------------------------
  # ● 处理纤程的主逻辑
  #--------------------------------------------------------------------------
  def fiber_main
    eagle_set_wh( {:open => true} ) # 由于pause精灵需要去除,增加更新宽高
    loop do
      Fiber.yield
      break if @fin
    end
    eagle_process_before_close
    close_and_wait
    @fiber = nil
  end
end
#=============================================================================
# ○ 对话框拷贝 - 用于环境初始化
#=============================================================================
class Window_EagleMessage_CloneEnv < Window_EagleMessage
  #--------------------------------------------------------------------------
  # ● 初始化对象
  #--------------------------------------------------------------------------
  def initialize(game_message)
    @game_message = game_message
    super()
    self.openness = 0
  end
  #--------------------------------------------------------------------------
  # ● 获取主参数
  #--------------------------------------------------------------------------
  def game_message; @game_message; end
  def game_message=(g); @game_message = g; end

  #--------------------------------------------------------------------------
  # ● 初始化组件(覆盖,不再初始化,防止在赋值前还要dispose)
  #--------------------------------------------------------------------------
  def eagle_message_init_assets; end
  #--------------------------------------------------------------------------
  # ● (覆盖)去除全部子窗口
  #--------------------------------------------------------------------------
  def create_all_windows; end
  def dispose_all_windows; end
  def update_all_windows; end
  #--------------------------------------------------------------------------
  # ● (覆盖)重置对话框
  #--------------------------------------------------------------------------
  def eagle_message_reset; end
  #--------------------------------------------------------------------------
  # ● 重设z值
  #--------------------------------------------------------------------------
  def eagle_reset_z; end
  #--------------------------------------------------------------------------
  # ● (覆盖)更新
  #--------------------------------------------------------------------------
  def update; end
  #--------------------------------------------------------------------------
  # ● 设置game_message
  #  本质上为全部绘制一次(但不真实绘制)
  #  空方法为去除对组件的设置
  #--------------------------------------------------------------------------
  def set_game_message(game_message, text)
    @game_message = game_message
    text_ = text.clone
    text_ = convert_escape_characters(text_)
    pos_  = { :x => 0, :y => 0, :new_x => 0, :height => 0}
    @flag_draw = false
    process_character(text_.slice!(0, 1), text_, pos_) until text_.empty?
  end
  #--------------------------------------------------------------------------
  # ● 绘制完成时的处理
  #--------------------------------------------------------------------------
  def eagle_process_draw_end(c_w, c_h, pos); end
  #--------------------------------------------------------------------------
  # ● 可以显示pop的tag?
  #--------------------------------------------------------------------------
  def show_pop_tag?; false; end
end

#=============================================================================
# ○ 金钱框窗口
#=============================================================================
class Window_EagleMsgGold < Window_Gold
  #--------------------------------------------------------------------------
  # ● 初始化对象
  #--------------------------------------------------------------------------
  def initialize(window_msg)
    bind_window(window_msg)
    super()
    self.x = Graphics.width - self.width
    self.y = 0
    self.openness = 0
  end
  def bind_window(window); @window_msg = window; end
end

#=============================================================================
# ○ 姓名框窗口
#=============================================================================
class Window_EagleMsgName < Window_Base
  attr_reader  :rect_real
  #--------------------------------------------------------------------------
  # ● 获取文字颜色
  #     n : 文字颜色编号(0..31)
  #--------------------------------------------------------------------------
  def text_color(n)
    MESSAGE_EX.text_color(n, self.windowskin)
  end
  #--------------------------------------------------------------------------
  # ● 初始化对象
  #--------------------------------------------------------------------------
  def initialize(window_msg)
    bind_window(window_msg)
    super(0, 0, 32, 32)
    self.visible = false # 从 opacity=0 修改为 visible,确保不会一瞬间出现背景框
    @back_sprite = Sprite.new
    @flag_use_back_sprite = false
    @params = {}
    # 在背景图片的影响下,姓名框四个方向的增加量
    @rect_real = Rect.new(0, 0, 0, 0)
  end
  def bind_window(window); @window_msg = window; end
  def name_params; @window_msg.name_params; end
  #--------------------------------------------------------------------------
  # ● 获取行高
  #--------------------------------------------------------------------------
  def line_height; name_params[:size] || Font.default_size; end
  #--------------------------------------------------------------------------
  # ● 姓名没有变化?
  #--------------------------------------------------------------------------
  def no_change?
    @params[:name] == name_params[:name]
  end
  #--------------------------------------------------------------------------
  # ● 开始
  #--------------------------------------------------------------------------
  def start
    if @flag_use_back_sprite
      # 显示背景图片时,隐藏默认窗口皮肤
      @back_sprite.visible = true
      self.opacity = 0
      self.back_opacity = 0
    else
      @back_sprite.visible = false
      self.opacity = name_params[:opa]
      self.back_opacity = name_params[:opa]
      if name_params[:do] == 0  # 嵌入时,隐藏背景框
        self.opacity = 0
        self.back_opacity = 0
      end
    end
    self.contents_opacity = 255
    self.openness = 255 if self.back_opacity == 0 # 没有背景框,则省略open过程
    self.show.open
  end
  #--------------------------------------------------------------------------
  # ● 重置
  #--------------------------------------------------------------------------
  def reset
    return close if name_params[:name] == ""
    skin = @window_msg.get_cur_windowskin_index(name_params[:skin])
    self.windowskin = MESSAGE_EX.windowskin(skin)
    return if open? && no_change?
    @params[:name] = name_params[:name]
    reset_size(name_params[:name])
    redraw(name_params[:name])
    if name_params[:bg] && eagle_draw_bg_pic(self.width, self.height)
      @flag_use_back_sprite = true
      @back_sprite.z = self.z - 1
      @back_sprite.opacity = 0  # 在更新前,先确保背景图片不显示
      @back_sprite.visible = false
    else
      @flag_use_back_sprite = false
    end
    reset_rect_real
    #open # 等待对话框中调用 start 方法来显示姓名框
  end
  #--------------------------------------------------------------------------
  # ● 重设窗口大小
  #--------------------------------------------------------------------------
  def reset_size(t)
    reset_font_settings
    w, h = MESSAGE_EX.calculate_text_wh(contents, t)
    h = [h, contents.font.size].max
    w = w + name_params[:cx]
    h = h + name_params[:cy]
    move(0, 0, w + standard_padding * 2, h + standard_padding * 2)
    create_contents
  end
  #--------------------------------------------------------------------------
  # ● 重绘
  #--------------------------------------------------------------------------
  def redraw(t)
    change_color(text_color(@window_msg.font_params[:c]))
    MESSAGE_EX.apply_font_params(contents.font, @window_msg.font_params)
    draw_text_ex(name_params[:cx], name_params[:cy], t)
  end
  #--------------------------------------------------------------------------
  # ● 重置字体设置
  #--------------------------------------------------------------------------
  def reset_font_settings
    change_color(normal_color)
    contents.font.size = name_params[:size] || Font.default_size
  end
  #--------------------------------------------------------------------------
  # ● 获取控制符的参数(这个方法会破坏原始数据)
  #--------------------------------------------------------------------------
  def obtain_escape_param_string(text)
    text.slice!(/^\[[\|\$\-\d\w]+\]/)[1..-2] rescue ""
  end
  #--------------------------------------------------------------------------
  # ● 控制符的处理
  #     code : 控制符的实际形式(比如“\C[1]”是“C”)
  #     text : 绘制处理中的字符串缓存(字符串可能会被修改)
  #     pos  : 绘制位置 {:x, :y, :new_x, :height}
  #--------------------------------------------------------------------------
  def process_escape_character(code, text, pos)
    if code.upcase == 'C'
      change_color(text_color(obtain_escape_param_string(text)))
      return
    end
    super(code, text, pos)
  end
  #--------------------------------------------------------------------------
  # ● 绘制背景图片
  #--------------------------------------------------------------------------
  def eagle_draw_bg_pic(w, h)
    _bitmap = MESSAGE_EX.namebg(name_params[:bg], w, h)
    if _bitmap != nil
      @back_sprite.bitmap = _bitmap
      return true
    end
    return false
  end
  #--------------------------------------------------------------------------
  # ● 更新背景精灵
  #--------------------------------------------------------------------------
  def update_back_sprite
    MESSAGE_EX.reset_xy_dorigin(@back_sprite, self, name_params[:bgo])
    MESSAGE_EX.reset_sprite_oxy(@back_sprite, name_params[:bgo])
    @back_sprite.z = self.z - 1
  end
  #--------------------------------------------------------------------------
  # ● 计算四方向上的增量(因为背景图片可能大于文字区域)
  #--------------------------------------------------------------------------
  def reset_rect_real
    if @flag_use_back_sprite
      update_back_sprite
      xw = self.x + standard_padding; ww = self.width - standard_padding * 2
      yw = self.y + standard_padding; hw = self.height - standard_padding * 2
      xp = @back_sprite.x - @back_sprite.ox
      yp = @back_sprite.y - @back_sprite.oy
      @rect_real.x = 0
      @rect_real.x = xw - xp if xp < xw
      @rect_real.y = 0
      @rect_real.y = yw - yp if yp < yw
      t = xp + @back_sprite.width - xw - ww
      @rect_real.width = 0
      @rect_real.width = t if t > 0
      t = yp + @back_sprite.height - yw - hw
      @rect_real.height = 0
      @rect_real.height = t if t > 0
    end
  end
  #--------------------------------------------------------------------------
  # ● 更新(在 eagle_win_update 中调用)
  #--------------------------------------------------------------------------
  def update_with_msg
    update_position
    update_back_sprite if @flag_use_back_sprite
  end
  #--------------------------------------------------------------------------
  # ● 更新位置
  #  尽管已经更新了姓名框位置,但保留此处用于扩展
  #--------------------------------------------------------------------------
  def update_position
  end

  #--------------------------------------------------------------------------
  # ● 显示
  #--------------------------------------------------------------------------
  def show
    @back_sprite.visible = true if @flag_use_back_sprite
    super
  end
  #--------------------------------------------------------------------------
  # ● 隐藏
  #--------------------------------------------------------------------------
  def hide
    @back_sprite.visible = false if @flag_use_back_sprite
    super
  end
  #--------------------------------------------------------------------------
  # ● 更新(每帧调用)
  #--------------------------------------------------------------------------
  def update
    super
    @back_sprite.opacity = get_back_sprite_opacity if @flag_use_back_sprite
  end
  #--------------------------------------------------------------------------
  # ● 更新背景图片的透明度
  #--------------------------------------------------------------------------
  def get_back_sprite_opacity
    return 0 if self.visible == false
    if self.back_opacity == 0
      return self.contents_opacity if self.contents_opacity < 255
    end
    return self.openness
  end
end

#=============================================================================
# ○ 等待按键的精灵
#=============================================================================
class Sprite_EaglePauseTag < Sprite
  #--------------------------------------------------------------------------
  # ● 初始化对象
  #--------------------------------------------------------------------------
  def initialize(window_bind)
    super(nil)
    bind_window(window_bind)
    @type_source = 0 # 记录当前源位图的类型(见module中对应【设置】)
    @type_pos = 0 # 记录当前相对于对话框的位置类型
    @last_chara = nil
    @last_pause_index = ""
    init_auto_countdown
    reset
    hide
  end
  def bind_window(window_bind); @window_bind = window_bind; end
  def params; @window_bind.pause_params; end
  #--------------------------------------------------------------------------
  # ● 释放
  #--------------------------------------------------------------------------
  def dispose
    @sprite_auto.bitmap.dispose if @sprite_auto.bitmap
    @sprite_auto.dispose
    @s_bitmap.dispose
    self.bitmap.dispose
    super
  end
  #--------------------------------------------------------------------------
  # ● 绑定文末精灵
  #--------------------------------------------------------------------------
  def bind_last_chara(sprite_chara)
    @last_chara = sprite_chara
    reset_position
  end
  #--------------------------------------------------------------------------
  # ● 重置
  #--------------------------------------------------------------------------
  def reset
    reset_source if @last_pause_index != params[:id]
    reset_bitmap
  end
  #--------------------------------------------------------------------------
  # ● 重置源位图
  #--------------------------------------------------------------------------
  def reset_source
    @s_bitmap.dispose if @s_bitmap
    @last_pause_index = params[:id]
    _params = MESSAGE_EX.pause_params(@last_pause_index)
    _bitmap = Cache.system(_params[0])
    _rect = _params[1].nil? ? _bitmap.rect : _params[1]
    @s_bitmap_row = _params[2] # 源位图中一行中帧数目
    @s_bitmap_col = _params[3] # 源位图中一列中帧数目
    @s_bitmap_n = @s_bitmap_row * @s_bitmap_col # 总帧数

    @s_bitmap = Bitmap.new(_rect.width, _rect.height)
    @s_bitmap.blt(0, 0, _bitmap, _rect)
    @s_rect = Rect.new(0, 0, @s_bitmap.width / @s_bitmap_row,
      @s_bitmap.height / @s_bitmap_col)

    self.bitmap.dispose if self.bitmap
    self.bitmap = Bitmap.new(@s_rect.width, @s_rect.height)
    @index = 0 # 当前index
  end
  #--------------------------------------------------------------------------
  # ● 重绘位图
  #--------------------------------------------------------------------------
  def reset_bitmap
    self.bitmap.clear
    @s_rect.x = (@index % @s_bitmap_row) * @s_rect.width
    @s_rect.y = (@index / @s_bitmap_row) * @s_rect.height
    self.bitmap.blt(0, 0, @s_bitmap, @s_rect)
    @count = 0
  end
  #--------------------------------------------------------------------------
  # ● 更新位置
  #--------------------------------------------------------------------------
  def reset_position
    self.viewport = nil
    if params[:do] > 0
      MESSAGE_EX.reset_xy_dorigin(self, @window_bind, params[:do])
    elsif @last_chara
      self.viewport = @window_bind.eagle_chara_viewport
      self.x = @last_chara._x + @last_chara.width - @window_bind.eagle_charas_ox
      self.y = @last_chara._y + @last_chara.height/2 - @window_bind.eagle_charas_oy
    else
      self.x = @window_bind.eagle_charas_x0
      self.y = @window_bind.eagle_charas_y0
    end
    self.x += params[:dx]
    self.y += params[:dy]
    MESSAGE_EX.reset_xy_origin(self, params[:o])
    reset_auto_countdown_position
  end
  #--------------------------------------------------------------------------
  # ● 更新
  #--------------------------------------------------------------------------
  def update
    super
    update_index
    reset_position if self.viewport && @last_chara
  end
  #--------------------------------------------------------------------------
  # ● 更新帧动画
  #--------------------------------------------------------------------------
  def update_index
    return if (@count += 1) < params[:t]
    @index = (@index + 1) % @s_bitmap_n
    reset_bitmap
  end
  #--------------------------------------------------------------------------
  # ● 显示
  #--------------------------------------------------------------------------
  def show
    return if params[:v] == 0
    reset_position
    self.visible = true
    self
  end
  #--------------------------------------------------------------------------
  # ● 隐藏
  #--------------------------------------------------------------------------
  def hide
    @sprite_auto.visible = false
    self.visible = false
    self
  end
  #--------------------------------------------------------------------------
  # ● 初始化自动倒计时
  #--------------------------------------------------------------------------
  def init_auto_countdown
    @sprite_auto = Sprite.new
    @sprite_auto.bitmap = Bitmap.new(MESSAGE_EX::WIN_AUTO_W,
      MESSAGE_EX::WIN_AUTO_H)
    @sprite_auto.bitmap.font.color = Color.new(0,0,0,255)
    @sprite_auto.bitmap.font.shadow = false
    @sprite_auto.bitmap.font.outline = false
    # 用于复制的白色文字
    @auto_temp_bitmap = Bitmap.new(@sprite_auto.width, @sprite_auto.height)
    @auto_temp_bitmap.font.color = Color.new(255,255,255,150)
    @auto_temp_bitmap.font.shadow = false
    @auto_temp_bitmap.font.outline = false
    t = MESSAGE_EX::WIN_AUTO_TEXT
    @auto_temp_bitmap.draw_text(0, 0, @sprite_auto.width, @sprite_auto.height, t, 1)
  end
  #--------------------------------------------------------------------------
  # ● 重置自动倒计时的位置
  #--------------------------------------------------------------------------
  def reset_auto_countdown_position
    MESSAGE_EX.reset_sprite_oxy(@sprite_auto, MESSAGE_EX::WIN_AUTO_O)
    MESSAGE_EX.reset_xy_dorigin(@sprite_auto, @window_bind, MESSAGE_EX::WIN_AUTO_DO)
    @sprite_auto.x += MESSAGE_EX::WIN_AUTO_DX
    @sprite_auto.y += MESSAGE_EX::WIN_AUTO_DY
    @sprite_auto.z = self.z + 1
    @sprite_auto.bitmap.clear
    @sprite_auto.visible = @window_bind.openness > 0 && @window_bind.win_params[:auto_t] != nil
  end
  #--------------------------------------------------------------------------
  # ● 重绘自动倒计时
  #--------------------------------------------------------------------------
  def redraw_auto_countdown(cd)
    count_rate = cd * 1.0 / @window_bind.win_params[:auto_t]
    b = @sprite_auto.bitmap
    b.clear
    b.fill_rect(0, 0, b.width, b.height, Color.new(255,255,255,150))
    _x = (b.width-2) * (1-count_rate)
    r = Rect.new(1+_x, 1, b.width-2-_x, b.height-2)
    b.fill_rect(r, Color.new(0,0,0,150))
    # 绘制黑色文字
    t = MESSAGE_EX::WIN_AUTO_TEXT
    b.draw_text(0, 0, b.width, b.height, t, 1)
    # 绘制白色文字
    r2 = Rect.new(_x, 0, b.width, b.height)
    b.blt(_x, 0, @auto_temp_bitmap, r2)
  end
end

#==============================================================================
# ○ 精灵池(用于更新需要延迟消失的精灵)
#==============================================================================
module MESSAGE_EX
  #--------------------------------------------------------------------------
  # ● 重置
  #--------------------------------------------------------------------------
  def self.pools_reset
    all_pools.each do |type|
      get_pool(type).each { |s| s.dispose }
      get_pool(type).clear
    end
  end
  #--------------------------------------------------------------------------
  # ● 更新
  #--------------------------------------------------------------------------
  def self.pools_update
    all_pools.each do |type|
      get_pool(type).each { |s| s.update if !s.disposed? && !s.finish? }
    end
  end
  #--------------------------------------------------------------------------
  # ● 从指定池子中取出一个可用的精灵(失败则返回nil)
  #--------------------------------------------------------------------------
  def self.pool_new(type)
    while true
      s = get_pool(type).shift
      return nil if s.nil?
      next if s.disposed?
      if !s.finish?
        get_pool(type).unshift(s)
        return nil
      end
      return s
    end
  end
  #--------------------------------------------------------------------------
  # ● 将指定精灵放入指定池子
  # 【注】精灵需要存在 finish? 方法,该方法返回 true 代表可以被重置复用
  #      返回 false 代表需要继续自己的 update
  #--------------------------------------------------------------------------
  def self.pool_push(type, s)
    return if s.disposed?
    return get_pool(type).unshift(s) if s.finish?
    get_pool(type).push(s)
  end

  #--------------------------------------------------------------------------
  # ● 定义可用的池
  #--------------------------------------------------------------------------
  def self.all_pools
    [:chara, :face]
  end
  #--------------------------------------------------------------------------
  # ● 定义全局数组
  #--------------------------------------------------------------------------
  @pool_charas = [] # 文字精灵池
  @pool_faces  = [] # 脸图精灵池
  def self.get_pool(type)
    return @pool_charas if type == :chara
    return @pool_faces if type == :face
  end

  #--------------------------------------------------------------------------
  # ● 对文字精灵池的操作
  #--------------------------------------------------------------------------
  def self.charapool_push(s)
    pool_push(:chara, s)
  end
  def self.charapool_new(window, font, x,y,w,h, viewport)
    s = pool_new(:chara)
    return Sprite_EagleCharacter.new(window, font, x,y,w,h, viewport) if s.nil?
    s.bind_viewport(viewport)
    s.bind_window(window)
    s.bind_font(font)
    s.reset(x,y,w,h)
    s
  end

  #--------------------------------------------------------------------------
  # ● 对脸图精灵池的操作
  #--------------------------------------------------------------------------
  def self.facepool_push(s)
    pool_push(:face, s)
  end
  def self.facepool_new
    s = pool_new(:face)
    return Sprite_EagleFace.new if s.nil?
    s
  end
end
#=============================================================================
# ○ Scene_Base
#=============================================================================
class Scene_Base
  #--------------------------------------------------------------------------
  # ● 开始处理
  #--------------------------------------------------------------------------
  alias eagle_message_pool_start start
  def start
    MESSAGE_EX.pools_reset
    eagle_message_pool_start
  end
  #--------------------------------------------------------------------------
  # ● 更新画面(基础)
  #--------------------------------------------------------------------------
  alias eagle_message_pool_update_basic update_basic
  def update_basic
    eagle_message_pool_update_basic
    MESSAGE_EX.pools_update
  end
  #--------------------------------------------------------------------------
  # ● 结束处理
  #--------------------------------------------------------------------------
  alias eagle_message_pool_terminate terminate
  def terminate
    eagle_message_pool_terminate
    MESSAGE_EX.pools_reset
  end
end

#=============================================================================
# ○ 脸图精灵
#=============================================================================
class Sprite_EagleFace < Sprite
  attr_accessor :opa
  #--------------------------------------------------------------------------
  # ● 绑定
  #--------------------------------------------------------------------------
  def bind_window(w); @window = w; end
  def face_params; @window.face_params; end
  #--------------------------------------------------------------------------
  # ● 已经结束使用?
  #--------------------------------------------------------------------------
  def finish?
    @flag_fin == true
  end
  #--------------------------------------------------------------------------
  # ● 初始化/重置
  #--------------------------------------------------------------------------
  def reset(window)
    bind_window(window)
    init_params
    apply_face_bitmap
    apply_face_params
  end
  #--------------------------------------------------------------------------
  # ● 初始化参数
  #--------------------------------------------------------------------------
  def init_params
    @params = {}   # 自用参数组
    @flag_fin = false  # 可复用的标志
    @fiber = nil # 移动用 fiber
    @fiber_tmp = nil # 若fiber未执行完,则只预存一个 fiber
    # 为了更好的扩展性,不直接使用默认属性,而是利用中间变量去赋值
    @x0 = @y0 = 0 # 因绑定对话框而获得的基础坐标
    @x1 = @y1 = 0 # 因为移动而增加的偏移
    @opa = 0 # 不透明度
  end
  #--------------------------------------------------------------------------
  # ● 脸图未更改?
  #--------------------------------------------------------------------------
  def no_change?
    self.bitmap && !self.bitmap.disposed? &&
    @params[:name] == face_params[:name] &&
    @params[:i] == face_params[:i]
  end
  #--------------------------------------------------------------------------
  # ● 设置脸图文件
  #--------------------------------------------------------------------------
  def apply_face_bitmap
    @params[:name] = face_params[:name]
    self.bitmap = Cache.face(@params[:name])
    @params[:name] =~ /_(\d+)x(\d+)_?/i  # 从文件名获取行数和列数(默认为2行4列)
    @params[:num_line] = $1 ? $1.to_i : face_default_line
    @params[:num_col] = $2 ? $2.to_i : face_default_col
    @params[:num] = @params[:num_line] * @params[:num_col]
    @params[:sole_w] = self.bitmap.width / @params[:num_col]
    @params[:sole_h] = self.bitmap.height / @params[:num_line]
    # 传出脸图宽高,用于对话框中的文字位移
    face_params[:width] = @params[:sole_w]
    face_params[:height] = @params[:sole_h]
    # 脸图以底部中心为显示原点
    self.ox = @params[:sole_w] / 2
    self.oy = @params[:sole_h]
  end
  #--------------------------------------------------------------------------
  # ● 脸图默认规格(行和列)
  #--------------------------------------------------------------------------
  def face_default_line; 2; end
  def face_default_col;  4; end
  #--------------------------------------------------------------------------
  # ● 默认大小的脸图?
  #--------------------------------------------------------------------------
  def face_default_size?
    @params[:sole_w] == 96 && @params[:sole_h] == 96
  end
  #--------------------------------------------------------------------------
  # ● 导入face参数
  #--------------------------------------------------------------------------
  def apply_face_params
    # 移入移出的参数
    @params[:it] = face_params[:it]
    @params[:ot] = face_params[:ot]

    # 判断是否需要循环的flag
    @params[:flag_l] = (face_params[:ls] > -1 && face_params[:le] > face_params[:ls])
    @params[:ls] = face_params[:ls]
    @params[:lt] = face_params[:lt]
    @params[:lw] = face_params[:lw]
    @params[:li_c] = face_params[:ls] # 循环用index计数
    @params[:lt_c] = face_params[:lt] # 循环用time计数
    @params[:lw_c] = face_params[:lw] # 循环后wait计数

    @params[:i] = face_params[:i]
    apply_index
  end
  #--------------------------------------------------------------------------
  # ● 应用当前帧
  #--------------------------------------------------------------------------
  def apply_index
    return if @params[:i] == nil
    @params[:i] = 0 if @params[:i] >= @params[:num]
    w = @params[:sole_w]
    h = @params[:sole_h]
    x = @params[:i] % @params[:num_col] * w
    y = @params[:i] / @params[:num_col] * h
    rect = Rect.new(x, y, w, h)
    self.src_rect = rect
  end
  #--------------------------------------------------------------------------
  # ● 更新
  #--------------------------------------------------------------------------
  def update
    super
    @fiber.resume if @fiber
    update_position
    update_pattern
  end
  #--------------------------------------------------------------------------
  # ● 更新位置
  #--------------------------------------------------------------------------
  def update_position
    if @window
      if face_params[:dir] # 脸图放置于右侧时
        @x0 = @window.x + @window.width - @window.standard_padding - self.ox
      else # 脸图放置于左侧时
        @x0 = @window.x + @window.standard_padding + self.ox
      end
      @x0 += face_params[:dx]
      @y0 = @window.y + @window.height - @window.standard_padding + face_params[:dy]
      self.mirror = face_params[:m]
      self.z = @window.z + face_params[:z]
    end
    self.x = @x0 + @x1
    self.y = @y0 + @y1
    self.opacity = @opa
  end
  #--------------------------------------------------------------------------
  # ● 更新自动播放
  #--------------------------------------------------------------------------
  def update_pattern
    if @params[:flag_l]
      if @params[:li_c] >= @params[:le]
        # 每次loop之间的等待
        return if @params[:lw].nil?
        @params[:lw_c] -= 1
        return if @params[:lw_c] > 0
        @params[:lw_c] = @params[:lw]
        @params[:li_c] = @params[:ls]
      else
        # 每帧之间的等待
        @params[:lt_c] -= 1
        return if @params[:lt_c] > 0
        @params[:lt_c] = @params[:lt]
        @params[:li_c] += 1
      end
      @params[:i] = @params[:li_c]
      apply_index
    end
  end
  #--------------------------------------------------------------------------
  # ● 执行动作
  #--------------------------------------------------------------------------
  def motion(type, param_str = "")
    m_c = ("fiber_#{type}").to_sym
    if respond_to?(m_c)
      @fiber = nil if type == :fade_out
      if @fiber
        @fiber_tmp = Fiber.new { fiber_main(m_c, param_str) }
      else
        @fiber = Fiber.new { fiber_main(m_c, param_str) }
      end
    else
      p "对话框中 \\facem 转义符,指令 #{type} 无效!请检查指令名称及其大小写!"
    end
  end
  #--------------------------------------------------------------------------
  # ● Fiber主逻辑
  #--------------------------------------------------------------------------
  def fiber_main(m_c, param_str)
    method(m_c).call(param_str)
    @fiber = nil
    @fiber = @fiber_tmp if @fiber_tmp
    @fiber_tmp = nil
  end
  #--------------------------------------------------------------------------
  # ● 动作:淡入
  #--------------------------------------------------------------------------
  def fiber_fade_in(param_str = "")
    v = 255.0 / @params[:it]
    while(@opa < 255)
      @opa += v
      yield self if block_given?
      Fiber.yield
    end
  end
  def fiber_in(param_str = ""); fiber_fade_in(param_str); end
  #--------------------------------------------------------------------------
  # ● 动作:淡出
  #--------------------------------------------------------------------------
  def fiber_fade_out(param_str = "")
    bind_window(nil)
    v = 255.0 / @params[:ot]
    while(@opa > 0)
      @opa -= v
      yield self if block_given?
      Fiber.yield
    end
    @flag_fin = true
  end
  def fiber_out(param_str = ""); fiber_fade_out(param_str); end
  #--------------------------------------------------------------------------
  # ● 动作:跳跃
  #--------------------------------------------------------------------------
  def fiber_jump(param_str = "")
    t = 0
    while t <= 10
      @y1 = (t-5)**2 * 40 * 1.0/25 - 40
      yield self if block_given?
      Fiber.yield
      t += 1
    end
  end
  #--------------------------------------------------------------------------
  # ● 动作:移动
  #--------------------------------------------------------------------------
  def fiber_move(param_str = "")
    h = { :x => nil, :y => nil, :dx => nil, :dy => nil, :t => 30 }
    MESSAGE_EX.parse_param(h, param_str, :t)

    init_x1 = @x1
    des_x = init_x1
    des_x = init_x1 + h[:dx] if h[:dx]
    des_x = h[:x] if h[:x]
    init_y1 = @y1
    des_y = init_y1
    des_y = init_y1 + h[:dy] if h[:dy]
    des_y = h[:y] if h[:y]
    return if init_x1 == des_x && init_y1 == des_y
    d_x = des_x - init_x1
    d_y = des_y - init_y1

    _i = 0; _t = h[:t]
    while(true)
      break if _i > _t
      per = _i * 1.0 / _t
      per = (_i == _t ? 1 : MESSAGE_EX.ease_value(:face_xy, per))
      @x1 = (init_x1 + d_x * per).round
      @y1 = (init_y1 + d_y * per).round
      yield self if block_given?
      Fiber.yield
      _i += 1
    end
  end
end

#=============================================================================
# ○ 文字绘制类
#=============================================================================
class Font_EagleCharacter
  attr_reader   :text  # 上一次绘制的文本
  #--------------------------------------------------------------------------
  # ● 初始化
  #--------------------------------------------------------------------------
  def initialize(font_params)
    @params = font_params.dup
    @text = ""
  end
  #--------------------------------------------------------------------------
  # ● 设置参数
  #--------------------------------------------------------------------------
  def set_param(sym, value)
    @params[sym] = value
  end
  #--------------------------------------------------------------------------
  # ● 获取文字颜色
  #     n : 文字颜色编号(0..31)
  #--------------------------------------------------------------------------
  def text_color(n)
    MESSAGE_EX.text_color(n, MESSAGE_EX.windowskin(@params[:skin]))
  end
  #--------------------------------------------------------------------------
  # ● 获取渐变色数组
  #--------------------------------------------------------------------------
  def get_gradient_color(str)
    result = []
    param = str.downcase
    while(param != "")
      param.slice!(/\D+/)
      result.push((param.slice!(/\d+/)).to_i)
    end
    result
  end
  #--------------------------------------------------------------------------
  # ● 执行文字绘制
  #--------------------------------------------------------------------------
  def draw(bitmap, x, y, w, h, c, ali = 0)
    @text = c
    bitmap.font.color = text_color(@params[:c])
    MESSAGE_EX.apply_font_params(bitmap.font, @params)

    draw_param_p(bitmap, x, y, w, h) if @params[:p]
    draw_param_l(bitmap, x, y, w, h, c, ali) if @params[:l]
    if defined?(Sion_GradientText) && @params[:ex_cg] && @params[:ex_cg] != ''
      grad_cs = get_gradient_color(@params[:ex_cg])
      Sion_GradientText.draw_text(bitmap,x,y,w*2,h,c,ali,grad_cs)
    else
      bitmap.draw_text(x, y, w*2, h, c, ali)
    end
    draw_param_m(bitmap, x, y, w, h) if @params[:m]
    draw_param_k(bitmap, x, y, w, h) if @params[:k]
    draw_param_d(bitmap) if @params[:d]
    draw_param_u(bitmap) if @params[:u]
  end
  #--------------------------------------------------------------------------
  # ● 执行图标绘制
  #--------------------------------------------------------------------------
  def draw_icon(bitmap, x, y, icon_index)
    draw_param_p(bitmap, x, y, 24, 24) if @params[:p]
    draw_param_l_rect(bitmap, x, y, 24, 24) if @params[:l]
    _bitmap = Cache.system("Iconset")
    rect = Rect.new(icon_index % 16 * 24, icon_index / 16 * 24, 24, 24)
    bitmap.blt(x, y, _bitmap, rect, 255)
    draw_param_m(bitmap, x, y, 24, 24) if @params[:m]
    draw_param_k(bitmap, x, y, 24, 24) if @params[:k]
    draw_param_d(bitmap) if @params[:d]
    draw_param_u(bitmap) if @params[:u]
  end
  #--------------------------------------------------------------------------
  # ● 绘制底纹
  #--------------------------------------------------------------------------
  def draw_param_p(bitmap, x, y, w, h)
    color = text_color(@params[:pc])
    case @params[:p]
    when 1 # 边框
      bitmap.fill_rect(x, y, w, h, color)
      bitmap.clear_rect(x+1, y+1, w-2, h-2)
    when 2 # 纯色方块
      bitmap.fill_rect(x, y, w, h, color)
    end
  end
  #--------------------------------------------------------------------------
  # ● 绘制外发光
  #--------------------------------------------------------------------------
  def draw_param_l(bitmap, x, y, w, h, c, ali)
    bitmap.font.outline = false
    bitmap.font.shadow = false
    color = bitmap.font.color.dup
    bitmap.font.color = text_color(@params[:lc])
    @params[:lp].times do
      bitmap.draw_text(x, y, w+4, h, c, ali)
      bitmap.blur
    end
    bitmap.font.color = color
  end
  def draw_param_l_rect(bitmap, x, y, w, h)
    c = text_color(@params[:lc])
    bitmap.fill_rect(x, y, w, h, c)
    bitmap.blur
  end
  #--------------------------------------------------------------------------
  # ● 绘制删除线
  #--------------------------------------------------------------------------
  def draw_param_d(bitmap)
    c = text_color(@params[:dc])
    bitmap.fill_rect(0, bitmap.height/2 - 1, bitmap.width, 1, c)
  end
  #--------------------------------------------------------------------------
  # ● 绘制下划线
  #--------------------------------------------------------------------------
  def draw_param_u(bitmap)
    c = text_color(@params[:uc])
    bitmap.fill_rect(0, bitmap.height - 1, bitmap.width, 1, c)
  end
  #--------------------------------------------------------------------------
  # ● 绘制叠加文字
  #--------------------------------------------------------------------------
  def draw_param_m(bitmap, x, y, w, h)
    color = bitmap.font.color.dup
    bitmap.font.color = text_color(@params[:mc]) if @params[:mc] >= 0
    @params[:m].each do |c|
      if c.is_a?(Integer)
        _bitmap = Cache.system("Iconset")
        rect = Rect.new(c % 16 * 24, c / 16 * 24, 24, 24)
        bitmap.blt(x+w/2-12, y+h/2-12, _bitmap, rect, 255)
      else
        bitmap.draw_text(x, y, w, h, c, 1)
      end
    end
    bitmap.font.color = color
  end
  #--------------------------------------------------------------------------
  # ● 文字破碎
  #--------------------------------------------------------------------------
  def draw_param_k(bitmap, x, y, w, h)
    v = @params[:kv]
    if v >= 100
      bitmap.clear_rect(x, y, w, h)
      return
    end
    for xi in x...(x + w)
      for yi in y...(y + h)
        c = bitmap.get_pixel(xi, yi)
        next if c.alpha == 0
        bitmap.set_pixel(xi, yi, Color.new(255,255,255, 0)) if rand(100) < v
      end
    end
  end
  #--------------------------------------------------------------------------
  # ● 执行图片绘制
  #--------------------------------------------------------------------------
  def draw_pic(bitmap, pic_bitmap, h)
    bitmap.stretch_blt(bitmap.rect, pic_bitmap, pic_bitmap.rect, h[:opa])
  end
end
#=============================================================================
# ○ 单个文字的精灵
#=============================================================================
class Sprite_EagleCharacter < Sprite
  attr_reader :origin_x, :origin_y, :_x, :_y, :eagle_font
  attr_accessor :flag_update_pos
  #--------------------------------------------------------------------------
  # ● 初始化对象
  #  window_bind :所绑定的显示窗口,需要有以下方法
  #    .z 返回窗口的z值(当前文字精灵将高于该值)
  #    .eagle_charas_x0 .eagle_charas_y0 返回文字显示区域的左上角坐标(屏幕坐标)
  #    .eagle_charas_ox .eagle_charas_oy 返回文字显示区域的显示原点(文字区域坐标)
  #    .eagle_charas_max_h 返回文字区域的最大高度
  #  font_bind :所绑定的字符绘制类 Font_EagleCharacter 的对象
  #--------------------------------------------------------------------------
  def initialize(window_bind, font_bind, x, y, w, h, viewport = nil)
    super(viewport)
    bind_viewport(viewport)
    bind_window(window_bind)
    bind_font(font_bind)
    reset(x, y, w, h)
  end
  #--------------------------------------------------------------------------
  # ● 释放
  #--------------------------------------------------------------------------
  def dispose
    self.bitmap.dispose if !self.bitmap.disposed?
    super
  end
  #--------------------------------------------------------------------------
  # ● 获取文字在屏幕上的碰撞矩形(计算了绑定的viewport的位置)
  #--------------------------------------------------------------------------
  def screen_rect
    r = Rect.new(self.x - self.ox, self.y - self.oy, self.width, self.height)
    if self.viewport
      r.x += self.viewport.rect.x
      r.y += self.viewport.rect.y
    end
    r
  end
  #--------------------------------------------------------------------------
  # ● 文字中心点是否在视图内部?
  #--------------------------------------------------------------------------
  def in_viewport?
    return true if self.viewport.nil?
    lux = self.x; luy = self.y
    rdx = lux + self.width; rdy = luy + self.height
    return false if lux < 0 || rdx > self.viewport.rect.width
    return false if luy < 0 || rdy > self.viewport.rect.height
    return true
  end
  #--------------------------------------------------------------------------
  # ● 设置绑定的视图
  #--------------------------------------------------------------------------
  def bind_viewport(vp)
    @viewport_bind = vp
    self.viewport = vp
  end
  # 临时解绑与重新绑定
  def unbind_viewport
    self.viewport = nil
  end
  def rebind_viewport
    self.viewport = @viewport_bind
  end
  #--------------------------------------------------------------------------
  # ● 设置绑定的窗口
  #--------------------------------------------------------------------------
  def bind_window(window_bind)
    @window_bind = window_bind
    self.z = @window_bind.z + 1 if @window_bind
  end
  #--------------------------------------------------------------------------
  # ● 设置绑定的绘制参数
  #--------------------------------------------------------------------------
  def bind_font(font_bind)
    @eagle_font = font_bind
  end
  #--------------------------------------------------------------------------
  # ● 在位置不变的情况下,文字精灵不再受限于对话框
  #--------------------------------------------------------------------------
  def free_from_msg
    reset_oxy(7)
    bind_viewport(nil) # 取消视图,确保不会出现资源崩溃,且不再限制可见范围
    update_position    # 更新一次位置,用于刷新保存的@x0和@_ox
    @window_bind = nil # 取消窗口的绑定
  end
  #--------------------------------------------------------------------------
  # ● 重置
  #--------------------------------------------------------------------------
  def reset(x, y, w, h)
    self.bitmap.dispose if self.bitmap
    self.bitmap = Bitmap.new(w, h)
    # (x0,y0) 当前的文字显示区域的左上角的屏幕坐标
    @x0 = 0; @y0 = 0
    # (_ox,_oy) 当前的文字显示区域的显示原点位置(对话框内部坐标系)
    @_ox = 0; @_oy = 0
    # 左对齐时,本文字的显示位置(存储为标准位置,方便对齐)
    @origin_x = x; @origin_y = y
    # 设置本文字的显示位置
    reset_xy(x, y)
    reset_oxy(7)
    # 重置参数
    @dx = 0; @dy = 0 # 动态移动时的偏移值
    @flag_first_move_in = true # 第一次移入?
    @flag_update_pos = true # 需要更新位置?
    @flag_move = nil # 在移动中?
    # 重置特效参数
    @effects = {} # effect_sym => param_string
    @effect_params = {} # effect_sym => param_has
    # 重置精灵参数
    self.src_rect = Rect.new(0,0,self.width,self.height)
    self.zoom_x = self.zoom_y = 1.0
    self.angle = 0
    self.wave_amp    = 0
    self.wave_length = 0
    self.wave_speed  = 0
    self.wave_phase  = 0
    self.mirror = false
    self.blend_type = 0
    self.color = Color.new(255,255,255,0)
    self.opacity = 255
    self.visible = true
  end
  #--------------------------------------------------------------------------
  # ● 设置相对偏移值(以对话框中的文字显示区域的屏幕左上角为原点)
  #--------------------------------------------------------------------------
  def reset_xy(x = nil, y = nil)
    @_x = x if x # 存储文字相对对话框左上角的显示位置
    @_y = y if y
  end
  #--------------------------------------------------------------------------
  # ● 设置移动增量
  #--------------------------------------------------------------------------
  def reset_dxy(dx = nil, dy = nil)
    @dx = dx if dx
    @dy = dy if dy
  end
  #--------------------------------------------------------------------------
  # ● 设置显示原点
  #--------------------------------------------------------------------------
  def reset_oxy(o)
    MESSAGE_EX.reset_sprite_oxy(self, o)
  end
  #--------------------------------------------------------------------------
  # ● 设置显示视图的原点位置
  # (当在移入移出时,若所绑定窗口的内容原点变动,靠此方法强制移动)
  #--------------------------------------------------------------------------
  def reset_window_oxy(ox = nil, oy = nil)
    @_ox = ox if ox
    @_oy = oy if oy
  end
  #--------------------------------------------------------------------------
  # ● 结束
  #--------------------------------------------------------------------------
  def finish
    @flag_move = :end
    self.opacity = 0
  end
  #--------------------------------------------------------------------------
  # ● 结束使命?(在文字池中使用,进行文字精灵的复用)
  #--------------------------------------------------------------------------
  def finish?
    @flag_move == :end
  end

  #--------------------------------------------------------------------------
  # ● 解析参数
  #--------------------------------------------------------------------------
  def parse_param(params, param_s, default_type = "default")
    MESSAGE_EX.parse_param(params, param_s, default_type)
  end
  #--------------------------------------------------------------------------
  # ● 初始化特效的默认参数
  #--------------------------------------------------------------------------
  def init_effect_params(sym)
    MESSAGE_EX.get_default_params(sym)
  end
  #--------------------------------------------------------------------------
  # ● 开始特效(整合)
  #--------------------------------------------------------------------------
  def start_effects(effects)
    @effects = {} # code_symbol => param_string
    effects.each { |sym, param_s| start_effect(sym, param_s) }
  end
  #--------------------------------------------------------------------------
  # ● 开始特效
  #--------------------------------------------------------------------------
  def start_effect(sym, param_s)
    @effects[sym] = param_s
    @effect_params[sym] = init_effect_params(sym).dup # 初始化
    m = ("start_effect_" + sym.to_s).to_sym
    method(m).call(@effect_params[sym], param_s.dup) if respond_to?(m)
  end
  # def start_effect_code(param)  code → 转义符
  # end
  #--------------------------------------------------------------------------
  # ● 更新特效(整合)
  #--------------------------------------------------------------------------
  def update_effects
    @effects.each { |sym, param_s|
      m = ("update_effect_" + sym.to_s).to_sym
      method(m).call(@effect_params[sym]) if respond_to?(m)
    }
  end
  # def update_effect_code(param)  code → 转义符
  # end
  #--------------------------------------------------------------------------
  # ● 结束特效(整合)
  #--------------------------------------------------------------------------
  def finish_effects
    @effects.each { |sym, param|
      m = ("finish_effect_" + sym.to_s).to_sym
      method(m).call(@effect_params[sym]) if respond_to?(m)
    }
    @effects.clear
  end
  #--------------------------------------------------------------------------
  # ● 结束特效
  #--------------------------------------------------------------------------
  def finish_effect(sym)
    return if [email protected]?(sym)
    m = ("finish_effect_" + sym.to_s).to_sym
    method(m).call(@effect_params[sym]) if respond_to?(m)
    @effects.delete(sym)
  end
  # def finish_effect_code(param)  code → 转义符
  # end

  #--------------------------------------------------------------------------
  # ● 更新
  #--------------------------------------------------------------------------
  def update
    return if finish?
    update_position if @flag_update_pos
    return move_update(@flag_move) if @flag_move
    if update_effects?
      super
      update_effects
    end
  end
  #--------------------------------------------------------------------------
  # ● 可以更新特效?
  #--------------------------------------------------------------------------
  def update_effects?
    self.visible && !finish? && [email protected]?
  end
  #--------------------------------------------------------------------------
  # ● 更新位置
  #--------------------------------------------------------------------------
  def update_position
    self.x = @_x + @dx + self.ox
    self.y = @_y + @dy + self.oy
    if @window_bind
      @x0 = @window_bind.eagle_charas_x0
      @y0 = @window_bind.eagle_charas_y0
      if self.viewport
        @_ox = @window_bind.eagle_charas_ox
        @_oy = @window_bind.eagle_charas_oy
        self.x -= self.viewport.rect.x
        self.y -= self.viewport.rect.y
      end
    end
    self.x += (@x0 - @_ox)
    self.y += (@y0 - @_oy)
  end
  #--------------------------------------------------------------------------
  # ● 更新移动
  #--------------------------------------------------------------------------
  def move_update(sym = :cin) # 只有移动结束时,才进行其他更新
    params = @effect_params[sym]
    params[:tc] += 1

    per = params[:tc] * 1.0 / params[:t]
    per = (params[:tc] == params[:t] ? 1 : MESSAGE_EX.ease_value(:chara_xy, per))
    @dx = (params[:dx_init] + params[:dx_d] * per).round
    @dy = (params[:dy_init] + params[:dy_d] * per).round

    if (params[:rxc] += 1) == params[:rxt]
      params[:rxc] = 0
      self.src_rect.x += params[:rx]
    end
    if (params[:ryc] += 1) == params[:ryt]
      params[:ryc] = 0
      self.src_rect.y += params[:ry]
    end
    (params[:vzc] = 0;@_zoom += params[:vz]) if (params[:vzc] += 1) == params[:vzt]
    self.zoom_x = self.zoom_y = 1.0 + @_zoom/100.0
    self.angle += params[:va]
    self.opacity += params[:vo]
    move_end(sym) if params[:tc] == params[:t]
  end
  def move_end(sym = :cin)
    @flag_move = nil
    rebind_viewport
    reset_oxy(7)
    if sym == :cin
      @dx = @dy = 0
      update_position
      self.zoom_x = self.zoom_y = 1.0
      self.opacity = 255
      @flag_first_move_in = false
    elsif sym == :cout
      finish
      @flag_move = :end
    end
  end
  #--------------------------------------------------------------------------
  # ● 正在进行移入移出?
  #--------------------------------------------------------------------------
  def move_updating?
    @flag_move != nil
  end

  #--------------------------------------------------------------------------
  # ● 移入
  #--------------------------------------------------------------------------
  def start_effect_cin(params, param_s, flag_move_in = true)
    parse_param(params, param_s)
    params[:t] = 1 if params[:t] < 1
    params[:vzc] = 0 # 计数用
    params[:vzt] = 1 if params[:vzt] < 1
    rand_cin(params, param_s) if params[:r] != 0
    _vo = 255 / params[:t] # 移入时每帧不透明度增量
    params[:vo] = [_vo, params[:vo], 1].max
    params[:rxc] = 0; params[:ryc] = 0
    params[:rxt] = 1 if params[:rxt] < 1
    params[:ryt] = 1 if params[:ryt] < 1
    move_in if flag_move_in
  end
  #--------------------------------------------------------------------------
  # ● 随机移入
  #--------------------------------------------------------------------------
  RAND_V = lambda { |v| rand(v * 2 + 1) - v }
  def rand_cin(params, param_s)
    params[:t] = rand(params[:t]) + 1
    params[:vz] = RAND_V.call(params[:vz])
    params[:vzt] = rand(params[:vzt]) + 1
    params[:va] = RAND_V.call(params[:va])
    params[:dx] = RAND_V.call(params[:dx])
    params[:dy] = RAND_V.call(params[:dy])
  end
  #--------------------------------------------------------------------------
  # ● 执行移入
  #--------------------------------------------------------------------------
  def move_in
    params = @effect_params[:cin]
    rebind_viewport # 重新绑定视图
    update_position # 记录包含视图位移的值
     # 如果没有定义移入特效 或 不是首次移入且在视图外
    if params.nil? || (!@flag_first_move_in && !in_viewport?)
      self.opacity = 255 # 直接指定不透明度
      @flag_move = nil
      return
    end
    unbind_viewport # 为了移入,先取消视图
    @dx = @dy = 0
    update_position # 用于获取实际的最终显示位置(屏幕坐标)

    _rect = Rect.new(self.x, self.y, self.width, self.height)
    if params[:do] != 0
      MESSAGE_EX.reset_xy_dorigin(_rect, @window_bind, params[:do])
    end
    @dx = params[:dx_init] = _rect.x + params[:dx] - self.x
    @dy = params[:dy_init] = _rect.y + params[:dy] - self.y
    params[:dx_d] = self.x - (_rect.x + params[:dx])
    params[:dy_d] = self.y - (_rect.y + params[:dy])

    @_zoom = -(params[:t]/params[:vzt]) * params[:vz]
    self.angle = -params[:t] * params[:va]
    self.src_rect.x = -(params[:t]/params[:rxt]) * params[:rx]
    self.src_rect.y = -(params[:t]/params[:ryt]) * params[:ry]
    self.zoom_x = self.zoom_y = 1.0 + @_zoom/100.0
    self.opacity = 0
    reset_oxy(5)
    params[:tc] = 0
    @flag_move = :cin
  end
  #--------------------------------------------------------------------------
  # ● 移出
  #--------------------------------------------------------------------------
  def start_effect_cout(params, param_s)
    start_effect_cin(params, param_s, false)
    params[:vo] *= -1
  end
  #--------------------------------------------------------------------------
  # ● 执行移出(外部调用的方法)
  #--------------------------------------------------------------------------
  def move_out
    finish_effects # 先结束全部特效
    finish if !in_viewport? # 若精灵在视图外,则会直接结束
    if !finish?
      process_move_out  # 处理移出模式
    end
    free_from_msg  # 不再受限于对话框内,但位置保持不变
    MESSAGE_EX.charapool_push(self) # 由文字池接管
  end
  #--------------------------------------------------------------------------
  # ● 执行移出(外部调用的方法)(临时移出,之后可以再执行move_in)
  #--------------------------------------------------------------------------
  def move_out_temp
    finish if !in_viewport? # 若精灵在视图外,则会直接结束
    unbind_viewport
    if !finish?
      process_move_out
      update_position
    end
  end
  #--------------------------------------------------------------------------
  # ● 处理移出模式
  #--------------------------------------------------------------------------
  def process_move_out
    if @effect_params[:cout] && !@effect_params[:cout].empty?
      return move_out_cout(@effect_params[:cout])
    end
    if @effect_params[:uout] && !@effect_params[:uout].empty?
      return move_out_uout(@effect_params[:uout])
    end
    finish # 如果未预订任何移出模式,则直接结束
  end
  #--------------------------------------------------------------------------
  # ● 执行默认移出
  #--------------------------------------------------------------------------
  def move_out_cout(params)
    _x = self.x; _y = self.y
    if(self.viewport)
      _x += self.viewport.rect.x; _y += self.viewport.rect.y
    end
    _rect = Rect.new(_x, _y, self.width, self.height)
    if params[:do] != 0
      MESSAGE_EX.reset_xy_dorigin(_rect, @window_bind, params[:do])
      MESSAGE_EX.reset_xy_origin(_rect, 5)
    end
    params[:dx_init] = 0
    params[:dy_init] = 0
    params[:dx_d] = (_rect.x + params[:dx]) - _x
    params[:dy_d] = (_rect.y + params[:dy]) - _y

    @dx = @dy = @_zoom = 0
    reset_oxy(5)
    params[:tc] = 0
    @flag_move = :cout
  end
  #--------------------------------------------------------------------------
  # ● 消散移出
  #--------------------------------------------------------------------------
  def start_effect_uout(params, param_s)
    parse_param(params, param_s)
    params[:dir] = MESSAGE_EX::CU_PARAM_DIR[ params[:dir] ]
    params[:s] = MESSAGE_EX::CU_PARAM_S[ params[:s] ]
  end
  def move_out_uout(params)
    _x = self.x; _y = self.y
    if(self.viewport)
      _x += self.viewport.rect.x; _y += self.viewport.rect.y
    end
    Unravel_Bitmap.new(_x, _y, self.bitmap.clone, 0, 0, self.width,
      self.height, params[:n], params[:d], params[:o], params[:dir], params[:s])
    finish
  end
  #--------------------------------------------------------------------------
  # ● 正弦扭曲特效
  #--------------------------------------------------------------------------
  def start_effect_csin(params, param_s)
    parse_param(params, param_s)
    self.wave_amp    = params[:a]
    self.wave_length = params[:l]
    self.wave_speed  = params[:s]
    self.wave_phase  = params[:p]
  end
  #--------------------------------------------------------------------------
  # ● 波浪特效
  #--------------------------------------------------------------------------
  def start_effect_cwave(params, param_s)
    params[:tc] = 0  # 移动计数用初值(一次性)
    parse_param(params, param_s)
  end
  def update_effect_cwave(params)
    return if (params[:tc] += 1) < params[:t]
    params[:tc] = 0
    @dy += params[:vy]
    params[:vy] *= -1 if @dy < -params[:h] || @dy > params[:h]
  end
  #--------------------------------------------------------------------------
  # ● 抖动特效
  #--------------------------------------------------------------------------
  def start_effect_cshake(params, param_s)
    params[:vxc] = 0  # 移动计数用初值(一次性)
    params[:vyc] = 0  # 移动计数用初值(一次性)
    parse_param(params, param_s)
    params[:vx] = rand(2) * 2 - 1 if params[:vx] == 0
    params[:vy] = rand(2) * 2 - 1 if params[:vy] == 0
  end
  def update_effect_cshake(params)
    if (params[:vxc] += 1) > params[:vxt]
      params[:vxc] = 0
      @dx += params[:vx]
      params[:vx] *= -1 if @dx < -params[:l] || @dx > params[:r]
    end
    if (params[:vyc] += 1) > params[:vyt]
      params[:vyc] = 0
      @dy += params[:vy]
      params[:vy] *= -1 if @dy < -params[:u] || @dy > params[:d]
    end
  end
  #--------------------------------------------------------------------------
  # ● 抖动特效2
  #--------------------------------------------------------------------------
  def start_effect_cshake2(params, param_s)
    parse_param(params, param_s)
  end
  def update_effect_cshake2(params)
    @dx = (-1.0 + params[:l] * rand()) * params[:dx]
    @dy = (-1.0 + params[:l] * rand()) * params[:dy]
  end
  #--------------------------------------------------------------------------
  # ● 摇摆特效
  #--------------------------------------------------------------------------
  def start_effect_cswing(params, param_s)
    params[:ac] = 0 # 当前偏移角度和
    params[:tc] = 0
    parse_param(params, param_s)
    reset_oxy(params[:o])
    self.angle = 0
  end
  def update_effect_cswing(params)
    return if (params[:tc] -= 1) > 0
    if params[:d] == 0
      params[:ac] = self.angle == 0 ? rand(2)*2-1 : (self.angle > 0 ? -1 : 1)
      params[:ac] *= params[:a]
    else
      params[:ac] += params[:d]
      params[:tc] = params[:t]
    end
    if params[:ac].abs >= params[:a]
      params[:ac] = params[:a] * (params[:ac] > 0 ? 1 : -1)
      params[:d] *= -1
      params[:tc] = params[:t2]
    end
    self.angle = params[:ac]
  end
  #--------------------------------------------------------------------------
  # ● 缩放特效
  #--------------------------------------------------------------------------
  def start_effect_czoom(params, param_s)
    params[:tc] = 0 # 计时
    parse_param(params, param_s)
    params[:zoom_x] = 100 # 初始的总缩放量
    params[:zoom_y] = 100
    reset_oxy(params[:o])
  end
  def update_effect_czoom(params)
    return if (params[:tc] -= 1) > 0
    params[:tc] = params[:t]
    if params[:dx] != 0
      params[:zoom_x] += params[:dx]
      params[:dx] *= -1 if params[:zoom_x] > params[:max] || params[:zoom_x] < params[:min]
      self.zoom_x = params[:zoom_x] * 1.0 / 100
    end
    if params[:dy] != 0
      params[:zoom_y] += params[:dy]
      params[:dy] *= -1 if params[:zoom_y] > params[:max] || params[:zoom_y] < params[:min]
      self.zoom_y = params[:zoom_y] * 1.0 / 100
    end
  end
  #--------------------------------------------------------------------------
  # ● 闪烁特效
  #--------------------------------------------------------------------------
  def start_effect_cflash(params, param_s)
    params[:tc] = 0  # 闪烁后的等待时间计数
    parse_param(params, param_s)
    params[:color] = Color.new(params[:r], params[:g], params[:b], params[:a])
  end
  def update_effect_cflash(params)
    return if (params[:tc] -= 1) > 0
    params[:tc] = params[:t] + params[:d]
    self.flash(params[:color], params[:d])
  end
  #--------------------------------------------------------------------------
  # ● 镜像特效
  #--------------------------------------------------------------------------
  def start_effect_cmirror(params, param_s)
    params[:b]  = '0'
    parse_param(params, param_s, :b)
    self.mirror = (params[:b] == '0' ? false : true)
  end
  def finish_effect_cmirror(params)
    self.mirror = false
  end
  #--------------------------------------------------------------------------
  # ● 消散特效
  #--------------------------------------------------------------------------
  def start_effect_cu(params, param_s)
    params[:t_c] = 0 # 间隔计数
    parse_param(params, param_s)
    params[:dir] = MESSAGE_EX::CU_PARAM_DIR[ params[:dir] ]
    params[:s] = MESSAGE_EX::CU_PARAM_S[ params[:s] ]
  end
  def update_effect_cu(params)
    return if !in_viewport?
    return if (params[:t_c] += 1) < params[:t]
    params[:t_c] = 0
    _x = self.x; _y = self.y
    if(self.viewport)
      _x += self.viewport.rect.x; _y += self.viewport.rect.y
    end
    Unravel_Bitmap.new(_x, _y, self.bitmap.clone, 0, 0, self.width,
      self.height, params[:n], params[:d], params[:o], params[:dir], params[:s])
  end
  #--------------------------------------------------------------------------
  # ● 位图切换特效
  #--------------------------------------------------------------------------
  def start_effect_ctog(params, param_s)
    parse_param(params, param_s)
    params[:bitmaps] = []
    params[:bitmaps].push(self.bitmap)
    charas = MESSAGE_EX.get_charas_array(:ctog, params[:i], params[:n])
    charas.each do |c|
      s = Bitmap.new(self.width, self.height)
      if c.is_a?(Integer)
        @eagle_font.draw_icon(s, 0+self.width/2-12, 0+self.height/2-12, c)
      else
        r = s.text_size(c)
        @eagle_font.draw(s, (self.width-r.width)/2, (self.height-r.height)/2,
          self.width, self.height, c, 0)
      end
      params[:bitmaps].push(s)
    end
    params[:i_cur] = 0
    params[:i_max] = params[:bitmaps].size
    params[:tc] = 0
  end
  def update_effect_ctog(params)
    return if (params[:tc] += 1) < params[:t]
    params[:tc] = 0
    if(params[:r] > 0)
      params[:i_cur] = rand(params[:i_max])
    else
      params[:i_cur] += 1
      params[:i_cur] %= params[:i_max]
    end
    self.bitmap = params[:bitmaps][params[:i_cur]]
  end
  def finish_effect_ctog(params)
    self.bitmap = params[:bitmaps].shift
    params[:bitmaps].each { |b| b.dispose }
    params[:bitmaps].clear
  end
  #--------------------------------------------------------------------------
  # ● 霓虹灯特效
  #--------------------------------------------------------------------------
  def start_effect_cneon(params, param_s)
    params[:c] = []
    s = param_s.downcase
    while(s != "")
      sym = s.slice!(/\D+/)
      v = (s.slice!(/\d+/)).to_i
      next params[:c].push(v) if sym == "c"
      params[sym.to_sym] = v
    end
    params[:tc] = 0
    params[:c1] = self.color
    params[:c1].alpha = 255
    params[:c2] = @window_bind.text_color(params[:c][0])
    params[:i] = 0
  end
  def update_effect_cneon(params)
    return if @window_bind.nil?
    params[:tc] += 1
    self.color.red = params[:c1].red +
      (params[:c2].red - params[:c1].red)*1.0/ params[:t] * params[:tc]
    self.color.green = params[:c1].green +
      (params[:c2].green - params[:c1].green)*1.0 / params[:t] * params[:tc]
    self.color.blue = params[:c1].blue +
      (params[:c2].blue - params[:c1].blue)*1.0 / params[:t] * params[:tc]

    if params[:tc] >= params[:t]
      params[:c1] = @window_bind.text_color(params[:c][params[:i]])
      params[:i] += 1
      params[:i] = 0 if params[:i] >= params[:c].size
      params[:c2] = @window_bind.text_color(params[:c][params[:i]])
      params[:tc] = 0
    end
  end
  #--------------------------------------------------------------------------
  # ● 文字叠加绘制
  #--------------------------------------------------------------------------
  def start_effect_cmc(params, param_s)
    parse_param(params, param_s)
    charas = MESSAGE_EX.get_charas_array(:cmc, params[:i], params[:n])
    @eagle_font.set_param(:m, charas)
    @eagle_font.set_param(:mc, params[:c])
  end
  #--------------------------------------------------------------------------
  # ● 跳跃特效
  #--------------------------------------------------------------------------
  def start_effect_cjump(params, param_s)
    parse_param(params, param_s)
    params[:tc] *= -1
    # 二次函数的x项系数
    params[:A] = (4.0 * params[:h]) / (params[:t] * params[:t])
  end
  def update_effect_cjump(params)
    if params[:tc] < 0 # 等待中
    elsif params[:tc] <= params[:t] # 跳跃中
      @dy = (params[:tc]-params[:t]/2)**2 * params[:A] - params[:h]
    elsif params[:w] && params[:tc] >= params[:t] + params[:w]
      # 跳跃后的等待结束
      params[:tc] = 0
    end
    params[:tc] += 1
  end
  #--------------------------------------------------------------------------
  # ● 明灭特效
  #--------------------------------------------------------------------------
  def start_effect_cfk(params, param_s)
    parse_param(params, param_s)
    params[:c] = 0
    params[:wait] = 0
  end
  def update_effect_cfk(params)
    return if params[:wait] == nil || (params[:wait] -= 1) > 0
    if params[:c] == 0
      self.opacity = 255
      if params[:t] > 0
        params[:t_] = params[:t]
        params[:dopa] = 255.0 / params[:t_]
      elsif params[:t] == 0
        params[:t_] = MESSAGE_EX::CFK_T0_WAIT
        params[:dopa] = 255
      else
        params[:t_] = rand(-params[:t])
        params[:dopa] = 255.0 / params[:t_]
      end
    end
    params[:c] += 1
    self.opacity += (params[:c]<=params[:t_] ? -params[:dopa] : params[:dopa])
    if self.opacity >= 255
      params[:c] = 0
      params[:wait] = params[:w]
    end
  end
end

#=============================================================================
# ○ 兼容模式
#=============================================================================
module MESSAGE_EX::COMPA_MODE
  #--------------------------------------------------------------------------
  # ● 更新画面
  #--------------------------------------------------------------------------
  def update_all_windows
    if $game_message.eagle_message == true
      @message_window = @msg_windows[1]
    else
      @message_window = @msg_windows[0]
    end
    eagle_message_ex_compa_mode_update_all_windows
  end
  #--------------------------------------------------------------------------
  # ● 释放所有窗口
  #--------------------------------------------------------------------------
  def dispose_all_windows
    @msg_windows.each { |w| w.dispose }
    @message_window = nil
    eagle_message_ex_compa_mode_dispose_all_windows
  end
end
#=============================================================================
# ○ Scene_Map
#=============================================================================
class Spriteset_Map; attr_reader :character_sprites; end
class Scene_Map
  attr_reader :spriteset, :message_window
if EAGLE_MSG_EX_COMPAT_MODE == true
  alias eagle_message_ex_compa_mode_update_all_windows update_all_windows
  alias eagle_message_ex_compa_mode_dispose_all_windows dispose_all_windows
  include MESSAGE_EX::COMPA_MODE
  #--------------------------------------------------------------------------
  # ● 生成信息窗口
  #--------------------------------------------------------------------------
  alias eagle_message_ex_compa_mode_create_message_window create_message_window
  def create_message_window
    eagle_message_ex_compa_mode_create_message_window
    @msg_windows = [@message_window, Window_EagleMessage.new]
  end
else
  #--------------------------------------------------------------------------
  # ● 生成信息窗口
  #--------------------------------------------------------------------------
  def create_message_window
    @message_window = Window_EagleMessage.new
  end
end
end
#=============================================================================
# ○ Scene_Battle
#=============================================================================
class Scene_Battle
  attr_reader :spriteset, :message_window
if EAGLE_MSG_EX_COMPAT_MODE == true
  alias eagle_message_ex_compa_mode_update_all_windows update_all_windows
  alias eagle_message_ex_compa_mode_dispose_all_windows dispose_all_windows
  include MESSAGE_EX::COMPA_MODE
  #--------------------------------------------------------------------------
  # ● 生成信息窗口
  #--------------------------------------------------------------------------
  alias eagle_message_ex_compa_mode_create_message_window create_message_window
  def create_message_window
    eagle_message_ex_compa_mode_create_message_window
    @msg_windows = [@message_window, Window_EagleMessage.new]
  end
else
  #--------------------------------------------------------------------------
  # ● 生成信息窗口
  #--------------------------------------------------------------------------
  def create_message_window
    @message_window = Window_EagleMessage.new
  end
end
end
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
200
在线时间
21 小时
注册时间
2024-6-21
帖子
20
15
 楼主| 发表于 2024-7-16 09:29:21 | 只看该作者
百里_飞柳 发表于 2024-7-15 20:02
名字加框是指什么,具体是怎么编写的转义符呢?
最好直接把文本内容列出来 ...

只是改了名字加框的位置,还有对话框文本的高度,还有色号
回复 支持 反对

使用道具 举报

Lv2.观梦者

梦石
0
星屑
556
在线时间
82 小时
注册时间
2024-4-13
帖子
55
16
发表于 2024-7-31 07:48:31 | 只看该作者
兄弟实在不行可以试试这个
  1. #===========================================================================
  2. # ◆ A1 Scripts ◆
  3. #    ネームウィンドウ(RGSS2/RGSS3共用)
  4. #
  5. # バージョン   : 2.40 (2012/01/19)
  6. # 作者         : A1
  7. # URL     : http://a1tktk.web.fc2.com/
  8. #---------------------------------------------------------------------------
  9. # 機能:
  10. # ・ネームウィンドウを表示します
  11. #---------------------------------------------------------------------------
  12. # 更新履歴   :2011/12/15 Ver1.00 リリース
  13. #            :2011/12/29 Ver1.10 アクター名表示対応
  14. #            :2011/12/30 Ver2.00 左右顔グラフィック対応
  15. #            :2011/12/30 Ver2.00 表示位置「上」対応
  16. #            :2011/12/30 Ver2.10 RGSS2対応
  17. #            :2011/12/30 Ver2.11 名前が切り替わる度にウィンドウを閉じる不具合を修正
  18. #            :2012/01/02 Ver2.20 同じ顔グラフィックの別名表示機能追加
  19. #            :2012/01/02 Ver2.20 表示名の直接指定機能追加
  20. #            :2012/01/02 Ver2.30 A1共通スクリプトVer3.30対応
  21. #            :2012/01/19 Ver2.40 バトルネームウィンドウ対応
  22. #---------------------------------------------------------------------------
  23. # 設置場所      
  24. #  A1共通スクリプトより下
  25. #  (左右顔グラフィックスクリプトより下)
  26. #
  27. # 必要スクリプト
  28. #    A1共通スクリプトVer3.30以上
  29. #---------------------------------------------------------------------------
  30. # 使い方
  31. # 設定項目を設定します
  32. # 
  33. #   設定項目の「表示する名前」を Actor[ID] とすると
  34. #   IDで指定したアクターの名前を表示します
  35. # 
  36. # イベントコマンド「注釈」に記述
  37. #
  38. #  ネームウィンドウ on|off
  39. #      表示の on/off を切り替えます
  40. #
  41. #    NWインデックス index
  42. #      同じ顔グラフィックに複数の名前を配列で登録している場合
  43. #      次に表示するネームウィンドウを指定した index の名前を使用します
  44. #      省略時には 0番目 の名前を使用します
  45. #
  46. #    NW名前指定 Name
  47. #      次に表示するネームウィンドウに Name を使用します
  48. #      顔グラフィックなしでも表示されます
  49. #==============================================================================
  50. $imported ||= {}
  51. $imported["A1_Name_Window"] = true
  52. if $imported["A1_Common_Script"]
  53. old_common_script("ネームウィンドウ", "3.30") if common_version < 3.30
  54. #==============================================================================
  55. # ■ 設定項目
  56. #==============================================================================
  57. module A1_System::NameWindow

  58.   #--------------------------------------------------------------------------
  59.   # ネームウィンドウを使用するクラス
  60.   #--------------------------------------------------------------------------
  61.   USE_NAME_WINDOW_CLASS = [Window_Message]
  62.   
  63.   #--------------------------------------------------------------------------
  64.   # ネームウィンドウのフォント
  65.   #--------------------------------------------------------------------------
  66.   NAME_FONT = "UmePlus Gothic"
  67.   
  68.   #--------------------------------------------------------------------------
  69.   # 長い名前の時に左(右)に寄せる
  70.   #--------------------------------------------------------------------------
  71.   FIX_LONG_NAME = false
  72.   
  73.   #--------------------------------------------------------------------------
  74.   # 顔グラフィックと名前の対応
  75.   #
  76.   #  "[ファイル名]_[Index]" => "表示する名前" ※Index毎に設定
  77.   #  "[ファイル名]"         => "表示する名前" ※該当ファイル全てに適用
  78.   #                            "Actor[ID]"    ※該当するIDのアクター名を表示
  79.   #--------------------------------------------------------------------------
  80.   NAME_LIST = {
  81.     "ahiru"    => "丑小鸭",
  82.   }
  83. end
  84. #==============================================================================
  85. # ■ Cache
  86. #------------------------------------------------------------------------------
  87. #  各種グラフィックを読み込み、Bitmap オブジェクトを作成、保持するモジュール
  88. # です。読み込みの高速化とメモリ節約のため、作成した Bitmap オブジェクトを内部
  89. # のハッシュに保存し、同じビットマップが再度要求されたときに既存のオブジェクト
  90. # を返すようになっています。
  91. #==============================================================================

  92. module Cache
  93.   #--------------------------------------------------------------------------
  94.   # ○ ネームウィンドウ用ビットマップの取得
  95.   #--------------------------------------------------------------------------
  96.   def self.name_bitmap(name)
  97.     return load_name_bitmap(name)
  98.   end
  99.   #--------------------------------------------------------------------------
  100.   # ○ 名前bitmapの作成
  101.   #--------------------------------------------------------------------------
  102.   def self.load_name_bitmap(name)
  103.     @cache ||= {}
  104.     key = [name, "name_window"]
  105.     return @cache[key] if include?(key)
  106.    
  107.     # 計算用ダミービットマップ
  108.     bitmap = Cache.system("")
  109.      bitmap.font.name = [ "Lolita",*Font.default_name]
  110.     bitmap.font.size = 20
  111.     tw = bitmap.text_size(name).width + 8
  112.    
  113.     # ビットマップ作成
  114.     bitmap = Bitmap.new(tw, bitmap.font.size + 4)
  115.      bitmap.font.name = [ "Lolita",*Font.default_name]
  116.     bitmap.font.size = 20
  117.     bitmap.font.color = Color.new(255,255,255)
  118.     bitmap.draw_text(0, 0, bitmap.width, bitmap.height, name, 1)
  119.    
  120.     @cache[key] = bitmap
  121.     return @cache[key]
  122.   end
  123. end
  124. #==============================================================================
  125. # ■ Window_FaceName
  126. #==============================================================================

  127. class Window_FaceName < Window_Base
  128.   #--------------------------------------------------------------------------
  129.   # ○ オブジェクト初期化
  130.   #--------------------------------------------------------------------------
  131.   def initialize(name, z)
  132.     info = create_name_sprite(name)
  133.     super(0, 0, info[0], info[1])
  134.     self.visible = true
  135.     self.openness = 0
  136.     self.z = z
  137.     skin = Cache.system("Window").clone
  138.     skin.clear_rect(80, 16, 32, 32)
  139.     self.windowskin = skin
  140.     @name_sprite.z = self.z + 10
  141.   end
  142.   #--------------------------------------------------------------------------
  143.   # ○ ネームウィンドウのセットアップ
  144.   #--------------------------------------------------------------------------
  145.   def setup_name_window(name)
  146.     info = create_name_sprite(name)
  147.     self.width  = info[0]
  148.     self.height = info[1]
  149.     create_contents
  150.     @name_sprite.z = self.z + 10
  151.   end
  152.   #--------------------------------------------------------------------------
  153.   # ○ フレーム更新
  154.   #--------------------------------------------------------------------------
  155.   def update
  156.     super
  157.     @name_sprite.visible = self.visible && self.open?
  158.     return unless self.open?
  159.     @name_sprite.update
  160.   end
  161.   #--------------------------------------------------------------------------
  162.   # ○ 解放
  163.   #--------------------------------------------------------------------------
  164.   def dispose
  165.     @name_sprite.bitmap.dispose
  166.     @name_sprite.dispose
  167.     super
  168.   end
  169.   #--------------------------------------------------------------------------
  170.   # ○ ウィンドウを開く
  171.   #--------------------------------------------------------------------------
  172.   def open
  173.     super
  174.     @name_sprite.x = self.x + self.width / 2
  175.     @name_sprite.y = self.y + self.height / 2
  176.   end
  177.   #--------------------------------------------------------------------------
  178.   # ○ スプライトの作成
  179.   #--------------------------------------------------------------------------
  180.   def create_name_sprite(name)
  181.     # ビットマップの取得
  182.     bitmap = Cache.name_bitmap(name)
  183.    
  184.     # スプライト設定
  185.     @name_sprite         = Sprite.new
  186.     @name_sprite.bitmap  = bitmap
  187.     @name_sprite.ox      = bitmap.width / 2
  188.     @name_sprite.oy      = bitmap.height / 2
  189.     @name_sprite.visible = false
  190.    
  191.     return [bitmap.width + 8, bitmap.height + 8]
  192.   end
  193. end
  194. #==============================================================================
  195. # ■ Window_Base
  196. #------------------------------------------------------------------------------
  197. #  ゲーム中のすべてのウィンドウのスーパークラスです。
  198. #==============================================================================

  199. class Window_Base < Window
  200.   #--------------------------------------------------------------------------
  201.   # ☆ オブジェクト初期化
  202.   #--------------------------------------------------------------------------
  203.   alias a1_name_window_window_base_initialize initialize
  204.   def initialize(x, y, width, height)
  205.     a1_name_window_window_base_initialize(x, y, width, height)
  206.     create_name_window
  207.   end
  208.   #--------------------------------------------------------------------------
  209.   # ☆ フレーム更新
  210.   #--------------------------------------------------------------------------
  211.   alias a1_name_window_window_base_update update
  212.   def update
  213.     a1_name_window_window_base_update
  214.     update_name_window
  215.   end
  216.   #--------------------------------------------------------------------------
  217.   # ☆ 顔グラフィックの描画
  218.   #--------------------------------------------------------------------------
  219.   alias a1_name_window_window_base_draw_face draw_face
  220.   def draw_face(face_name, face_index, x, y, size = 96)
  221.     a1_name_window_window_base_draw_face(face_name, face_index, x, y, size)
  222.     show_name_window(face_name, face_index, x, size)
  223.   end
  224.   #--------------------------------------------------------------------------
  225.   # ☆ ウィンドウを閉じる
  226.   #--------------------------------------------------------------------------
  227.   alias a1_name_window_window_base_close close
  228.   def close
  229.     a1_name_window_window_base_close
  230.   end
  231.   #--------------------------------------------------------------------------
  232.   # ☆ 解放
  233.   #--------------------------------------------------------------------------
  234.   alias a1_name_window_window_base_dispose dispose
  235.   def dispose
  236.     a1_name_window_window_base_dispose
  237.     dispose_name_window
  238.   end
  239.   #--------------------------------------------------------------------------
  240.   # ○ ネームウィンドウの解放
  241.   #--------------------------------------------------------------------------
  242.   def dispose_name_window
  243.     @name_windows.values.each {|window| window.dispose }
  244.   end
  245.   #--------------------------------------------------------------------------
  246.   # ○ ネームウィンドウの更新
  247.   #--------------------------------------------------------------------------
  248.   def update_name_window
  249.     @name_windows.values.each {|window| window.update }
  250.   end
  251.   #--------------------------------------------------------------------------
  252.   # ○ ネームウィンドウを使用?
  253.   #--------------------------------------------------------------------------
  254.   def use_name_window?
  255.     A1_System::NameWindow::USE_NAME_WINDOW_CLASS.each {|clas| return true if self.is_a?(clas) }
  256.     return false
  257.   end
  258.   #--------------------------------------------------------------------------
  259.   # ○ ネームウィンドウの作成
  260.   #--------------------------------------------------------------------------
  261.   def create_name_window
  262.     @name_windows = {}
  263.   end
  264.   #--------------------------------------------------------------------------
  265.   # ○ 表示する名前の取得
  266.   #--------------------------------------------------------------------------
  267.   def show_name(face_name, face_index)
  268.     return nil unless $game_system.use_name_window
  269.     name = $game_temp.direct_show_name
  270.     if name.empty?
  271.       return nil if face_name == nil || face_name.empty?
  272.       name = A1_System::NameWindow::NAME_LIST[sprintf("%s_%d", face_name, face_index)]
  273.       name = A1_System::NameWindow::NAME_LIST[face_name] if name == nil
  274.       name = name[$game_temp.name_index] if name.is_a?(Array)
  275.       name = $game_actors[$1.to_i].name if name =~ /Actor\[(\d+)\]/
  276.     end
  277.     $game_temp.name_index       = 0
  278.     $game_temp.direct_show_name = ""
  279.     return name
  280.   end
  281.   #--------------------------------------------------------------------------
  282.   # ○ ネームウィンドウの表示
  283.   #--------------------------------------------------------------------------
  284.   def show_name_window(face_name, face_index, x, size = 96)
  285.     return unless use_name_window?
  286.     name = show_name(face_name, face_index)
  287.     return if name == nil or name.empty?
  288.     @name_windows[name] ||= Window_FaceName.new(name, self.z + 10)
  289.     if x <= Graphics.width / 2
  290.       @name_windows[name].x = x + size + 20
  291.       @name_windows[name].x = 0 if @name_windows[name].x + @name_windows[name].width > Graphics.width / 2 and A1_System::NameWindow::FIX_LONG_NAME
  292.     else
  293.       @name_windows[name].x = Graphics.width - size - @name_windows[name].width
  294.       @name_windows[name].x = Graphics.width - @name_windows[name].width if @name_windows[name].x < Graphics.width / 2 and A1_System::NameWindow::FIX_LONG_NAME
  295.     end
  296.     @name_windows[name].y = self.y      - 16 if self.y  > 0
  297.     @name_windows[name].y = self.height - 16 if self.y == 0
  298.     @name_windows[name].openness = 255 if self.open?
  299.     @name_windows[name].open
  300.     @name_windows[name].visible = true
  301.   end
  302.   #--------------------------------------------------------------------------
  303.   # ○ ネームウィンドウを閉じる
  304.   #--------------------------------------------------------------------------
  305.   def name_window_close
  306.     @name_windows.values.each {|window| window.close }
  307.   end
  308.   #--------------------------------------------------------------------------
  309.   # ○ ネームウィンドウを非表示
  310.   #--------------------------------------------------------------------------
  311.   def name_window_visible_false
  312.     @name_windows.values.each {|window| window.visible = false }
  313.   end
  314. end
  315. #==============================================================================
  316. # ■ Window_Message
  317. #------------------------------------------------------------------------------
  318. #  文章表示に使うメッセージウィンドウです。
  319. #==============================================================================

  320. class Window_Message
  321.   #--------------------------------------------------------------------------
  322.   # ○ ウィンドウを閉じる
  323.   #--------------------------------------------------------------------------
  324.   def close
  325.     name_window_close
  326.     super
  327.   end
  328. end
  329. #==============================================================================
  330. # ◆ RGSS3用処理
  331. #==============================================================================
  332. if rgss_version == 3
  333. #==============================================================================
  334. # ■ Window_Message
  335. #------------------------------------------------------------------------------
  336. #  文章表示に使うメッセージウィンドウです。
  337. #==============================================================================

  338. class Window_Message < Window_Base
  339.   #--------------------------------------------------------------------------
  340.   # ☆ 改ページ処理
  341.   #--------------------------------------------------------------------------
  342.   alias a1_name_window_window_message_new_page new_page
  343.   def new_page(text, pos)
  344.     name_window_visible_false
  345.     a1_name_window_window_message_new_page(text, pos)
  346.   end
  347. end
  348. #==============================================================================
  349. # ◆ RGSS2用処理
  350. #==============================================================================
  351. elsif rgss_version == 2
  352. #==============================================================================
  353. # ■ Window_Message
  354. #------------------------------------------------------------------------------
  355. #  文章表示に使うメッセージウィンドウです。
  356. #==============================================================================

  357. class Window_Message < Window_Selectable
  358.   #--------------------------------------------------------------------------
  359.   # ☆ 改ページ処理
  360.   #--------------------------------------------------------------------------
  361.   alias a1_name_window_window_message_new_page new_page
  362.   def new_page
  363.     name_window_visible_false
  364.     a1_name_window_window_message_new_page
  365.   end
  366. end
  367. #==============================================================================
  368. # ◆ RGSS用処理
  369. #==============================================================================
  370. elsif rgss_version == 1
  371. end
  372. #==============================================================================
  373. # ■ Game_System
  374. #------------------------------------------------------------------------------
  375. #  システム周りのデータを扱うクラスです。乗り物や BGM などの管理も行います。
  376. # このクラスのインスタンスは $game_system で参照されます。
  377. #==============================================================================

  378. class Game_System
  379.   #--------------------------------------------------------------------------
  380.   # ○ 公開インスタンス変数
  381.   #--------------------------------------------------------------------------
  382.   attr_accessor :use_name_window                # ネームウィンドウ表示フラグ
  383.   #--------------------------------------------------------------------------
  384.   # ☆ オブジェクト初期化
  385.   #--------------------------------------------------------------------------
  386.   alias a1_name_window_game_system_initialize initialize
  387.   def initialize
  388.     a1_name_window_game_system_initialize
  389.     @use_name_window = false
  390.   end
  391. end
  392. #==============================================================================
  393. # ■ Game_Temp
  394. #------------------------------------------------------------------------------
  395. #  セーブデータに含まれない、一時的なデータを扱うクラスです。このクラスのイン
  396. # スタンスは $game_temp で参照されます。
  397. #==============================================================================

  398. class Game_Temp
  399.   #--------------------------------------------------------------------------
  400.   # ○ 公開インスタンス変数
  401.   #--------------------------------------------------------------------------
  402.   attr_accessor :name_index
  403.   attr_accessor :direct_show_name
  404.   #--------------------------------------------------------------------------
  405.   # ☆ オブジェクト初期化
  406.   #--------------------------------------------------------------------------
  407.   alias a1_name_window_gt_initialize initialize
  408.   def initialize
  409.     a1_name_window_gt_initialize
  410.     @name_index       = 0
  411.     @direct_show_name = ""
  412.   end
  413. end
  414. #==============================================================================
  415. # ■ A1_System::CommonModule
  416. #==============================================================================

  417. class A1_System::CommonModule
  418.   #--------------------------------------------------------------------------
  419.   # ☆ 注釈コマンド定義
  420.   #--------------------------------------------------------------------------
  421.   alias a1_name_window_define_command define_command
  422.   def define_command
  423.     a1_name_window_define_command
  424.     @cmd_108["ネームウィンドウ"] = :name_window
  425.     @cmd_108["NWインデックス"]   = :nw_index
  426.     @cmd_108["NW名前指定"]       = :nw_set_name
  427.   end
  428. end
  429. #==============================================================================
  430. # ■ Game_Interpreter
  431. #------------------------------------------------------------------------------
  432. #  イベントコマンドを実行するインタプリタです。このクラスは Game_Map クラス、
  433. # Game_Troop クラス、Game_Event クラスの内部で使用されます。
  434. #==============================================================================

  435. class Game_Interpreter
  436.   #--------------------------------------------------------------------------
  437.   # ○ ネームウィンドウ
  438.   #--------------------------------------------------------------------------
  439.   def name_window(params)
  440.     $game_system.use_name_window = params[0] == "on" ? true : false
  441.   end
  442.   #--------------------------------------------------------------------------
  443.   # ○ NWインデックス
  444.   #--------------------------------------------------------------------------
  445.   def nw_index(params)
  446.     $game_temp.name_index = params[0].to_i
  447.   end
  448.   #--------------------------------------------------------------------------
  449.   # ○ NW名前指定
  450.   #--------------------------------------------------------------------------
  451.   def nw_set_name(params)
  452.     $game_temp.direct_show_name = params[0]
  453.   end
  454. end
  455. end
复制代码

另外还要
  1. # ===========================================================================
  2. # ◆ A1 Scripts ◆
  3. #    A1共通処理(RGSS2/RGSS3共用)
  4. #
  5. # バージョン   : 4.50 (2012/01/26)
  6. # 作者         : A1
  7. # URL     : http://a1tktk.web.fc2.com/
  8. # ---------------------------------------------------------------------------
  9. # 更新履歴   :2011/11/11 Ver1.00 新規作成
  10. #        :2011/12/22 Ver2.00 RGSS3用と同様の処理に変更
  11. #        :2011/12/30 Ver2.10 RGSS3用メソッドを追加
  12. #        :2011/12/30 Ver3.00 RGSS3用共通処理と統合
  13. #        :2011/12/31 Ver3.10 マップチップサーチの仕様を変更
  14. #        :2011/12/31 Ver3.10 拡張通行判定を追加
  15. #        :2012/01/01 Ver3.11 クラス名の取得処理を追加
  16. #              :2012/01/02 Ver3.20 配列を考慮したsplit処理を追加
  17. #              :2012/01/02 Ver3.20 配列の全ての要素を整数にする処理を追加
  18. #              :2012/01/02 Ver3.30 注釈の処理の仕様を変更
  19. #              :2012/01/02 Ver3.40 「前のイベントコマンドの取得」を追加
  20. #              :2012/01/03 Ver3.50 「フレーム更新」を追加
  21. #              :2012/01/04 Ver3.60 「指定のウィンドウが開いている間ウェイト」追加
  22. #              :2012/01/04 Ver3.70 RGSS2用処理見直し
  23. #              :2012/01/05 Ver3.80 注釈文字列にエスケープコマンド対応
  24. #              :2012/01/05 Ver3.80 多次元配列を考慮したsplit処理を追加
  25. #              :2012/01/05 Ver3.80 注釈にスクリプト処理機能を追加
  26. #              :2012/01/10 Ver3.90 文字縁取り描画を追加
  27. #              :2012/01/11 Ver4.00 テキストビットマップのキャッシュを追加
  28. #              :2012/01/13 Ver4.01 「タイルセットの変更」ができなくなる不具合を修正
  29. #              :2012/01/14 Ver4.10 split処理を強化
  30. #              :2012/01/14 Ver4.20 空白を含む注釈コマンドに対応
  31. #              :2012/01/14 Ver4.21 split処理の不具合を修正
  32. #              :2012/01/21 Ver4.30 メモの内容を取得する関数を追加
  33. #              :2012/01/24 Ver4.40 メモの内容を取得する関数を追加
  34. #              :2012/01/24 Ver4.50 メモの内容を取得する関数を追加
  35. # ---------------------------------------------------------------------------
  36. # 設置場所      
  37. #  なるべく上の方
  38. #
  39. # 必要スクリプト
  40. #    なし
  41. #==============================================================================
  42. $imported = {} if $imported == nil
  43. $imported["A1_Common_Script"] = 4.50
  44. #==============================================================================
  45. # ■ Kernel
  46. #==============================================================================
  47. module Kernel
  48.   #--------------------------------------------------------------------------
  49.   # ○ RGSSのバージョン取得
  50.   #--------------------------------------------------------------------------
  51.   def rgss_version
  52.     return 3 if defined? Graphics.play_movie
  53.     return 2 if defined? Graphics.resize_screen
  54.     return 1
  55.   end
  56.   #--------------------------------------------------------------------------
  57.   # ○ コモンスクリプトのバージョン取得
  58.   #--------------------------------------------------------------------------
  59.   def common_version
  60.     $imported["A1_Common_Script"]
  61.   end
  62.   #--------------------------------------------------------------------------
  63.   # ○ コモンスクリプトのバージョンが古い
  64.   #--------------------------------------------------------------------------
  65.   def old_common_script(script_name, version)
  66.     msgbox("#{script_name}にはA1共通スクリプトVer#{version}以上が必要です")
  67.   end
  68. end
  69. #==============================================================================
  70. # ■ A1_System
  71. #==============================================================================
  72. module A1_System
  73. end
  74. #==============================================================================
  75. # ■ A1_System::CommonModule
  76. #==============================================================================

  77. class A1_System::CommonModule
  78.   #--------------------------------------------------------------------------
  79.   # ○ 定数
  80.   #--------------------------------------------------------------------------
  81.   TWOBYTE_LIST = {
  82.                   " " => " ",
  83.                   "=" => "=",
  84.                   ":" => ":"
  85.                   }
  86.   #--------------------------------------------------------------------------
  87.   # ○ オブジェクト初期化
  88.   #--------------------------------------------------------------------------
  89.   def initialize
  90.     define_command
  91.   end
  92.   #--------------------------------------------------------------------------
  93.   # ○ 変換対象の全角文字を半角に置換
  94.   #--------------------------------------------------------------------------
  95.   def replace_twobyte(str)
  96.     for key in TWOBYTE_LIST.keys
  97.       str.gsub!(key) {TWOBYTE_LIST[key]}
  98.     end
  99.     return str
  100.   end
  101.   #--------------------------------------------------------------------------
  102.   # ○ マイナスが含まれている文字列を数値にする
  103.   #--------------------------------------------------------------------------
  104.   def minus_to_i(s)
  105.     if s[0,0] == "-"
  106.       s.gsub!("-","")
  107.       return s.to_i * -1
  108.     else
  109.       return s.to_i
  110.     end
  111.   end
  112.   #--------------------------------------------------------------------------
  113.   # ○ ミリ秒単位の現在時間
  114.   #--------------------------------------------------------------------------
  115.   def now_usec
  116.     now = Time.now
  117.     hour = now.hour * 60 * 60
  118.     min  = now.min * 60
  119.     sec  = now.sec
  120.     msec = (now.usec / 1000.0).round
  121.     return (hour + min + sec) * 1000 + msec
  122.   end
  123.   #--------------------------------------------------------------------------
  124.   # ○ イベントリストの作成
  125.   #--------------------------------------------------------------------------
  126.   def create_event_list(code, indent, parameters)
  127.     list = RPG::EventCommand.new
  128.     list.code       = code
  129.     list.indent     = indent
  130.     list.parameters = parameters
  131.     return list
  132.   end
  133.   #--------------------------------------------------------------------------
  134.   # ○ メソッド呼び出し
  135.   #--------------------------------------------------------------------------
  136.   def send_method(method_name)
  137.     return send(method_name) if respond_to?(method_name)
  138.   end
  139.   #--------------------------------------------------------------------------
  140.   # ○ オブジェクトの型を判断してStringならエンコード
  141.   #--------------------------------------------------------------------------
  142.   def encoding_string(obj)
  143.     obj.force_encoding("UTF-8") if obj.is_a?(String)
  144.     return obj
  145.   end
  146.   #--------------------------------------------------------------------------
  147.   # ○ メモの内容から必要な情報を取得
  148.   #--------------------------------------------------------------------------
  149.   def note_data(note, key)
  150.     result = []
  151.     note.each_line {|line|
  152.       next unless line =~ /<#{key}[ ]?(.*)>/
  153.       return true if $1.empty?
  154.       data = $a1_common.replace_twobyte($1).split(" ")
  155.       for st in data
  156.         result.push(st)
  157.       end
  158.     }
  159.     return false if result.empty?
  160.     return result
  161.   end
  162.   #--------------------------------------------------------------------------
  163.   # ○ メモの内容からハッシュを作成
  164.   #--------------------------------------------------------------------------
  165.   def note_data_hash(note, key, data_default = nil, default = {}, ret = {})
  166.     list = note_data_split(note, key)
  167.     return default if list.empty?
  168.     list.each {|data| ret[data[0]] = data[1] ? data[1] : data_default }
  169.     return ret
  170.   end
  171.   #--------------------------------------------------------------------------
  172.   # ○ メモの内容からカンマ区切りの多元配列を取得
  173.   #--------------------------------------------------------------------------
  174.   def note_data_split(note, key, default = [], ret = [])
  175.     data = note_data(note, key)
  176.     return default unless data.is_a?(Array)
  177.     data.each {|str| ret.push(convert_integer_from_array(split_array(str)))}
  178.     return ret
  179.   end
  180.   #--------------------------------------------------------------------------
  181.   # ○ 配列の内容から数値があれば変換
  182.   #--------------------------------------------------------------------------
  183.   def convert_integer_from_array(data, ret = [])
  184.     data.each {|str| ret.push(convert_integer(str))}
  185.     return ret
  186.   end
  187.   #--------------------------------------------------------------------------
  188.   # ○ 数値があれば変換
  189.   #--------------------------------------------------------------------------
  190.   def convert_integer(str)
  191.     return $1.to_i if str =~ /(^[-]?[0-9]+$)/
  192.     str.is_a?(Array) ? convert_integer_from_array(str) : str
  193.   end
  194.   #--------------------------------------------------------------------------
  195.   # ○ メモの内容から単項目の数値を取得
  196.   #--------------------------------------------------------------------------
  197.   def note_data_one_value(note, key, default)
  198.     data = note_data(note, key)
  199.     return data[0].to_i if data.is_a?(Array)
  200.     return default
  201.   end
  202.   #--------------------------------------------------------------------------
  203.   # ○ メモの内容から単項目を取得
  204.   #--------------------------------------------------------------------------
  205.   def note_data_one(note, key, default)
  206.     data = note_data(note, key)
  207.     return data[0] if data.is_a?(Array)
  208.     return default
  209.   end
  210.   #--------------------------------------------------------------------------
  211.   # ○ メモの内容からカンマ区切りの文字列を取得
  212.   #--------------------------------------------------------------------------
  213.   def note_data_array_str(note, key, default)
  214.     data = note_data(note, key)
  215.     return data[0].split(",") if data.is_a?(Array)
  216.     return default
  217.   end
  218.   #--------------------------------------------------------------------------
  219.   # ○ メモの内容からカンマ区切りの数値を取得
  220.   #--------------------------------------------------------------------------
  221.   def note_data_array_value(note, key, default)
  222.     data = note_data(note, key)
  223.     return default unless data.is_a?(Array)
  224.     return convert_integer_from_array(split_array(data[0]))
  225.   end
  226.   #--------------------------------------------------------------------------
  227.   # ○ カンマ区切りの文字列メモを変換
  228.   #--------------------------------------------------------------------------
  229.   def note_data_array(note, key, type, default = nil, through = true)
  230.     ret = []
  231.     default.each {|dat| ret.push(dat)} if default != nil
  232.    
  233.     data = note_data(note, key)
  234.     return ret unless data.is_a?(Array)
  235.    
  236.     data = data[0].split(",")
  237.     for d in data
  238.       next if ret.include?(d) if through
  239.       ret.push(d.to_i) if type.is_a?(Integer)
  240.       ret.push(d)      if type.is_a?(String)
  241.     end
  242.     return ret
  243.   end
  244.   #--------------------------------------------------------------------------
  245.   # ○ ディレクトリの作成
  246.   #--------------------------------------------------------------------------
  247.   def make_directory(dir_name)
  248.     Dir::mkdir(dir_name) unless FileTest.exist?(dir_name)
  249.   end
  250.   #--------------------------------------------------------------------------
  251.   # ○ コマンドリスト
  252.   #--------------------------------------------------------------------------
  253.   def make_command(command, src = "", dect = "")
  254.     src.gsub!("/","\\")
  255.     dect.gsub!("/","\\")
  256.     cmd = "#{command} \"#{src}\" \"#{dect}\""
  257.     return cmd
  258.   end
  259.   #--------------------------------------------------------------------------
  260.   # ○ 素材の拡張子を取得
  261.   #--------------------------------------------------------------------------
  262.   def material_ext(directory, file, direct = false)
  263.     exts = []
  264.     exts = [".png",".bmp",".jpg"]               if directory =~ /(.*)Graphics\/(.*)/
  265.     exts = [".mid",".ogg",".wav",".mp3",".wma"] if directory =~ /(.*)Audio(.*)/
  266.    
  267.     find_file = sprintf("%s%s", directory, file) unless direct
  268.     find_file = file if direct
  269.    
  270.     for ext in exts
  271.       return ext if File.exist?(sprintf("%s%s", find_file, ext))
  272.     end
  273.     return nil
  274.   end
  275.   #--------------------------------------------------------------------------
  276.   # ○ 素材が存在するかチェック
  277.   #--------------------------------------------------------------------------
  278.   def material_exist?(directory, file, direct = false)
  279.     return false if material_ext(directory, file, direct) == nil
  280.     return true
  281.   end
  282.   #--------------------------------------------------------------------------
  283.   # ○ ファイルコピー
  284.   #--------------------------------------------------------------------------
  285.   def copy_file(src, dest)
  286.     srcFile = File.open( src, "rb" )
  287.     dstFile = File.open( dest, "wb" )
  288.     dstFile.write( srcFile.read )
  289.     srcFile.close
  290.     dstFile.close
  291.   end
  292.   #--------------------------------------------------------------------------
  293.   # ○ ファイルの存在を確認してファイルコピー
  294.   #--------------------------------------------------------------------------
  295.   def material_copy(src, dest, directory)
  296.     ext = material_ext(directory, src, true)
  297.     copy_file( src + ext, dest + ext ) if ext != nil
  298.   end
  299.   #--------------------------------------------------------------------------
  300.   # ○ 配列からAudioを作成
  301.   #--------------------------------------------------------------------------
  302.   def set_audio(sound, kind)
  303.     case kind
  304.     when "BGM"; audio = RPG::BGM.new
  305.     when "BGS"; audio = RPG::BGS.new
  306.     when "ME";  audio = RPG::ME.new
  307.     when "SE";  audio = RPG::SE.new
  308.     end
  309.     audio.name   = sound[0]
  310.     audio.volume = sound[1]
  311.     audio.pitch  = sound[2]
  312.     return audio
  313.   end
  314.   #--------------------------------------------------------------------------
  315.   # ○ 既に準拠識別子を持っているかチェック
  316.   #--------------------------------------------------------------------------
  317.   def chk_rtp(file_name, default)
  318.     return "" if file_name =~ /^VX_.*/
  319.     return "" if file_name =~ /^XP_.*/
  320.     return "" if file_name =~ /^2000_.*/
  321.     return "" if file_name =~ /^2003_.*/
  322.     return default
  323.   end
  324.   #--------------------------------------------------------------------------
  325.   # ○ 先頭の $ を切り出す
  326.   #--------------------------------------------------------------------------
  327.   def one_character(file_name)
  328.     return file_name unless file_name[0] == "$"
  329.     tmp = file_name.clone
  330.     tmp[0] = ""
  331.     return tmp
  332.   end
  333.   #--------------------------------------------------------------------------
  334.   # ○ 配列を入れ替える
  335.   #--------------------------------------------------------------------------
  336.   def change_array(array, index1, index2)
  337.     tmp = array[index1]
  338.     array[index1] = array[index2]
  339.     array[index2] = tmp
  340.     return array
  341.   end
  342.   #--------------------------------------------------------------------------
  343.   # ○ 移動ルートの作成
  344.   #--------------------------------------------------------------------------
  345.   def create_move_route(repeat, skippable, wait, list)
  346.     move_route = RPG::MoveRoute.new
  347.     move_route.repeat    = repeat
  348.     move_route.skippable = skippable
  349.     move_route.wait      = wait
  350.     move_route.list      = list
  351.     return move_route
  352.   end
  353.   #--------------------------------------------------------------------------
  354.   # ○ 移動ルートコマンドの作成
  355.   #--------------------------------------------------------------------------
  356.   def create_move_command(code, parameters)
  357.     list = RPG::MoveCommand.new
  358.     list.code       = code
  359.     list.parameters = parameters
  360.     return list
  361.   end
  362.   #--------------------------------------------------------------------------
  363.   # ○ インタプリタ起動用リストの作成
  364.   #--------------------------------------------------------------------------
  365.   def create_list(code, indent, parameters)
  366.     list            = RPG::EventCommand.new
  367.     list.code       = code
  368.     list.indent     = indent
  369.     list.parameters = parameters
  370.     return list
  371.   end
  372.   #--------------------------------------------------------------------------
  373.   # ○ クラス名の取得
  374.   #--------------------------------------------------------------------------
  375.   def class_name(class_instance)
  376.     return class_instance.to_s.split(":")[0].gsub("#<","")
  377.   end
  378.   #--------------------------------------------------------------------------
  379.   # ○ 配列を考慮したsplit
  380.   #--------------------------------------------------------------------------
  381.   def split_array(str)
  382.     str = convert_escape_characters(str)
  383.    
  384.     ret       = []
  385.     tmp_array = str.split(",")
  386.    
  387.     return strip_array_str(tmp_array) unless str.include?("[")
  388.     tmp_str   = ""
  389.     tmp_array.each {|s|
  390.       if char_in_str(s, "[", "]") && tmp_str.empty?
  391.         ret.push(s) unless s =~ /^\[/
  392.         ret.push([s[1...s.size-1]]) if s =~ /^\[/
  393.       else
  394.         tmp_str = "#{tmp_str}#{s},"
  395.         if char_in_str(tmp_str, "[", "]")
  396.           unless tmp_str =~ /^\[/
  397.             ret.push(tmp_str[0...tmp_str.size-1])
  398.             tmp_str = ""
  399.           else
  400.             tmp_str = tmp_str[1...tmp_str.size-2]
  401.             tmp_str = split_array(tmp_str) if tmp_str.include?("[")
  402.             tmp_str = tmp_str.split(",") if tmp_str.include?(",")
  403.             ret.push(tmp_str)   if tmp_str.is_a?(Array)
  404.             ret.push([tmp_str]) if !tmp_str.is_a?(Array)
  405.             tmp_str = ""
  406.           end
  407.         end
  408.       end
  409.     }
  410.     return strip_array_str(ret)
  411.   end
  412.   #--------------------------------------------------------------------------
  413.   # ○ 配列の中の文字列の先頭と末尾の空白を除去
  414.   #--------------------------------------------------------------------------
  415.   def strip_array_str(array, ret = [])
  416.     array.each {|str| ret.push(strip_array_str(str)) if str.is_a?(Array); next if str.is_a?(Array); ret.push(str.strip) }
  417.     return ret
  418.   end
  419.   #--------------------------------------------------------------------------
  420.   # ○ 文字列の中に文字が何文字含まれているか調べて同数ならtrueを返す
  421.   #--------------------------------------------------------------------------
  422.   def char_in_str(str, c1, c2)
  423.     num1 = 0
  424.     num2 = 0
  425.     (0...str.size).each {|i| num1 += 1 if str[i] == c1; num2 += 1 if str[i] == c2 }
  426.     return num1 == num2
  427.   end
  428.   #--------------------------------------------------------------------------
  429.   # ○ 制御文字の変換
  430.   #--------------------------------------------------------------------------
  431.   def convert_escape_characters(text)
  432.     result = text.to_s.clone
  433.     result.gsub!(/\\/)                           { "\e" }
  434.     result.gsub!(/\e\e/)                         { "\\" }
  435.     result.gsub!(/\eV\[(\d+)\]/i)                { $game_variables[$1.to_i] }
  436.     result.gsub!(/\eV\[(\d+)\]/i)                { $game_variables[$1.to_i] }
  437.     result.gsub!(/\eN\[(\d+)\]/i)                { actor_name($1.to_i) }
  438.     result.gsub!(/\eP\[(\d+)\]/i)                { party_member_name($1.to_i) }
  439.     result.gsub!(/\eG/i)                         { Vocab::currency_unit }
  440.     loop { result = result.sub(/<s>(.+?)<\/s>/i) { eval($1) }; break unless $1 }
  441.     result
  442.   end
  443.   #--------------------------------------------------------------------------
  444.   # ○ アクター n 番の名前を取得
  445.   #--------------------------------------------------------------------------
  446.   def actor_name(n)
  447.     actor = n >= 1 ? $game_actors[n] : nil
  448.     actor ? actor.name : ""
  449.   end
  450.   #--------------------------------------------------------------------------
  451.   # ○ パーティメンバー n 番の名前を取得
  452.   #--------------------------------------------------------------------------
  453.   def party_member_name(n)
  454.     actor = n >= 1 ? $game_party.members[n - 1] : nil
  455.     actor ? actor.name : ""
  456.   end
  457.   #--------------------------------------------------------------------------
  458.   # ○ 配列を全て整数にする
  459.   #--------------------------------------------------------------------------
  460.   def params_to_i(params)
  461.     ret = []
  462.     params.each {|param| ret.push(param.to_i)}
  463.     return ret
  464.   end
  465.   #--------------------------------------------------------------------------
  466.   # ○ 注釈コマンド定義
  467.   #--------------------------------------------------------------------------
  468.   def define_command
  469.     @cmd_108 = {}
  470.   end
  471.   #--------------------------------------------------------------------------
  472.   # ○ 注釈コマンド定義取得
  473.   #--------------------------------------------------------------------------
  474.   def cmd_108
  475.     @cmd_108
  476.   end
  477.   #--------------------------------------------------------------------------
  478.   # ○ フレーム更新
  479.   #--------------------------------------------------------------------------
  480.   def update
  481.   end
  482.   #--------------------------------------------------------------------------
  483.   # ○ 文字の幅と高さを取得
  484.   #--------------------------------------------------------------------------
  485.   def text_size(font, size, text)
  486.     bitmap = Cache.system("")
  487.     bitmap.font.name = font
  488.     bitmap.font.size = size
  489.     tw = bitmap.text_size(text).width
  490.     th = bitmap.text_size(text).height
  491.     bitmap.dispose
  492.     return [tw, th]
  493.   end
  494.   #--------------------------------------------------------------------------
  495.   # ○ 文字の幅を取得
  496.   #--------------------------------------------------------------------------
  497.   def text_width(font, text)
  498.     texts = text.split("\n")
  499.     @max_width = 0
  500.     texts.each {|text|
  501.       width = text_size(font.name, font.size, text)[0]
  502.       @max_width = @max_width < width ? width : @max_width
  503.     }
  504.     return @max_width
  505.   end
  506. end
  507. #==============================================================================
  508. # ◆ RGSS3用処理
  509. #==============================================================================
  510. if rgss_version == 3
  511. #==============================================================================
  512. # ■ RPG::Tileset
  513. #==============================================================================

  514. class RPG::Tileset
  515.   #--------------------------------------------------------------------------
  516.   # ○ 拡張通行判定
  517.   #--------------------------------------------------------------------------
  518.   def ex_flags
  519.     @ex_flags ||= Table.new(8192)
  520.     return @ex_flags
  521.   end
  522.   #--------------------------------------------------------------------------
  523.   # ○ 拡張通行判定初期化
  524.   #--------------------------------------------------------------------------
  525.   def init_ex_flags
  526.     @ex_flags = Table.new(8192)
  527.   end
  528. end
  529. #==============================================================================
  530. # ■ Game_Interpreter
  531. #------------------------------------------------------------------------------
  532. #  イベントコマンドを実行するインタプリタです。このクラスは Game_Map クラス、
  533. # Game_Troop クラス、Game_Event クラスの内部で使用されます。
  534. #==============================================================================

  535. class Game_Interpreter
  536.   #--------------------------------------------------------------------------
  537.   # ☆ オブジェクト初期化
  538.   #     depth : ネストの深さ
  539.   #--------------------------------------------------------------------------
  540.   alias a1_common_gi_rgss3_initialize initialize
  541.   def initialize(depth = 0, sub_interpreter = false)
  542.     @sub_interpreter = sub_interpreter
  543.     a1_common_gi_rgss3_initialize(depth)
  544.   end
  545.   #--------------------------------------------------------------------------
  546.   # ☆ メッセージ表示がビジー状態の間ウェイト
  547.   #--------------------------------------------------------------------------
  548.   alias a1_common_gi_wait_for_message wait_for_message
  549.   def wait_for_message
  550.     return if @sub_interpreter
  551.     a1_common_gi_wait_for_message
  552.   end
  553. end
  554. #==============================================================================
  555. # ■ Window_Message
  556. #------------------------------------------------------------------------------
  557. #  文章表示に使うメッセージウィンドウです。
  558. #==============================================================================

  559. class Window_Message < Window_Base
  560.   #--------------------------------------------------------------------------
  561.   # ☆ 通常文字の処理
  562.   #--------------------------------------------------------------------------
  563.   alias a1_common_wm_process_normal_character process_normal_character
  564.   def process_normal_character(c, pos)
  565.     wait_for_one_character_before
  566.     a1_common_wm_process_normal_character(c, pos)
  567.   end
  568.   #--------------------------------------------------------------------------
  569.   # ○ 一文字出力前のウェイト
  570.   #--------------------------------------------------------------------------
  571.   def wait_for_one_character_before
  572.   end
  573. end
  574. #==============================================================================
  575. # ■ RPG::Map
  576. #==============================================================================

  577. class RPG::Map
  578.   #--------------------------------------------------------------------------
  579.   # ○ マップチップを調べるか判定する
  580.   #--------------------------------------------------------------------------
  581.   def search_map_chip?
  582.     return true if $a1_common.note_data(self.note, "マップチップサーチ")
  583.     return false
  584.   end
  585. end
  586. #==============================================================================
  587. # ■ Game_Map
  588. #------------------------------------------------------------------------------
  589. #  マップを扱うクラスです。スクロールや通行可能判定などの機能を持っています。
  590. # このクラスのインスタンスは $game_map で参照されます。
  591. #==============================================================================

  592. class Game_Map
  593.   #--------------------------------------------------------------------------
  594.   # ☆ セットアップ
  595.   #--------------------------------------------------------------------------
  596.   alias a1_common_gm_setup setup
  597.   def setup(map_id)
  598.     a1_common_gm_setup(map_id)
  599.     setup_tileset
  600.     map_chip_search if search_map_chip?
  601.   end
  602.   #--------------------------------------------------------------------------
  603.   # ★ タイルセットの取得
  604.   #--------------------------------------------------------------------------
  605.   def tileset
  606.     setup_tileset unless @tileset && @now_tileset_id == @tileset_id
  607.     return @tileset
  608.   end
  609.   #--------------------------------------------------------------------------
  610.   # ○ タイルセットのセットアップ
  611.   #--------------------------------------------------------------------------
  612.   def setup_tileset
  613.     @tileset        = $data_tilesets[@tileset_id].clone
  614.     @tileset.flags  = $data_tilesets[@tileset_id].flags.clone
  615.     @now_tileset_id = @tileset_id
  616.   end
  617.   #--------------------------------------------------------------------------
  618.   # ○ マップチップを調べるか判定する
  619.   #--------------------------------------------------------------------------
  620.   def search_map_chip?
  621.     return @map.search_map_chip?
  622.   end
  623.   #--------------------------------------------------------------------------
  624.   # ○ 指定座標の全レイヤーのフラグ判定(イベント含む)
  625.   #--------------------------------------------------------------------------
  626.   def all_tiles_flag?(x, y, bit)
  627.     all_tiles(x, y).any? {|tile_id| tileset.flags[tile_id] & bit != 0 }
  628.   end
  629.   #--------------------------------------------------------------------------
  630.   # ○ 指定座標の全レイヤーの拡張フラグ判定(イベント含む)
  631.   #--------------------------------------------------------------------------
  632.   def all_tiles_flag_ex?(x, y, bit)
  633.     all_tiles(x, y).any? {|tile_id| tileset.ex_flags[tile_id] & bit != 0 }
  634.   end
  635.   #--------------------------------------------------------------------------
  636.   # ○ 指定座標の全レイヤーの拡張フラグ判定
  637.   #--------------------------------------------------------------------------
  638.   def layered_tiles_flag_ex?(x, y, bit)
  639.     layered_tiles(x, y).any? {|tile_id| tileset.ex_flags[tile_id] & bit != 0 }
  640.   end
  641.   #--------------------------------------------------------------------------
  642.   # ○ 地形タグの取得(イベント含む)
  643.   #--------------------------------------------------------------------------
  644.   def terrain_tag_all_tailes(x, y)
  645.     return 0 unless valid?(x, y)
  646.     all_tiles(x, y).each do |tile_id|
  647.       tag = tileset.flags[tile_id] >> 12
  648.       return tag if tag > 0
  649.     end
  650.     return 0
  651.   end
  652. end
  653. #==============================================================================
  654. # ■ DataManager
  655. #------------------------------------------------------------------------------
  656. #  データベースとゲームオブジェクトを管理するモジュールです。ゲームで使用する
  657. # ほぼ全てのグローバル変数はこのモジュールで初期化されます。
  658. #==============================================================================

  659. module DataManager
  660.   #--------------------------------------------------------------------------
  661.   # ○ エイリアス用特異メソッド
  662.   #--------------------------------------------------------------------------
  663.   class << self
  664.     alias :a1_common_create_game_objects :create_game_objects
  665.   end
  666.   #--------------------------------------------------------------------------
  667.   # ☆ 各種ゲームオブジェクトの作成
  668.   #--------------------------------------------------------------------------
  669.   def self.create_game_objects
  670.     $a1_common ||= A1_System::CommonModule.new
  671.     a1_common_create_game_objects
  672.   end
  673. end
  674. #==============================================================================
  675. # ■ Scene_Base
  676. #------------------------------------------------------------------------------
  677. #  ゲーム中の全てのシーンのスーパークラスです。
  678. #==============================================================================

  679. class Scene_Base
  680.   #--------------------------------------------------------------------------
  681.   # ☆ フレーム更新(基本)
  682.   #--------------------------------------------------------------------------
  683.   alias a1_common_sb_update_basic update_basic
  684.   def update_basic
  685.     a1_common_sb_update_basic
  686.     $a1_common.update
  687.   end
  688.   #--------------------------------------------------------------------------
  689.   # ○ 指定のウィンドウが開いている間ウェイト
  690.   #--------------------------------------------------------------------------
  691.   def wait_for_window_open(window)
  692.     update_basic until window.openness == 0
  693.   end
  694. end
  695. #==============================================================================
  696. # ■ Window_Base
  697. #------------------------------------------------------------------------------
  698. #  ゲーム中の全てのウィンドウのスーパークラスです。
  699. #==============================================================================

  700. class Window_Base < Window
  701.   #--------------------------------------------------------------------------
  702.   # ★ 制御文字の事前変換
  703.   #    実際の描画を始める前に、原則として文字列に変わるものだけを置き換える。
  704.   #    文字「\」はエスケープ文字(\e)に変換。
  705.   #--------------------------------------------------------------------------
  706.   def convert_escape_characters(text)
  707.     return $a1_common.convert_escape_characters(text)
  708.   end
  709. end
  710. #==============================================================================
  711. # ◆ RGSS2用処理
  712. #==============================================================================
  713. elsif rgss_version == 2
  714. #==============================================================================
  715. # ■ Window
  716. #==============================================================================

  717. class Window
  718.   #--------------------------------------------------------------------------
  719.   # ○ ウィンドウが開いている?
  720.   #--------------------------------------------------------------------------
  721.   def open?
  722.     return self.openness == 255
  723.   end
  724.   #--------------------------------------------------------------------------
  725.   # ○ ウィンドウが閉じている?
  726.   #--------------------------------------------------------------------------
  727.   def close?
  728.     return self.openness == 0
  729.   end
  730. end
  731. #==============================================================================
  732. # ■ Cache
  733. #------------------------------------------------------------------------------
  734. #  各種グラフィックを読み込み、Bitmap オブジェクトを作成、保持するモジュール
  735. # です。読み込みの高速化とメモリ節約のため、作成した Bitmap オブジェクトを内部
  736. # のハッシュに保存し、同じビットマップが再度要求されたときに既存のオブジェクト
  737. # を返すようになっています。
  738. #==============================================================================

  739. module Cache
  740.   #--------------------------------------------------------------------------
  741.   # ○ キャッシュ存在チェック
  742.   #--------------------------------------------------------------------------
  743.   def self.include?(key)
  744.     @cache[key] && !@cache[key].disposed?
  745.   end
  746. end
  747. #==============================================================================
  748. # ■ Game_Interpreter
  749. #------------------------------------------------------------------------------
  750. #  イベントコマンドを実行するインタプリタです。このクラスは Game_Map クラス、
  751. # Game_Troop クラス、Game_Event クラスの内部で使用されます。
  752. #==============================================================================

  753. class Game_Interpreter
  754.   #--------------------------------------------------------------------------
  755.   # ○ 注釈
  756.   #--------------------------------------------------------------------------
  757.   def command_108
  758.     @comments = [@params[0]]
  759.     while next_event_code == 408
  760.       @index += 1
  761.       @comments.push(@list[@index].parameters[0])
  762.     end
  763.   end
  764.   #--------------------------------------------------------------------------
  765.   # ★ イベントコマンドの実行
  766.   #--------------------------------------------------------------------------
  767.   def execute_command
  768.     return rgss3_execute_command unless @index >= @list.size-1
  769.     command_end
  770.     return true
  771.   end
  772.   #--------------------------------------------------------------------------
  773.   # ○ RGSS3風「イベントコマンドの実行」
  774.   #--------------------------------------------------------------------------
  775.   def rgss3_execute_command
  776.     command = @list[@index]
  777.     @params = command.parameters
  778.     @indent = command.indent
  779.     method_name = "command_#{command.code}"
  780.     send(method_name) if respond_to?(method_name)
  781.   end
  782.   #--------------------------------------------------------------------------
  783.   # ○ 次のイベントコマンドのコードを取得
  784.   #--------------------------------------------------------------------------
  785.   def next_event_code
  786.     @list[@index + 1].code
  787.   end
  788. end
  789. #==============================================================================
  790. # ■ Game_Map
  791. #------------------------------------------------------------------------------
  792. #  マップを扱うクラスです。スクロールや通行可能判定などの機能を持っています。
  793. # このクラスのインスタンスは $game_map で参照されます。
  794. #==============================================================================

  795. class Game_Map
  796.   #--------------------------------------------------------------------------
  797.   # ○ マップチップを調べるか判定する
  798.   #--------------------------------------------------------------------------
  799.   def search_map_chip?
  800.     return $data_map_infos[@map_id].name =~ /\[サーチ\]/
  801.   end
  802.   #--------------------------------------------------------------------------
  803.   # ○ 指定座標に存在するタイル扱いイベント(すり抜け以外)の配列取得
  804.   #--------------------------------------------------------------------------
  805.   def tile_events_xy(x, y)
  806.     @tile_events.select {|event| event.pos_nt?(x, y) }
  807.   end
  808.   #--------------------------------------------------------------------------
  809.   # ○ タイル扱いイベントの配列をリフレッシュ
  810.   #--------------------------------------------------------------------------
  811.   def refresh_tile_events
  812.     @tile_events = @events.values.select {|event| event.tile? }
  813.   end
  814. end
  815. #==============================================================================
  816. # ■ Game_Character
  817. #------------------------------------------------------------------------------
  818. #  キャラクターを扱うクラスです。このクラスは Game_Player クラスと Game_Event
  819. # クラスのスーパークラスとして使用されます。
  820. #==============================================================================

  821. class Game_Character
  822.   #--------------------------------------------------------------------------
  823.   # ○ タイル判定
  824.   #--------------------------------------------------------------------------
  825.   def tile?
  826.     @tile_id > 0 && @priority_type == 0
  827.   end
  828. end
  829. #==============================================================================
  830. # ■ Scene_Title
  831. #------------------------------------------------------------------------------
  832. #  タイトル画面の処理を行うクラスです。
  833. #==============================================================================

  834. class Scene_Title < Scene_Base
  835.   #--------------------------------------------------------------------------
  836.   # ☆ データベースのロード
  837.   #--------------------------------------------------------------------------
  838.   alias a1_common_st_load_database load_database
  839.   def load_database
  840.     a1_common_st_load_database
  841.     $data_map_infos = load_data("Data/MapInfos.rvdata")
  842.   end
  843.   #--------------------------------------------------------------------------
  844.   # ☆ 各種ゲームオブジェクトの作成
  845.   #--------------------------------------------------------------------------
  846.   alias a1_common_st_create_game_objects create_game_objects
  847.   def create_game_objects
  848.     $a1_common ||= A1_System::CommonModule.new
  849.     a1_common_st_create_game_objects
  850.   end
  851. end
  852. #==============================================================================
  853. # ◆ RGSS用処理
  854. #==============================================================================
  855. elsif rgss_version == 1
  856. end
  857. #==============================================================================
  858. # ■ Cache
  859. #------------------------------------------------------------------------------
  860. #  各種グラフィックを読み込み、Bitmap オブジェクトを作成、保持するモジュール
  861. # です。読み込みの高速化とメモリ節約のため、作成した Bitmap オブジェクトを内部
  862. # のハッシュに保存し、同じビットマップが再度要求されたときに既存のオブジェクト
  863. # を返すようになっています。
  864. #==============================================================================

  865. module Cache
  866.   #--------------------------------------------------------------------------
  867.   # ○ 拡大縮小したビットマップのロード
  868.   #--------------------------------------------------------------------------
  869.   def self.load_resize_bitmap(load_path, key, resize = nil)
  870.     @cache ||= {}
  871.     key = load_path if key == nil
  872.     return @cache[key] if include?(key)
  873.    
  874.     @cache[key] = Bitmap.new(load_path)
  875.     return @cache[key] if resize == nil
  876.     return @cache[key] if @cache[key].width == resize[0] and @cache[key].height == resize[1]
  877.    
  878.     info = calc_size(resize, key)
  879.     return resize_bitmap(@cache[key], info[0], info[1], key)
  880.   end
  881.   #--------------------------------------------------------------------------
  882.   # ○ 拡大縮小した色相変化済みビットマップを作成/取得
  883.   #--------------------------------------------------------------------------
  884.   def self.load_resize_hue_changed_bitmap(load_path, path, hue, resize)
  885.     key = [path, hue]
  886.     return @cache[key] if include?(key)
  887.    
  888.     @cache[key] = load_resize_bitmap(load_path, path, resize).clone
  889.     @cache[key].hue_change(hue)
  890.     return @cache[key]
  891.   end
  892.   #--------------------------------------------------------------------------
  893.   # ○ リサイズするサイズを取得
  894.   #--------------------------------------------------------------------------
  895.   def self.calc_size(resize, key)
  896.     width  = resize[0]
  897.     width  = @cache[key].width * width.abs if width < 0
  898.     height = resize[1]
  899.     height = @cache[key].height * height.abs if height < 0
  900.     height = Integer(@cache[key].height * (width.to_f / @cache[key].width.to_f)) if height == 0
  901.     return [width, height]
  902.   end
  903.   #--------------------------------------------------------------------------
  904.   # ○ ビットマップの拡大縮小
  905.   #--------------------------------------------------------------------------
  906.   def self.resize_bitmap(bitmap, width, height, key)
  907.     resize = Bitmap.new(width, height)
  908.     resize.stretch_blt(resize.rect, bitmap, bitmap.rect)
  909.     @cache[key] = resize
  910.     return resize
  911.   end
  912.   #--------------------------------------------------------------------------
  913.   # ○ テキストビットマップの取得
  914.   #--------------------------------------------------------------------------
  915.   def self.text_picture(text, font)
  916.     load_text_bitmap(text, font)
  917.   end
  918.   #--------------------------------------------------------------------------
  919.   # ○ フォントのキーを作成
  920.   #--------------------------------------------------------------------------
  921.   def self.make_font_key(text, font)
  922.     [text, font.name, font.size, font.bold, font.italic, font.outline, font.shadow, font.color.to_s, font.out_color.to_s]
  923.   end
  924.   #--------------------------------------------------------------------------
  925.   # ○ テキストビットマップの作成
  926.   #--------------------------------------------------------------------------
  927.   def self.load_text_bitmap(text, font)
  928.     @cache ||= {}
  929.     key = make_font_key(text, font)
  930.     return @cache[key] if include?(key)
  931.    
  932.     # 計算用ダミービットマップ
  933.     bitmap = Cache.system("")
  934.     bitmap.font = font
  935.     tw = bitmap.text_size(text).width + 8
  936.    
  937.     # ビットマップ作成
  938.     bitmap = Bitmap.new(tw, bitmap.font.size + 4)
  939.     bitmap.font = font
  940.     bitmap.draw_text(0, 0, bitmap.width, bitmap.height, text, 1)
  941.    
  942.     @cache[key] = bitmap
  943.     return @cache[key]
  944.   end
  945. end
  946. #==============================================================================
  947. # ■ Game_Interpreter
  948. #------------------------------------------------------------------------------
  949. #  イベントコマンドを実行するインタプリタです。このクラスは Game_Map クラス、
  950. # Game_Troop クラス、Game_Event クラスの内部で使用されます。
  951. #==============================================================================

  952. class Game_Interpreter
  953.   #--------------------------------------------------------------------------
  954.   # ○ 注釈
  955.   #--------------------------------------------------------------------------
  956.   alias a1_common_command_108 command_108
  957.   def command_108
  958.     a1_common_command_108
  959.     proc_comment(@comments)
  960.   end
  961.   #--------------------------------------------------------------------------
  962.   # ○ 注釈の処理
  963.   #--------------------------------------------------------------------------
  964.   def proc_comment(comments)
  965.     param = ""
  966.     comments.each {|comment| param += comment }
  967.     params = param.sub(/^(\S+)/, "")
  968.     command = $1
  969.     comment_parameters = $a1_common.split_array(params) if params
  970.     proc_comment_command(command, comment_parameters)
  971.   end
  972.   #--------------------------------------------------------------------------
  973.   # ○ 注釈の実行
  974.   #--------------------------------------------------------------------------
  975.   def proc_comment_command(command, params)
  976.     cmd_108 = $a1_common.cmd_108[command]
  977.     method(cmd_108).call(params) if cmd_108 != nil
  978.   end
  979.   #--------------------------------------------------------------------------
  980.   # ○ 前のイベントコマンドを取得
  981.   #--------------------------------------------------------------------------
  982.   def prev_event
  983.     @list[@index - 1]
  984.   end
  985. end
  986. #==============================================================================
  987. # ■ Game_Map
  988. #------------------------------------------------------------------------------
  989. #  マップを扱うクラスです。スクロールや通行可能判定などの機能を持っています。
  990. # このクラスのインスタンスは $game_map で参照されます。
  991. #==============================================================================

  992. class Game_Map
  993.   #--------------------------------------------------------------------------
  994.   # ○ 全マップチップを調べる
  995.   #--------------------------------------------------------------------------
  996.   def map_chip_search
  997.     tileset.init_ex_flags
  998.     ([email protected]).each {|x| map_chip_search_y(x) }
  999.   end
  1000.   #--------------------------------------------------------------------------
  1001.   # ○ x座標にあるy座標のマップチップを調べる
  1002.   #--------------------------------------------------------------------------
  1003.   def map_chip_search_y(x)
  1004.     ([email protected]).each {|y| map_pos_proc(x, y); map_chip_search_z(x, y) }
  1005.   end
  1006.   #--------------------------------------------------------------------------
  1007.   # ○ x,y座標にあるz座標のマップチップを調べる
  1008.   #--------------------------------------------------------------------------
  1009.   def map_chip_search_z(x, y)
  1010.     ([email protected]).each {|z| map_chip_proc(x, y, z) }
  1011.     tile_events_xy(x, y).collect {|ev| tile_event_proc(ev.tile_id) }
  1012.   end
  1013.   #--------------------------------------------------------------------------
  1014.   # ○ 座標に対して処理を行う
  1015.   #--------------------------------------------------------------------------
  1016.   def map_pos_proc(x, y)
  1017.   end
  1018.   #--------------------------------------------------------------------------
  1019.   # ○ マップチップに対して処理を行う
  1020.   #--------------------------------------------------------------------------
  1021.   def map_chip_proc(x, y, z)
  1022.   end
  1023.   #--------------------------------------------------------------------------
  1024.   # ○ タイルのイベントに対して処理を行う
  1025.   #--------------------------------------------------------------------------
  1026.   def tile_event_proc(tile_id)
  1027.   end
  1028. end
  1029. #==============================================================================
  1030. # ■ Window_Base
  1031. #------------------------------------------------------------------------------
  1032. #  ゲーム中の全てのウィンドウのスーパークラスです。
  1033. #==============================================================================

  1034. class Window_Base < Window
  1035.   #--------------------------------------------------------------------------
  1036.   # ○ 入力を受け付けるか?
  1037.   #--------------------------------------------------------------------------
  1038.   def can_input?
  1039.     @can_input
  1040.   end
  1041.   #--------------------------------------------------------------------------
  1042.   # ○ 入力受け付け設定
  1043.   #--------------------------------------------------------------------------
  1044.   def can_input=(flag)
  1045.     @can_input = flag
  1046.   end
  1047. end
  1048. #==============================================================================
  1049. # ■ Bitmap
  1050. #==============================================================================

  1051. class Bitmap
  1052.   #--------------------------------------------------------------------------
  1053.   # ○ 文字縁取り描画
  1054.   #--------------------------------------------------------------------------
  1055.   def draw_text_f(x, y, width, height, str, align = 0, color = Color.new(64,32,128))
  1056.     shadow = self.font.shadow
  1057.     b_color = self.font.color.dup
  1058.     font.shadow = false
  1059.     font.color = color
  1060.     draw_text(x + 1, y, width, height, str, align)
  1061.     draw_text(x - 1, y, width, height, str, align)
  1062.     draw_text(x, y + 1, width, height, str, align)
  1063.     draw_text(x, y - 1, width, height, str, align)
  1064.     font.color = b_color
  1065.     draw_text(x, y, width, height, str, align)
  1066.     font.shadow = shadow
  1067.   end
  1068.   #--------------------------------------------------------------------------
  1069.   # ○ 文字縁取り描画の矩形を取得
  1070.   #--------------------------------------------------------------------------
  1071.   def draw_text_f_rect(r, str, align = 0, color = Color.new(64,32,128))
  1072.     draw_text_f(r.x, r.y, r.width, r.height, str, align = 0, color)
  1073.   end
  1074. end
复制代码

A1的要放在上面,ネームウィンドウ on的是要放在事情的注释里
然后每一次对话的话加个NW名前指定 Name
如果想不麻烦点可以再名前指定下面的NAME_LLIST加上文件名和文件名所对应的名字,实在不知道查翻译去
好累啊,今天又是不睡觉的一天
回复 支持 反对

使用道具 举报

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

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-9-8 09:05

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表