class Game_Interpreter
# QTE_RESERVED_KEYS: 仅扫描此列 QTE 按键
# 在 QTE 事件中,若按下不正确的按键,视为失败
QTE_RESERVED_KEYS = [
:DOWN, :LEFT, :RIGHT, :UP,
:A, :B, :C, :X, :Y, :Z, :L, :R,
:SHIFT, :CTRL, :ALT,
:F5, :F6, :F7, :F8, :F9
]
alias qte_logic_update update
def update
qte_logic_update
update_qte_logic if @qte_event
end
# qte!(60, [:C, :B], 1, 2, true) {|t, key_states| }
# duration 判定时间,一般为 60 (帧)
# keys 按键,Input.trigger? 的参数的数组,当 order=true 时需要按顺序按下
# success 判定成功时执行的公共事件 id,可以为 0, 1 ... nil
# failure 判定失败时执行的公共事件 id,可以为 0, 1 ... nil
# order 是否需要按顺序按下
# &blk 每次刷新前可以查看当前帧数和每个按键的状态 (可用于制作 UI)
def qte!(duration=60, keys=[:C], success=nil, failure=nil, order=false, &blk)
@qte_event = {
timer: 0,
duration: duration,
keys: keys,
success: success,
failure: failure,
flag: false,
order: order,
states: Hash[keys.group_by {|k| k }.map {|k, v| [k, v.size] }],
block: blk,
}
Fiber.yield while @qte_event
end
def update_qte_logic
i, s, d, b = @qte_event.values_at(:keys, :states, :duration, :block)
b.call(@qte_event[:timer], s) if b
ks = i.select {|k| s[k] > 0 }
ks = [ks.first] if @qte_event[:order]
unless s.values.all?(&:zero?) or @qte_event[:flag]
QTE_RESERVED_KEYS.each do |k|
if Input.trigger?(k)
if ks.include?(k)
i.delete_at(i.index(k))
s[k] -= 1
else
break @qte_event[:flag] = true
end
end
end
end
@qte_event[:timer] += 1
if @qte_event[:timer] >= d
t, f = @qte_event.values_at(:success, :failure)
if s.values.all?(&:zero?) and not @qte_event[:flag]
$game_temp.reserve_common_event(t) if t
else
$game_temp.reserve_common_event(f) if f
end
b.call(@qte_event[:timer], s) if b
@qte_event = nil
end
end
# 是否在 QTE 事件中
def qte?
!!@qte_event
end
attr_reader :qte_event
# 注释: QTE > 5
# 注释: QTE [5] 上 上 2 [5] 左 右 左 右 1 [5] true
QTE_ALIAS = {
/8|上|UP?/i => :UP,
/2|下|D(?:OWN)?/i => :DOWN,
/4|左|L(?:EFT)?/i => :LEFT,
/6|右|R(?:IGHT)?/i => :RIGHT,
/1|一|A?/i => :X,
}
alias qte_command_108 command_108
def command_108
qte_command_108
@comments.each do |comment|
if comment[/^QTE\s*>/]
@qte_record = comment.split('>')[1].to_i
elsif comment.start_with?("QTE")
comment.gsub!(/\[(\d+)\]/) { $game_variables[$1.to_i] }
a = comment.split
order = a[-1] == 'true'
a.pop if order
qte!(
a[1].to_i,
a[2..-3].map {|k|
r, x = QTE_ALIAS.find {|q, _| k[q] }
x = x.call(r.match(k)) if x.respond_to? :call
x
},
a[-2].to_i,
a[-1].to_i,
order
) do |t, v|
if @qte_record and v.values.all?(&:zero?)
$game_variables[@qte_record] = t
@qte_record = nil
end
end
end
end
end
end
class Scene_Map
alias qte_logic_update update
def update
# 当处于 QTE 事件时,禁止各种按键响应事件,仅处理地图解释器
if $game_map.interpreter.qte?
super
$game_map.update(true)
@spriteset.update
else
qte_logic_update
end
end
end
class Game_Interpreter
# QTE_RESERVED_KEYS: 仅扫描此列 QTE 按键
# 在 QTE 事件中,若按下不正确的按键,视为失败
QTE_RESERVED_KEYS = [
:DOWN, :LEFT, :RIGHT, :UP,
:A, :B, :C, :X, :Y, :Z, :L, :R,
:SHIFT, :CTRL, :ALT,
:F5, :F6, :F7, :F8, :F9
]
alias qte_logic_update update
def update
qte_logic_update
update_qte_logic if @qte_event
end
# qte!(60, [:C, :B], 1, 2, true) {|t, key_states| }
# duration 判定时间,一般为 60 (帧)
# keys 按键,Input.trigger? 的参数的数组,当 order=true 时需要按顺序按下
# success 判定成功时执行的公共事件 id,可以为 0, 1 ... nil
# failure 判定失败时执行的公共事件 id,可以为 0, 1 ... nil
# order 是否需要按顺序按下
# &blk 每次刷新前可以查看当前帧数和每个按键的状态 (可用于制作 UI)
def qte!(duration=60, keys=[:C], success=nil, failure=nil, order=false, &blk)
@qte_event = {
timer: 0,
duration: duration,
keys: keys,
success: success,
failure: failure,
flag: false,
order: order,
states: Hash[keys.group_by {|k| k }.map {|k, v| [k, v.size] }],
block: blk,
}
Fiber.yield while @qte_event
end
def update_qte_logic
i, s, d, b = @qte_event.values_at(:keys, :states, :duration, :block)
b.call(@qte_event[:timer], s) if b
ks = i.select {|k| s[k] > 0 }
ks = [ks.first] if @qte_event[:order]
unless s.values.all?(&:zero?) or @qte_event[:flag]
QTE_RESERVED_KEYS.each do |k|
if Input.trigger?(k)
if ks.include?(k)
i.delete_at(i.index(k))
s[k] -= 1
else
break @qte_event[:flag] = true
end
end
end
end
@qte_event[:timer] += 1
if @qte_event[:timer] >= d
t, f = @qte_event.values_at(:success, :failure)
if s.values.all?(&:zero?) and not @qte_event[:flag]
$game_temp.reserve_common_event(t) if t
else
$game_temp.reserve_common_event(f) if f
end
b.call(@qte_event[:timer], s) if b
@qte_event = nil
end
end
# 是否在 QTE 事件中
def qte?
!!@qte_event
end
attr_reader :qte_event
# 注释: QTE > 5
# 注释: QTE [5] 上 上 2 [5] 左 右 左 右 1 [5] true
QTE_ALIAS = {
/8|上|UP?/i => :UP,
/2|下|D(?:OWN)?/i => :DOWN,
/4|左|L(?:EFT)?/i => :LEFT,
/6|右|R(?:IGHT)?/i => :RIGHT,
/1|一|A?/i => :X,
}
alias qte_command_108 command_108
def command_108
qte_command_108
@comments.each do |comment|
if comment[/^QTE\s*>/]
@qte_record = comment.split('>')[1].to_i
elsif comment.start_with?("QTE")
comment.gsub!(/\[(\d+)\]/) { $game_variables[$1.to_i] }
a = comment.split
order = a[-1] == 'true'
a.pop if order
qte!(
a[1].to_i,
a[2..-3].map {|k|
r, x = QTE_ALIAS.find {|q, _| k[q] }
x = x.call(r.match(k)) if x.respond_to? :call
x
},
a[-2].to_i,
a[-1].to_i,
order
) do |t, v|
if @qte_record and v.values.all?(&:zero?)
$game_variables[@qte_record] = t
@qte_record = nil
end
end
end
end
end
end
class Scene_Map
alias qte_logic_update update
def update
# 当处于 QTE 事件时,禁止各种按键响应事件,仅处理地图解释器
if $game_map.interpreter.qte?
super
$game_map.update(true)
@spriteset.update
else
qte_logic_update
end
end
end