赞 | 400 |
VIP | 0 |
好人卡 | 24 |
积分 | 250 |
经验 | 45372 |
最后登录 | 2024-7-2 |
在线时间 | 3339 小时 |
Lv5.捕梦者 (版主)
- 梦石
- 1
- 星屑
- 23994
- 在线时间
- 3339 小时
- 注册时间
- 2011-7-8
- 帖子
- 3926
|
2楼
楼主 |
发表于 2018-7-25 20:19:54
|
只看该作者
本帖最后由 guoxiaomi 于 2018-7-26 01:40 编辑
剧本系统
把游戏的剧情文本写在游戏外部,方便后续的更改。可能增加一些新功能,比如,特定剧情的出现条件等。
剧本系统尽量使用markdown的语法,会读取#开头的段落并将后续的文本导入到一个hash里缓存。目前有以下功能:
- 使用>表示注释,引用的内容会被忽略
- 使用```括起的内容,会转换成事件脚本
- 使用![name](pic_name "1")表示“显示文章”的头像
- 普通的文本会转化为“显示文章”的内容,
- 如果普通文本之后出现了空行,会转化为下一个显示文章
- 普通文本末尾的<br>会自动忽略
- 使用`作为内嵌表达式,会在生成事件的时候求值
- 即载入地图前
- 仅对显示文章有效
- 调用事件的 auto_reload 方法会重新求值
- 使用有序列表(比如1. xxx)表示选择项,选择项只有跳转到对应标签的功能,取消的话会继续执行
- 选择项之间不要空开,选择项和后面的文本要空开
- 在列表项后面插入深一层的无序列表,以控制选项出现的条件(见后面)
- 调用事件的 auto_reload 方法会重新计算条件
- 使用<email@com>的格式表示一些特殊的“可见”指令
- <CommonEvent@16> 调用16号公共事件,注意调用公共事件后会返回
- <GetItem@16> 选择物品,ID保存在16号变量里
- <JumpTo@chapter> 标签跳转
- <AutoEvent@reload> 重新载入本事件
- 使用 [sth]: para1 "para2"表示一些特殊的“不可见”指令
- 这个格式的引用在markdown中不显示
- [Balloon]: event "surprise" 显示气泡
- [SelfSwitch]: A "ON" 打开独立开关
- [Dialog]: setting "ON" 对话加强脚本设置
- [Move]: -1 "w3 S" 设置移动路线
- [PlaySE]: file "80" 播放SE
- 使用表格表示商店
- 商店已经完成,后续其他的想法再说
- 表格前后都要有空行
- 使用[chapter](condition)表示有条件的标签跳转,支持短跳
- 转换成条件分歧-脚本
- 默认会在条件分歧后创建标签:#chapter,从而允许执行跳转后再回来
- (高级) 使用[cmd] [paras]的格式输入任意事件指令
- 中间必须有一个空格
- 后面的[paras]是一个数组,eval求值后作为参数
- indent = 0
选项出现的条件
如下所示,多个条件的话必须全部满足。
- 我是文本:
- 1. 选项1
- 2. 金币大于 100 才会出现的选项
- - `$game_party.gold > 100`
复制代码
商店格式
商店格式,可以支持多个物品。
- 第1列是道具种类,第2列是id,最后一列是价格,如果价格为'-',则用默认的价格
- 第二个参数 P = Purchase Only 表示不可卖出。只识别第一个字母
其他指令说明
- # 这个脚本会重新载入事件页,从而对内嵌表达式和选项出现的条件重新求值
- $game_map.events[@event_id].auto_reload
- # 可以使用 <AutoEvent@reload> 替代
复制代码 设置移动路线
设置移动路线的格式如下:[Move]: -1 "w2 X1":
- 第1项是角色的id:-1表示Player,0表示本事件,1以上是事件的id。
- 如果第一个参数是字符串,会搜索同名的事件
- 原理是在“设置移动路线”的指令前面加了一段脚本修改了后续事件的内容
- @list[@index + 1].parameters[1] = ...
- 使用 [Moves] 使得事件设置为:不等待移动结束
- 第2项的参数用空格隔开,每一项对应成移动指令,具体规则见下表。区分大小写。
代码 | 说明 | 移动 | - | w1 | 向上移动 1 步 | a2 | 向左移动 2 步 | s3 | 向下移动 3 步 | d4 | 向右移动 4 步 | f5 | 向前 5 步 | b6 | 向后 6 步 | j7.-8 | 跳跃 7, -8 | J9.10 | 传送到 9, 10 | 转向 | - | W | 脸朝上 | A | 脸朝左 | S | 脸朝下 | D | 脸朝右 | L | 左转90度 | R | 右转90度 | B | 转180度 | FIX | 面向固定 ON | fix | 面向固定 OFF | 设置 | - | PASS | 穿透 ON | pass | 穿透 OFF | v1 | 设置移动速度 1 | V2 | 设置移动频率 2 | STEP | 踏步动画 ON | step | 踏步动画 OFF | 透明 | - | T | 透明 ON | t | 透明 OFF | o128 | 调整透明度为 128 | 控制 | - | W60 | 等待 60 帧 | X1 | 打开 1 号开关 | x2 | 关闭 2 号开关 |
解析用代码:
# functions: # auto load event list from the txt file with same name. class Game_Event < Game_Character alias _auto_load_setup_page_settings setup_page_settings def setup_page_settings _auto_load_setup_page_settings if @list[0].code == 355 && @list[0].parameters[0] =~ /AUTO:(.+)/ auto_load(@event.name, $1.strip) elsif @auto_load_keys auto_load *@auto_load_keys end end def auto_load(name, key) @auto_load_keys = [name, key] @list.replace Auto_Event.load(name).list(key) end def name @event.name end def auto_reload @page = nil refresh end end module Auto_Event module_function Event_List = {} def reload # only after .md file changes Event_List.keys.each do |name| Event_List[name] = load_list_txt(name) end end def load(name) Event_List[name] ||= load_list_txt(name) @data = Event_List[name] @list = [] @keys = [] self end def load_list_txt(name) data, key, value = {}, '', [] File.open('Events/' + name + '.md', 'r') do |f| f.readlines.each do |line| # start with # key if line =~ /^#+\s*(.+)/ data[key] = value.join("\n") if key != '' key, value = $1, [] next else value << line.strip next end end # last key data[key] = value.join("\n") if key != '' end data end def list(key) @keys << key event_keys = [] while @keys.size != event_keys.size @keys.uniq! @keys.each do |key| next if event_keys.include?(key) event_keys << key next if !@data[key] add(118, 0, [key]) set_text(@data[key]) add(115, 0, []) end end add(0, 0, []) end def set_text(text) msg_tag, script_tag, choice_tag, table_tag = *[false] * 4 choices = [] choice_ignore = [] table_rows = [] text.split("\n").each do |line| # ------------------------------------------------------------ # 优先判定是否为脚本 # ------------------------------------------------------------ if script_tag if line =~ /^```/ script_tag = false else add(655, 0, [line]) msg_tag = false end next end # ------------------------------------------------------------ # 判定注释 # ------------------------------------------------------------ if line =~ /^>/ next end # ------------------------------------------------------------ # 继续剩下的判定 # ------------------------------------------------------------ case line # ------------------------------------------------------------ # ```识别为 脚本 # ------------------------------------------------------------ when /^```/ add(355, 0, '#~ event scripts') script_tag = true # ------------------------------------------------------------ # 空行 # ------------------------------------------------------------ when /^\s*$/ if msg_tag msg_tag = false end if choice_tag choice_tag = false add_choices(choices, choice_ignore) end if table_tag table_tag = false add_table(table_rows) table_rows.clear end # ------------------------------------------------------------ # 数字列表:选择项 # ------------------------------------------------------------ when /^\s*\d+\.\s*(.+)/ text = $1 if choice_tag choices << text else choice_tag = true choice_ignore = [] choices = [text] end when /^-\s+`(.+)`/ if choice_tag if !eval($1) choice_ignore << choices.size - 1 end end # ------------------------------------------------------------ # 图片:对话开始,自动名称和头像 # ------------------------------------------------------------ when /^!\[(.+)\]\((.+)\)/ name, fig = $1, $2 if fig =~ /"(\d+)"/ num = $1.to_i fig = fig.sub(/"\d+"/, '').strip else num = 0 end msg_tag = true add(101, 0, [fig, num, 0, 2]) add(401, 0, [name]) # ------------------------------------------------------------ # 邮件<>:可见指令 # ------------------------------------------------------------ when /^<(.+)@(.+)>/ # cmd, para = $1, $2 case cmd when 'CommonEvent' if para =~ /\d+/ add(117, 0, [para.to_i]) else id = $data_common_events.find{|e| e && e.name == para}.id add(117, 0, [id]) end when 'GetItem' add(104, 0, [para.to_i]) when 'JumpTo' add(119, 0, [para]) @keys << para when 'AutoEvent' if para == 'reload' add(355, 0, ['$game_map.events[@event_id].auto_reload']) end end # ------------------------------------------------------------ # [sth]: site "description" 不可见指令 # ------------------------------------------------------------ when /^\[(.+)\]\s*:\s*(.+)\s+"(.+)"/ cmd, para1, para2 = $1, $2, $3 case cmd when 'SelfSwitch' # 独立开关 _v = (para2 == 'ON')? 0 : 1 add(123, 0, [para1, _v]) when 'Move', 'Moves' # 移动事件,等待移动结束 if para1 =~ /\-*\d+/ id = para1.to_i else add(355, 0, ["s = $game_map.events.select{|id, e| e.name == '#{para1}'}"] ) add(655, 0, ["@list[@index + 1].parameters[0] = s.keys[0]"]) end route = add_moveroute(para2, cmd == 'Move') add(205, 0, [id, route]) when 'PlaySE' add(250, 0, [RPG::SE.new(para1, para2.to_i, 100)]) when 'Dialog' case para1 when 'change-skin' id = Window_Message::Skin[:var] add(122, 0, [id, id, 0, 4, para2]) end end # ------------------------------------------------------------ # [label](script) 条件跳转 # ------------------------------------------------------------ when /^\[(.+)\]\((.+)\)/ label, script = $1, $2 add(111, 0, [12, script]) add(119, 1, [label]) add(0, 1, []) add(412, 0, []) add(118, 0, ['#' + label]) @keys << label # ------------------------------------------------------------ # [code][paras] 直接执行命令: # [201] [0, map, x, y, 0, 0] 切换地图 # [214] [] 暂时消除事件 # [213] [-1, 1, true] Balloon # ... # ------------------------------------------------------------ when /^\[(\d+)\] (\[.+\])/ add($1.to_i, 0, eval($2)) # ------------------------------------------------------------ # 表格的情况 # ------------------------------------------------------------ when /^.+\|/ ary = line.split('|') if table_tag table_rows << ary else if table_rows.empty? table_rows << ary elsif ['-'] * table_rows[0].size == ary table_tag = true else table_rows.clear end end # ------------------------------------------------------------ # 其他的情况 # ------------------------------------------------------------ else # 移除行尾的 <br/> 或者 <br> line.sub!(/<br\/*>$/, '') # 转义 `` 中的内容 while line =~ /`(.*?)`/ line.sub! /`.*?`/, eval($1).to_s end # 视为普通的对话 if msg_tag add(401, 0, [line]) else msg_tag = true add(101, 0, ['', 0, 1, 2]) add(401, 0, [line]) end end end # 如果以选择项结尾 add_choices(choices, choice_ignore) if choice_tag add_table(table_rows) if table_tag end def add(*parameters) @list << RPG::EventCommand.new(*parameters) end def add_choices(choices, choice_ignore) @keys.concat(choices) choice_ignore.each do |i| choices[i] = nil end choices.compact! add(102, 0, [choices, 5]) choices.each_index do |i| add(402, 0, [i, choices[i]]) add(119, 1, [choices[i]]) add(0, 1, []) end add(403, 0, []) add(0, 1, []) add(404, 0, []) end def add_table(rows) case rows[0][0] when 'Shop' add(302, 0, [-1, 0, 0, 0, rows[0][1] =~ /^P/]) rows.each do |paras| paras[2] = (paras[3] == '-') ? 0 : 1 add(605, 0, paras.map{|x| x.to_i}) end end end def add_moveroute(string, wait = false) route = RPG::MoveRoute.new route.repeat = false route.skippable = true route.wait = wait route.list.clear list = [] string.split(' ').each do |c| case c # move when /w(\d+)/ then list.concat([4, []] * $1.to_i) # move up when /a(\d+)/ then list.concat([2, []] * $1.to_i) # move left when /s(\d+)/ then list.concat([1, []] * $1.to_i) # move down when /d(\d+)/ then list.concat([3, []] * $1.to_i) # move right when /f(\d+)/ then list.concat([12, []] * $1.to_i) # move forward when /b(\d+)/ then list.concat([13, []] * $1.to_i) # move backward when /j(.+)/ then list << [14, $1.split('.').map{|i| i.to_i}] # jump when /J(.+)/ then list << [45, ["moveto #{$1.sub('.', ',')}"]] # move to # turn when 'W' then list << [19, []] # turn up when 'A' then list << [17, []] # turn left when 'S' then list << [16, []] # turn down when 'D' then list << [18, []] # turn right when 'L' then list << [21, []] # turn left 90 when 'R' then list << [20, []] # turn right 90 when 'B' then list << [22, []] # turn 180 when 'FIX' then list << [35, []] # fix direction on when 'fix' then list << [36, []] # fix direction off # move setting when 'PASS' then list << [37, []] # pass through on when 'pass' then list << [38, []] # pass through off when /v(\d+)/ then list << [29, [$1.to_i]] # change speed when /V(\d+)/ then list << [30, [$1.to_i]] # change frequency when 'STEP' then list << [33, []] # step animation on when 'step' then list << [33, []] # step animation off # opacity when 'T' then list << [39, []] # transparent on when 't' then list << [40, []] # transparent off when /o(\d+)/ then list << [42, [$1.to_i]] # opacity # other when /W(\d+)/ then list << [15, [$1.to_i]] # wait when /X(\d+)/ then list << [27, [$1.to_i]] # switch on when /x(\d+)/ then list << [28, [$1.to_i]] # switch off end end list << [0, []] list.each do |code, paras| route.list << RPG::MoveCommand.new(code, paras) end route end end
# functions:
# auto load event list from the txt file with same name.
class Game_Event < Game_Character
alias _auto_load_setup_page_settings setup_page_settings
def setup_page_settings
_auto_load_setup_page_settings
if @list[0].code == 355 && @list[0].parameters[0] =~ /AUTO:(.+)/
auto_load(@event.name, $1.strip)
elsif @auto_load_keys
auto_load *@auto_load_keys
end
end
def auto_load(name, key)
@auto_load_keys = [name, key]
@list.replace Auto_Event.load(name).list(key)
end
def name
@event.name
end
def auto_reload
@page = nil
refresh
end
end
module Auto_Event
module_function
Event_List = {}
def reload # only after .md file changes
Event_List.keys.each do |name|
Event_List[name] = load_list_txt(name)
end
end
def load(name)
Event_List[name] ||= load_list_txt(name)
@data = Event_List[name]
@list = []
@keys = []
self
end
def load_list_txt(name)
data, key, value = {}, '', []
File.open('Events/' + name + '.md', 'r') do |f|
f.readlines.each do |line|
# start with # key
if line =~ /^#+\s*(.+)/
data[key] = value.join("\n") if key != ''
key, value = $1, []
next
else
value << line.strip
next
end
end
# last key
data[key] = value.join("\n") if key != ''
end
data
end
def list(key)
@keys << key
event_keys = []
while @keys.size != event_keys.size
@keys.uniq!
@keys.each do |key|
next if event_keys.include?(key)
event_keys << key
next if !@data[key]
add(118, 0, [key])
set_text(@data[key])
add(115, 0, [])
end
end
add(0, 0, [])
end
def set_text(text)
msg_tag, script_tag, choice_tag, table_tag = *[false] * 4
choices = []
choice_ignore = []
table_rows = []
text.split("\n").each do |line|
# ------------------------------------------------------------
# 优先判定是否为脚本
# ------------------------------------------------------------
if script_tag
if line =~ /^```/
script_tag = false
else
add(655, 0, [line])
msg_tag = false
end
next
end
# ------------------------------------------------------------
# 判定注释
# ------------------------------------------------------------
if line =~ /^>/
next
end
# ------------------------------------------------------------
# 继续剩下的判定
# ------------------------------------------------------------
case line
# ------------------------------------------------------------
# ```识别为 脚本
# ------------------------------------------------------------
when /^```/
add(355, 0, '#~ event scripts')
script_tag = true
# ------------------------------------------------------------
# 空行
# ------------------------------------------------------------
when /^\s*$/
if msg_tag
msg_tag = false
end
if choice_tag
choice_tag = false
add_choices(choices, choice_ignore)
end
if table_tag
table_tag = false
add_table(table_rows)
table_rows.clear
end
# ------------------------------------------------------------
# 数字列表:选择项
# ------------------------------------------------------------
when /^\s*\d+\.\s*(.+)/
text = $1
if choice_tag
choices << text
else
choice_tag = true
choice_ignore = []
choices = [text]
end
when /^-\s+`(.+)`/
if choice_tag
if !eval($1)
choice_ignore << choices.size - 1
end
end
# ------------------------------------------------------------
# 图片:对话开始,自动名称和头像
# ------------------------------------------------------------
when /^!\[(.+)\]\((.+)\)/
name, fig = $1, $2
if fig =~ /"(\d+)"/
num = $1.to_i
fig = fig.sub(/"\d+"/, '').strip
else
num = 0
end
msg_tag = true
add(101, 0, [fig, num, 0, 2])
add(401, 0, [name])
# ------------------------------------------------------------
# 邮件<>:可见指令
# ------------------------------------------------------------
when /^<(.+)@(.+)>/ #
cmd, para = $1, $2
case cmd
when 'CommonEvent'
if para =~ /\d+/
add(117, 0, [para.to_i])
else
id = $data_common_events.find{|e| e && e.name == para}.id
add(117, 0, [id])
end
when 'GetItem'
add(104, 0, [para.to_i])
when 'JumpTo'
add(119, 0, [para])
@keys << para
when 'AutoEvent'
if para == 'reload'
add(355, 0, ['$game_map.events[@event_id].auto_reload'])
end
end
# ------------------------------------------------------------
# [sth]: site "description" 不可见指令
# ------------------------------------------------------------
when /^\[(.+)\]\s*:\s*(.+)\s+"(.+)"/
cmd, para1, para2 = $1, $2, $3
case cmd
when 'SelfSwitch' # 独立开关
_v = (para2 == 'ON')? 0 : 1
add(123, 0, [para1, _v])
when 'Move', 'Moves' # 移动事件,等待移动结束
if para1 =~ /\-*\d+/
id = para1.to_i
else
add(355, 0,
["s = $game_map.events.select{|id, e| e.name == '#{para1}'}"]
)
add(655, 0, ["@list[@index + 1].parameters[0] = s.keys[0]"])
end
route = add_moveroute(para2, cmd == 'Move')
add(205, 0, [id, route])
when 'PlaySE'
add(250, 0, [RPG::SE.new(para1, para2.to_i, 100)])
when 'Dialog'
case para1
when 'change-skin'
id = Window_Message::Skin[:var]
add(122, 0, [id, id, 0, 4, para2])
end
end
# ------------------------------------------------------------
# [label](script) 条件跳转
# ------------------------------------------------------------
when /^\[(.+)\]\((.+)\)/
label, script = $1, $2
add(111, 0, [12, script])
add(119, 1, [label])
add(0, 1, [])
add(412, 0, [])
add(118, 0, ['#' + label])
@keys << label
# ------------------------------------------------------------
# [code][paras] 直接执行命令:
# [201] [0, map, x, y, 0, 0] 切换地图
# [214] [] 暂时消除事件
# [213] [-1, 1, true] Balloon
# ...
# ------------------------------------------------------------
when /^\[(\d+)\] (\[.+\])/
add($1.to_i, 0, eval($2))
# ------------------------------------------------------------
# 表格的情况
# ------------------------------------------------------------
when /^.+\|/
ary = line.split('|')
if table_tag
table_rows << ary
else
if table_rows.empty?
table_rows << ary
elsif ['-'] * table_rows[0].size == ary
table_tag = true
else
table_rows.clear
end
end
# ------------------------------------------------------------
# 其他的情况
# ------------------------------------------------------------
else
# 移除行尾的 <br/> 或者 <br>
line.sub!(/<br\/*>$/, '')
# 转义 `` 中的内容
while line =~ /`(.*?)`/
line.sub! /`.*?`/, eval($1).to_s
end
# 视为普通的对话
if msg_tag
add(401, 0, [line])
else
msg_tag = true
add(101, 0, ['', 0, 1, 2])
add(401, 0, [line])
end
end
end
# 如果以选择项结尾
add_choices(choices, choice_ignore) if choice_tag
add_table(table_rows) if table_tag
end
def add(*parameters)
@list << RPG::EventCommand.new(*parameters)
end
def add_choices(choices, choice_ignore)
@keys.concat(choices)
choice_ignore.each do |i|
choices[i] = nil
end
choices.compact!
add(102, 0, [choices, 5])
choices.each_index do |i|
add(402, 0, [i, choices[i]])
add(119, 1, [choices[i]])
add(0, 1, [])
end
add(403, 0, [])
add(0, 1, [])
add(404, 0, [])
end
def add_table(rows)
case rows[0][0]
when 'Shop'
add(302, 0, [-1, 0, 0, 0, rows[0][1] =~ /^P/])
rows.each do |paras|
paras[2] = (paras[3] == '-') ? 0 : 1
add(605, 0, paras.map{|x| x.to_i})
end
end
end
def add_moveroute(string, wait = false)
route = RPG::MoveRoute.new
route.repeat = false
route.skippable = true
route.wait = wait
route.list.clear
list = []
string.split(' ').each do |c|
case c
# move
when /w(\d+)/ then list.concat([4, []] * $1.to_i) # move up
when /a(\d+)/ then list.concat([2, []] * $1.to_i) # move left
when /s(\d+)/ then list.concat([1, []] * $1.to_i) # move down
when /d(\d+)/ then list.concat([3, []] * $1.to_i) # move right
when /f(\d+)/ then list.concat([12, []] * $1.to_i) # move forward
when /b(\d+)/ then list.concat([13, []] * $1.to_i) # move backward
when /j(.+)/ then list << [14, $1.split('.').map{|i| i.to_i}] # jump
when /J(.+)/ then list << [45, ["moveto #{$1.sub('.', ',')}"]] # move to
# turn
when 'W' then list << [19, []] # turn up
when 'A' then list << [17, []] # turn left
when 'S' then list << [16, []] # turn down
when 'D' then list << [18, []] # turn right
when 'L' then list << [21, []] # turn left 90
when 'R' then list << [20, []] # turn right 90
when 'B' then list << [22, []] # turn 180
when 'FIX' then list << [35, []] # fix direction on
when 'fix' then list << [36, []] # fix direction off
# move setting
when 'PASS' then list << [37, []] # pass through on
when 'pass' then list << [38, []] # pass through off
when /v(\d+)/ then list << [29, [$1.to_i]] # change speed
when /V(\d+)/ then list << [30, [$1.to_i]] # change frequency
when 'STEP' then list << [33, []] # step animation on
when 'step' then list << [33, []] # step animation off
# opacity
when 'T' then list << [39, []] # transparent on
when 't' then list << [40, []] # transparent off
when /o(\d+)/ then list << [42, [$1.to_i]] # opacity
# other
when /W(\d+)/ then list << [15, [$1.to_i]] # wait
when /X(\d+)/ then list << [27, [$1.to_i]] # switch on
when /x(\d+)/ then list << [28, [$1.to_i]] # switch off
end
end
list << [0, []]
list.each do |code, paras|
route.list << RPG::MoveCommand.new(code, paras)
end
route
end
end
|
评分
-
查看全部评分
|