bcdice 3.8.0 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,7 +25,7 @@ module BCDice
25
25
  例2)KC6 → 目標値6の継続判定
26
26
  ・罠動作チェック+獲物表(P163): CTR
27
27
  罠ごとに1D12を振り、12が出た場合には生き物が罠を動作させ、その影響を受けている。
28
- ・各種表
28
+ ・各種表(基本ルールブック)
29
29
  ・大失敗表(P120): FT
30
30
  ・能力値ランダム決定表(P121): RST
31
31
  ・ランダム所要時間表(P122): RTT
@@ -38,6 +38,16 @@ module BCDice
38
38
  ・食材採集表(P157): GFT
39
39
  ・水採集表(P157): GWT
40
40
  ・白の魔石効果表(P186): WST
41
+ ・部位ダメージ関連の表(参照先ページはリプレイ&データブック「嚙神ノ宴」のもの)
42
+ ・人間部位表(P216): HPT
43
+ ・部位ダメージ段階表(P217): PDT
44
+ ・四足動物部位表(P225): QPT
45
+ ・無足動物部位表(P225): APT
46
+ ・二足動物部位表(P226): TPT
47
+ ・鳥部位表(P226): BPT
48
+ ・頭足動物部位表(P227): CPT
49
+ ・昆虫部位表(P227): IPT
50
+ ・蜘蛛部位表(P228): SPT
41
51
  MESSAGETEXT
42
52
 
43
53
  def eval_game_system_specific_command(command)
@@ -237,6 +247,128 @@ module BCDice
237
247
  '大型動物を召喚する',
238
248
  ]
239
249
  ),
250
+ 'HPT' => DiceTable::RangeTable.new(
251
+ '人間部位表',
252
+ '1D12',
253
+ [
254
+ [1..2, '右腕部'],
255
+ [3..4, '左腕部'],
256
+ [5..6, '右脚部'],
257
+ [7..8, '左脚部'],
258
+ [9..11, '胴部'],
259
+ [12, '頭部'],
260
+ ]
261
+ ),
262
+ 'PDT' => DiceTable::RangeTable.new(
263
+ '部位ダメージ段階表',
264
+ '1D12',
265
+ [
266
+ [1..6, '軽傷'],
267
+ [7..10, '重傷'],
268
+ [11, '破壊'],
269
+ [12, '喪失'],
270
+ ]
271
+ ),
272
+ 'QPT' => DiceTable::RangeTable.new(
273
+ '四足動物部位表',
274
+ '1D12',
275
+ [
276
+ [1..2, '異形'],
277
+ [3, '武器'],
278
+ [4, '右前脚部'],
279
+ [5, '左前脚部'],
280
+ [6, '右後脚部'],
281
+ [7, '左後脚部'],
282
+ [8..10, '胴部'],
283
+ [11..12, '頭部'],
284
+ ]
285
+ ),
286
+ 'APT' => DiceTable::RangeTable.new(
287
+ '無足動物部位表',
288
+ '1D12',
289
+ [
290
+ [1..3, '異形'],
291
+ [4..6, '武器'],
292
+ [7..10, '胴部'],
293
+ [11..12, '頭部'],
294
+ ]
295
+ ),
296
+ 'TPT' => DiceTable::RangeTable.new(
297
+ '二足動物部位表',
298
+ '1D12',
299
+ [
300
+ [1, '異形'],
301
+ [2, '武器'],
302
+ [3, '右腕部'],
303
+ [4, '左腕部'],
304
+ [5..6, '右脚部'],
305
+ [7..8, '左脚部'],
306
+ [9..11, '胴部'],
307
+ [12, '頭部'],
308
+ ]
309
+ ),
310
+ 'BPT' => DiceTable::RangeTable.new(
311
+ '鳥部位表',
312
+ '1D12',
313
+ [
314
+ [1, '異形'],
315
+ [2, '武器'],
316
+ [3..4, '右翼(右腕部)'],
317
+ [5..6, '左翼(左腕部)'],
318
+ [7, '右脚部'],
319
+ [8, '左脚部'],
320
+ [9..11, '胴部'],
321
+ [12, '頭部'],
322
+ ]
323
+ ),
324
+ 'CPT' => DiceTable::RangeTable.new(
325
+ '頭足動物部位表',
326
+ '1D12',
327
+ [
328
+ [1, '異形'],
329
+ [2, '武器'],
330
+ [3, '右腕部'],
331
+ [4, '左腕部'],
332
+ [5..7, '右脚部'],
333
+ [8..10, '左脚部'],
334
+ [11, '胴部'],
335
+ [12, '頭部'],
336
+ ]
337
+ ),
338
+ 'IPT' => DiceTable::RangeTable.new(
339
+ '昆虫部位表',
340
+ '1D12',
341
+ [
342
+ [1..2, '異形'],
343
+ [3, '武器'],
344
+ [4, '右前脚部'],
345
+ [5, '左前脚部'],
346
+ [6, '右中脚部'],
347
+ [7, '左中脚部'],
348
+ [8, '右後脚部'],
349
+ [9, '左後脚部'],
350
+ [10..11, '胴部'],
351
+ [12, '頭部'],
352
+ ]
353
+ ),
354
+ 'SPT' => DiceTable::RangeTable.new(
355
+ '蜘蛛部位表',
356
+ '1D12',
357
+ [
358
+ [1, '異形'],
359
+ [2, '武器'],
360
+ [3, '右第一脚部'],
361
+ [4, '左第一脚部'],
362
+ [5, '右第二脚部'],
363
+ [6, '左第二脚部'],
364
+ [7, '右第三脚部'],
365
+ [8, '左第三脚部'],
366
+ [9, '右第四脚部'],
367
+ [10, '左第四脚部'],
368
+ [11, '胴部'],
369
+ [12, '頭部'],
370
+ ]
371
+ ),
240
372
  }.freeze
241
373
 
242
374
  register_prefix('K[AC]', 'CTR', TABLES.keys)
@@ -59,7 +59,7 @@ module BCDice
59
59
  private
60
60
 
61
61
  def check_roll(command)
62
- m = /^(\d+)D6([+\-\d]*)>=(\d+)(\[(\d+)?(,(\d+))?\])?$/i.match(command)
62
+ m = /^(\d+)D6([+\-\d]*)>=(\?|\d+)(\[(\d+)?(,(\d+))?\])?$/i.match(command)
63
63
  unless m
64
64
  return nil
65
65
  end
@@ -80,6 +80,8 @@ module BCDice
80
80
  Result.fumble(translate("MonotoneMuseum.automatic_failure"))
81
81
  elsif dice_value >= critical
82
82
  Result.critical(translate("MonotoneMuseum.automatic_success"))
83
+ elsif target == 0
84
+ Result.success(nil)
83
85
  elsif total >= target
84
86
  Result.success(translate("success"))
85
87
  else
@@ -91,7 +93,7 @@ module BCDice
91
93
  "#{dice_value}[#{dice_str}]#{Format.modifier(modify_number)}",
92
94
  total.to_s,
93
95
  result.text,
94
- ]
96
+ ].compact
95
97
 
96
98
  result.text = sequence.join(" > ")
97
99
 
@@ -8,24 +8,26 @@ module BCDice
8
8
  SORT_KEY = "えすああるえすしやないせかいしゆのめいきゆうTRPG"
9
9
 
10
10
  HELP_MESSAGE = <<~MESSAGETEXT
11
- ■ 判定 (xSQ±y)
12
- xD6の判定。3つ以上振ったとき、出目の高い2つを表示します。クリティカル、ファンブルも計算します。
11
+ ■ 判定 (xSQ±y>=z)
12
+ xD6の判定。3つ以上振ったとき、出目の高い2つを表示します。絶対成功、絶対失敗も計算します。
13
+ 2つのサイコロを使用して出目に1があった場合は、FPの獲得も表示します。3つ以上使用した場合は表示しません。
13
14
  ±y: yに修正値を入力。±の計算に対応。省略可能。
15
+ z: 目標値。省略可能。
14
16
 
15
17
  ■ ダメージロール (xDR(C)(+)y)
16
- xD6のダメージロール。クリティカルの自動判定を行います。Cを付けるとクリティカルアップ状態で計算できます。+を付けるとクリティカル時のダイスが8個になります。
18
+ xD6のダメージロール。クリティカルヒットの自動判定を行います。Cを付けるとクリティカルアップ状態で計算できます。+を付けるとクリティカルヒット時のダイスが8個になります。
17
19
  x: xに振るダイス数を入力。
18
20
  y: yに耐性を入力。
19
21
  例) 5DR3 5DRC4 5DRC+4
20
22
 
21
23
  ■ 回復ロール (xHRy)
22
- xD6の回復ロール。クリティカルが発生しません。
24
+ xD6の回復ロール。クリティカルヒットが発生しません。
23
25
  x: xに振るダイス数を入力。
24
26
  y: yに耐性を入力。省略した場合3。
25
27
  例) 2HR 10HR2
26
28
 
27
- 採取ロール (TC±z,SC±z,GC±z)
28
- ちょっと(T)、そこそこ(S)、がっつり(G)採取採掘伐採を行う。
29
+ 採集ロール (TC±z,SC±z,GC±z)
30
+ 少しだけ(T)、そこそこ(S)、ガッツリ(G)採取採掘伐採を行います。
29
31
  z: zに追加でロールする回数を入力。省略可能。
30
32
  例) TC SC+1 GC-1
31
33
  MESSAGETEXT
@@ -40,27 +42,38 @@ module BCDice
40
42
 
41
43
  # 判定
42
44
  def roll_sq(command)
43
- m = /(\d+)SQ([+\-\d]+)?/i.match(command)
45
+ m = /(\d+)SQ([+\-\d]+)?(([>=]+)(\d+))?/i.match(command)
44
46
  return nil unless m
45
47
 
46
48
  dice_count = m[1].to_i
47
49
  modifier = ArithmeticEvaluator.eval(m[2])
50
+ target = m[5].nil? ? nil : m[5].to_i
48
51
 
49
52
  dice_list = @randomizer.roll_barabara(dice_count, 6)
50
53
  largest_two = dice_list.sort.reverse.take(2)
51
54
  total = largest_two.sum + modifier
55
+ num_1 = dice_list.count(1)
52
56
 
53
57
  additional_result =
54
58
  if largest_two == [6, 6]
55
- " クリティカル!"
59
+ Result.critical(" > 絶対成功!")
56
60
  elsif largest_two == [1, 1]
57
- " ファンブル!"
61
+ Result.fumble(" > 絶対失敗!")
62
+ elsif target && total >= target
63
+ Result.success(" > 成功")
64
+ elsif target && total < target
65
+ Result.failure(" > 失敗")
66
+ else
67
+ Result.new
58
68
  end
59
69
 
70
+ # ダイス数が2個の場合は1の出目の数だけ【FP】を獲得できる
71
+ fp_result = dice_count == 2 && num_1 >= 1 ? " (【FP】#{num_1}獲得)" : ""
72
+
60
73
  sequence = [
61
74
  "(#{command})",
62
75
  "[#{dice_list.join(',')}]#{Format.modifier(modifier)}",
63
- "#{total}[#{largest_two.join(',')}]#{additional_result}",
76
+ "#{total}[#{largest_two.join(',')}]#{additional_result.text}#{fp_result}",
64
77
  ]
65
78
 
66
79
  return sequence.join(" > ")
@@ -83,7 +96,7 @@ module BCDice
83
96
  critical_target = critical_up ? 1 : 2
84
97
 
85
98
  if dice_list.count(6) - dice_list.count(1) >= critical_target
86
- result += " クリティカル!\n"
99
+ result += " クリティカルヒット!\n"
87
100
  result += additional_damage_roll(increase_critical_dice, resist)
88
101
  end
89
102
 
@@ -16,27 +16,33 @@ module BCDice
16
16
 
17
17
  HELP_MESSAGE_1 = <<~HELP_MESSAGE
18
18
  ・判定
19
-  ・通常判定:2D6+m>=t[c,f]
19
+  ・通常判定:2D6+m@c#f>=t または 2D6+m>=t[c,f]
20
20
    修正値m、目標値t、クリティカル値c、ファンブル値fで判定ロールを行います。
21
-   修正値、クリティカル値、ファンブル値は省略可能です([]ごと省略可)。
21
+   修正値、クリティカル値、ファンブル値は省略可能です([]ごと省略可、@c・#fの指定は順不同)。
22
22
    クリティカル値、ファンブル値の既定値は、それぞれ12、2です。
23
23
    自動成功、自動失敗、成功、失敗を自動表示します。
24
24
 
25
25
    例) 2d6>=10     修正値0、目標値10で判定
26
26
    例) 2d6+2>=10    修正値+2、目標値10で判定
27
27
    例) 2d6+2>=10[11]  ↑をクリティカル値11で判定
28
+   例) 2d6+2@11>=10   ↑をクリティカル値11で判定
28
29
    例) 2d6+2>=10[12,4] ↑をクリティカル値12、ファンブル値4で判定
30
+   例) 2d6+2@12#4>=10  ↑をクリティカル値12、ファンブル値4で判定
31
+   例) 2d6+2>=10[,4]  ↑をクリティカル値12、ファンブル値4で判定(クリティカル値の省略)
32
+   例) 2d6+2#4>=10   ↑をクリティカル値12、ファンブル値4で判定(クリティカル値の省略)
29
33
  HELP_MESSAGE
30
34
 
31
35
  HELP_MESSAGE_2 = <<~HELP_MESSAGE
32
-  ・クリティカルおよびファンブルのみの判定:2D6+m[c,f]
36
+  ・クリティカルおよびファンブルのみの判定:2D6+m@c#f または 2D6+m[c,f]
33
37
    目標値を指定せず、修正値m、クリティカル値c、ファンブル値fで判定ロールを行います。
34
-   修正値、クリティカル値、ファンブル値は省略可能です([]は省略不可)。
38
+   修正値、クリティカル値、ファンブル値は省略可能です([]は省略不可、@c・#fの指定は順不同)。
35
39
    自動成功、自動失敗を自動表示します。
36
40
 
37
41
    例) 2d6[]    修正値0、クリティカル値12、ファンブル値2で判定
38
42
    例) 2d6+2[11]  修正値+2、クリティカル値11、ファンブル値2で判定
43
+   例) 2d6+2@11   修正値+2、クリティカル値11、ファンブル値2で判定
39
44
    例) 2d6+2[12,4] 修正値+2、クリティカル値12、ファンブル値4で判定
45
+   例) 2d6+2@12#4  修正値+2、クリティカル値12、ファンブル値4で判定
40
46
  HELP_MESSAGE
41
47
 
42
48
  HELP_MESSAGE_3 = <<~HELP_MESSAGE
@@ -50,18 +56,14 @@ module BCDice
50
56
 
51
57
  # 成功判定のエイリアスコマンド定義用のクラスメソッドを提供するモジュール
52
58
  module ClassMethods
59
+ # 成功判定のエイリアスコマンドの一覧
60
+ # @return [Array<String>]
61
+ attr_reader :aliases
62
+
53
63
  # ダイスボットの説明文を返す
54
64
  # @return [String]
55
65
  attr_reader :help_message
56
66
 
57
- # 目標値あり成功判定のエイリアスコマンドの正規表現を返す
58
- # @return [Regexp, nil]
59
- attr_reader :aliases_re_for_srs_roll_with_target_value
60
-
61
- # 目標値なし成功判定のエイリアスコマンドの正規表現を返す
62
- # @return [Regexp, nil]
63
- attr_reader :aliases_re_for_srs_roll_without_target_value
64
-
65
67
  # 成功判定のエイリアスコマンドを設定する
66
68
  # @param [String] aliases エイリアスコマンド(可変長引数)
67
69
  # @return [self]
@@ -70,71 +72,48 @@ module BCDice
70
72
  # 実行時にそれが2D6に置換されるようになる。
71
73
  def set_aliases_for_srs_roll(*aliases)
72
74
  aliases_upcase = aliases.map(&:upcase)
73
- aliases_part = aliases_upcase
74
- .map { |a| Regexp.escape(a) }
75
- .join('|')
76
-
77
- @aliases_re_for_srs_roll_with_target_value = Regexp.new(
78
- '\A' \
79
- "(?:#{aliases_part})" \
80
- '((?:[-+][-+\d]+)?>=\d+(?:\[\d*(?:,\d+)?\])?)\z'
81
- )
82
-
83
- @aliases_re_for_srs_roll_without_target_value = Regexp.new(
84
- '\A' \
85
- "(?:#{aliases_part})" \
86
- '([-+][-+\d]+)?(\[\d*(?:,\d+)?\])?\z'
87
- )
88
-
89
- prepare_help_msg_for_aliases_for_srs_roll(aliases_upcase)
90
- @help_message = concatenate_help_messages
91
75
 
76
+ @aliases = aliases_upcase.map { |a| Regexp.escape(a) }
77
+ @help_message = concatenate_help_messages(aliases_upcase)
92
78
  return self
93
79
  end
94
80
 
95
81
  # 成功判定のエイリアスコマンドを未設定にする
96
82
  # @return [self]
97
83
  def clear_aliases_for_srs_roll
98
- @aliases_re_for_srs_roll_with_target_value = nil
99
- @aliases_re_for_srs_roll_without_target_value = nil
84
+ @aliases = []
100
85
  @help_message = SRS::DEFAULT_HELP_MESSAGE
101
-
102
86
  return self
103
87
  end
104
88
 
105
89
  private
106
90
 
107
- # 成功判定のエイリアスコマンドの説明文を用意する
91
+ # ダイスボットの説明文を結合する
108
92
  # @param [Array<String>] aliases エイリアスコマンドの配列
109
- # @return [self]
93
+ # @return [String] 結合された説明文
110
94
  # @todo 現在は2文字のエイリアスコマンドに幅を合わせてある。
111
95
  # エイリアスコマンドの文字数が変わる場合があれば、位置を調整するコードが
112
96
  # 必要。
113
- def prepare_help_msg_for_aliases_for_srs_roll(aliases)
114
- @help_msg_for_aliases_for_srs_roll_with_target_value =
97
+ def concatenate_help_messages(aliases)
98
+ help_msg_for_aliases_for_target_value =
115
99
  aliases
116
- .map { |a| "  例) #{a}+2>=10     2d6+2>=10と同じ(#{a}が2D6のショートカットコマンド)\n" }
100
+ .map do |a|
101
+ "  例) #{a}+2>=10     2d6+2>=10と同じ(#{a}が2D6のショートカットコマンド)\n"
102
+ end
117
103
  .join()
118
-
119
- @help_msg_for_aliases_for_srs_roll_without_target_value =
104
+ help_msg_for_aliases_for_without_target_value =
120
105
  aliases
121
106
  .map do |a|
122
107
  "  例) #{a}      2d6[]と同じ(#{a}が2D6のショートカットコマンド)\n" \
123
- "  例) #{a}+2[12,4]  2d6+2[12,4]と同じ(#{a}が2D6のショートカットコマンド)\n"
108
+ "  例) #{a}+2@12#4  2d6+2@12#4と同じ(#{a}が2D6のショートカットコマンド)\n"
124
109
  end
125
110
  .join()
126
111
 
127
- return self
128
- end
129
-
130
- # ダイスボットの説明文を結合する
131
- # @return [String] 結合された説明文
132
- def concatenate_help_messages
133
- "#{SRS::HELP_MESSAGE_1}" \
134
- "#{@help_msg_for_aliases_for_srs_roll_with_target_value}\n" \
135
- "#{SRS::HELP_MESSAGE_2}" \
136
- "#{@help_msg_for_aliases_for_srs_roll_without_target_value}\n" \
137
- "#{SRS::HELP_MESSAGE_3}"
112
+ return "#{SRS::HELP_MESSAGE_1}" \
113
+ "#{help_msg_for_aliases_for_target_value}\n" \
114
+ "#{SRS::HELP_MESSAGE_2}" \
115
+ "#{help_msg_for_aliases_for_without_target_value}\n" \
116
+ "#{SRS::HELP_MESSAGE_3}"
138
117
  end
139
118
  end
140
119
 
@@ -153,16 +132,10 @@ module BCDice
153
132
  DEFAULT_HELP_MESSAGE
154
133
  end
155
134
 
156
- # 目標値あり成功判定のエイリアスコマンドの正規表現を返す
157
- # @return [nil]
158
- def aliases_re_for_srs_roll_with_target_value
159
- nil
160
- end
161
-
162
- # 目標値なし成功判定のエイリアスコマンドの正規表現を返す
163
- # @return [nil]
164
- def aliases_re_for_srs_roll_without_target_value
165
- nil
135
+ # 成功判定のエイリアスコマンドの一覧
136
+ # @return [Array<String>]
137
+ def aliases
138
+ []
166
139
  end
167
140
  end
168
141
 
@@ -187,22 +160,17 @@ module BCDice
187
160
  self.class.help_message
188
161
  end
189
162
 
163
+ # 成功判定のエイリアスコマンドの一覧
164
+ # @return [Array<String>]
165
+ def aliases
166
+ self.class.aliases
167
+ end
168
+
190
169
  # 既定のクリティカル値
191
170
  DEFAULT_CRITICAL_VALUE = 12
192
171
  # 既定のファンブル値
193
172
  DEFAULT_FUMBLE_VALUE = 2
194
173
 
195
- # 目標値あり成功判定の正規表現
196
- SRS_ROLL_WITH_TARGET_VALUE_RE =
197
- /\A2D6([-+][-+\d]+)?>=(\d+)(?:\[(\d+)?(?:,(\d+))?\])?\z/.freeze
198
- # 目標値なし成功判定の正規表現
199
- SRS_ROLL_WITHOUT_TARGET_VALUE_RE =
200
- /\A2D6([-+][-+\d]+)?\[(\d+)?(?:,(\d+))?\]\z/.freeze
201
-
202
- # 既定の閾値指定表記
203
- SRS_ROLL_DEFAULT_THRESHOLDS =
204
- "[#{DEFAULT_CRITICAL_VALUE},#{DEFAULT_FUMBLE_VALUE}]"
205
-
206
174
  # 成功判定コマンドのノード
207
175
  SRSRollNode = Struct.new(
208
176
  :modifier, :critical_value, :fumble_value, :target_value
@@ -221,9 +189,15 @@ module BCDice
221
189
  # @param [String] command 入力されたコマンド
222
190
  # @return [Result, nil] ダイスロールコマンドの実行結果
223
191
  def eval_game_system_specific_command(command)
224
- alias_replaced_with_2d6 = replace_alias_for_srs_roll_with_2d6(command)
225
-
226
- if (node = parse(alias_replaced_with_2d6))
192
+ legacy_c_f_match = /(.+)\[(.*)\]\z/.match(command)
193
+ node =
194
+ if legacy_c_f_match
195
+ parse_legacy(legacy_c_f_match[1], legacy_c_f_match[2])
196
+ else
197
+ parse(command)
198
+ end
199
+
200
+ if node
227
201
  return execute_srs_roll(node)
228
202
  end
229
203
 
@@ -232,67 +206,46 @@ module BCDice
232
206
 
233
207
  private
234
208
 
235
- # 成功判定のエイリアスコマンドを2D6に置換する
236
- # @param [String] input 入力文字列
237
- # @return [String]
238
- def replace_alias_for_srs_roll_with_2d6(input)
239
- case input
240
- when self.class.aliases_re_for_srs_roll_with_target_value
241
- return "2D6#{Regexp.last_match(1)}"
242
- when self.class.aliases_re_for_srs_roll_without_target_value
243
- modifier = Regexp.last_match(1)
244
- thresholds = Regexp.last_match(2) || SRS_ROLL_DEFAULT_THRESHOLDS
245
-
246
- return "2D6#{modifier}#{thresholds}"
247
- else
248
- return input
209
+ def parse(command)
210
+ prefix_re = Regexp.new(["2D6"].concat(aliases()).join('|'), Regexp::IGNORECASE)
211
+ parser = Command::Parser.new(prefix_re, round_type: @round_type)
212
+ .enable_critical
213
+ .enable_fumble
214
+ .restrict_cmp_op_to(nil, :>=)
215
+ cmd = parser.parse(command)
216
+ unless cmd
217
+ return nil
249
218
  end
250
- end
251
219
 
252
- # 構文解析する
253
- # @param [String] command コマンド文字列
254
- # @return [SRSRollNode, nil]
255
- def parse(command)
256
- case command
257
- when SRS_ROLL_WITH_TARGET_VALUE_RE
258
- return parse_srs_roll_with_target_value(Regexp.last_match)
259
- when SRS_ROLL_WITHOUT_TARGET_VALUE_RE
260
- return parse_srs_roll_without_target_value(Regexp.last_match)
261
- else
220
+ if command.start_with?(/2D6/i) && cmd.critical.nil? && cmd.fumble.nil? && cmd.target_number.nil?
221
+ # fallback to default dice
262
222
  return nil
263
223
  end
264
- end
265
224
 
266
- # 修正値を評価する
267
- # @param [String, nil] modifier_str 修正値部分の文字列
268
- # @return [Integer] 修正値
269
- def eval_modifier(modifier_str)
270
- return 0 unless modifier_str
225
+ cmd.critical ||= DEFAULT_CRITICAL_VALUE
226
+ cmd.fumble ||= DEFAULT_FUMBLE_VALUE
271
227
 
272
- return ArithmeticEvaluator.eval(modifier_str, round_type: @round_type)
228
+ return SRSRollNode.new(cmd.modify_number, cmd.critical, cmd.fumble, cmd.target_number)
273
229
  end
274
230
 
275
- # 目標値あり成功判定の正規表現マッチ情報からノードを作る
276
- # @param [MatchData] m 正規表現のマッチ情報
277
- # @return [SRSRollNode]
278
- def parse_srs_roll_with_target_value(m)
279
- modifier = eval_modifier(m[1])
280
- target_value = m[2].to_i
281
- critical_value = m[3]&.to_i || DEFAULT_CRITICAL_VALUE
282
- fumble_value = m[4]&.to_i || DEFAULT_FUMBLE_VALUE
231
+ def parse_legacy(command, c_f)
232
+ m = /^(-?\d+)?(?:,(-?\d+))?$/.match(c_f)
233
+ unless m
234
+ return nil
235
+ end
283
236
 
284
- return SRSRollNode.new(modifier, critical_value, fumble_value, target_value)
285
- end
237
+ critical = m[1]&.to_i || DEFAULT_CRITICAL_VALUE
238
+ fumble = m[2]&.to_i || DEFAULT_FUMBLE_VALUE
286
239
 
287
- # 目標値なし成功判定の正規表現マッチ情報からノードを作る
288
- # @param [MatchData] m 正規表現のマッチ情報
289
- # @return [SRSRollNode]
290
- def parse_srs_roll_without_target_value(m)
291
- modifier = eval_modifier(m[1])
292
- critical_value = m[2]&.to_i || DEFAULT_CRITICAL_VALUE
293
- fumble_value = m[3]&.to_i || DEFAULT_FUMBLE_VALUE
240
+ prefix_re = Regexp.new(["2D6"].concat(aliases()).join('|'), Regexp::IGNORECASE)
241
+ parser = Command::Parser.new(prefix_re, round_type: @round_type)
242
+ .restrict_cmp_op_to(nil, :>=)
243
+ cmd = parser.parse(command)
244
+ unless cmd
245
+ return nil
246
+ end
294
247
 
295
- return SRSRollNode.new(modifier, critical_value, fumble_value, nil)
248
+ return SRSRollNode.new(cmd.modify_number, critical, fumble, cmd.target_number)
296
249
  end
297
250
 
298
251
  # 成功判定を実行する