bcdice 3.6.0 → 3.7.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.
@@ -0,0 +1,184 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class BeginningIdol2022 < Base
6
+ # ゲームシステムの識別子
7
+ ID = 'BeginningIdol2022'
8
+
9
+ # ゲームシステム名
10
+ NAME = 'ビギニングアイドル(2022年改訂版)'
11
+
12
+ # ゲームシステム名の読みがな
13
+ SORT_KEY = 'ひきにんくあいとる2022'
14
+
15
+ # ダイスボットの使い方
16
+ HELP_MESSAGE = <<~INFO_MESSAGE_TEXT
17
+ これは、2022年に大判サイズで発売された『駆け出しアイドルRPG ビギニングアイドル 基本ルールブック』に対応したコマンドです。
18
+
19
+ ・行為判定 BIn@c#f+m>=t
20
+  nD6をダイスロールし、行為判定に成功したかを出力します。スペシャルとファンブルの判定も行います。
21
+   n: ダイス数(省略時 2)
22
+   c: スペシャル値(省略時 12)
23
+   f: ファンブル値(省略時 2)
24
+   m: 修正値(省略可)
25
+   t: 目標値
26
+
27
+ ・パフォーマンス判定 PDn+m
28
+  nD6をダイスロールし、パフォーマンス値を出力します。パーフェクトミラクルとミラクルの判定も行います。
29
+   n: ダイス数
30
+   m: 修正値(省略可)
31
+
32
+ ・シンフォニー xxxPDn+m
33
+  nD6をダイスロールし、場に残っているダイスを加味してパフォーマンス値を出力します。
34
+  パーフェクトミラクルとミラクルシンクロの判定も行います。
35
+   xxx: 場に残っているダイスの出目を列挙したもの
36
+   n: ダイス数
37
+   m: 修正値(省略可)
38
+ INFO_MESSAGE_TEXT
39
+
40
+ def initialize(command)
41
+ super(command)
42
+
43
+ @sort_add_dice = true
44
+ @d66_sort_type = D66SortType::ASC
45
+ end
46
+
47
+ register_prefix("BI", "PD", "[1-6]+PD")
48
+
49
+ def eval_game_system_specific_command(command)
50
+ roll_skill_check(command) || roll_performance_check(command) || roll_symphony_check(command)
51
+ end
52
+
53
+ private
54
+
55
+ # 行為判定
56
+ def roll_skill_check(command)
57
+ parser = Command::Parser.new("BI", round_type: @round_type)
58
+ .enable_suffix_number
59
+ .enable_critical
60
+ .enable_fumble
61
+ .restrict_cmp_op_to(:>=)
62
+ parsed = parser.parse(command)
63
+ unless parsed
64
+ return nil
65
+ end
66
+
67
+ dice_times = parsed.suffix_number || 2
68
+ critical = parsed.critical || 12
69
+ fumble = parsed.fumble || 2
70
+
71
+ dice_list = @randomizer.roll_barabara(dice_times, 6).sort()
72
+ dice_total = dice_list.sum()
73
+ is_critical = dice_total >= critical
74
+ is_fumble = !is_critical && dice_total <= fumble
75
+ total = dice_total + parsed.modify_number
76
+
77
+ result =
78
+ if is_critical
79
+ Result.critical("スペシャル(PCは【思い出】を1つ獲得する)")
80
+ elsif is_fumble
81
+ Result.fumble("ファンブル(【思い出】を1つ獲得し、ファンブル表を振る)")
82
+ elsif total >= parsed.target_number
83
+ Result.success("成功")
84
+ else
85
+ Result.failure("失敗")
86
+ end
87
+
88
+ result.text = "(#{parsed}) > #{dice_total}[#{dice_list.join(',')}]#{Format.modifier(parsed.modify_number)} > #{total} > #{result.text}"
89
+ return result
90
+ end
91
+
92
+ # パフォーマンス判定
93
+ def roll_performance_check(command)
94
+ m = /^PD(\d+)([+\-]\d+)?$/.match(command)
95
+ unless m
96
+ return nil
97
+ end
98
+
99
+ suffix_number = m[1].to_i
100
+ modifier = m[2].to_i
101
+ is_extension = suffix_number >= 7
102
+ dice_times = is_extension ? 6 : suffix_number
103
+ extension_bonus = is_extension ? suffix_number - dice_times : 0
104
+
105
+ if dice_times <= 0
106
+ return nil
107
+ end
108
+
109
+ dice_list = @randomizer.roll_barabara(dice_times, 6).sort()
110
+ uniqed = select_uniqs(dice_list).sort()
111
+
112
+ is_perfect_miracle = uniqed == [1, 2, 3, 4, 5, 6]
113
+ is_miracle = uniqed.empty?
114
+ result_label =
115
+ if is_perfect_miracle
116
+ "【パーフェクトミラクル】#{30 + extension_bonus + modifier}"
117
+ elsif is_miracle
118
+ "【ミラクル】#{10 + extension_bonus + modifier}"
119
+ else
120
+ (uniqed.sum() + extension_bonus + modifier).to_s
121
+ end
122
+ if is_extension
123
+ result_label += " (エクステンション: #{extension_bonus}個まで振りなおし可能)"
124
+ end
125
+
126
+ Result.new.tap do |result|
127
+ result.critical = is_perfect_miracle || is_miracle
128
+ result.text = [
129
+ "(#{command})",
130
+ "パフォーマンス判定",
131
+ "[#{dice_list.join(',')}]#{Format.modifier(extension_bonus)}#{Format.modifier(modifier)}",
132
+ ("[#{uniqed.join(',')}]#{Format.modifier(extension_bonus)}#{Format.modifier(modifier)}" if dice_list.size != uniqed.size),
133
+ result_label,
134
+ ].compact.join(" > ")
135
+ end
136
+ end
137
+
138
+ def select_uniqs(array)
139
+ # TODO: Ruby 2.7以降のみサポートするようになった場合に Enumerable#tally で書く
140
+ array.group_by(&:itself)
141
+ .to_a
142
+ .select { |_, arr| arr.size == 1 }
143
+ .map { |key, _| key }
144
+ end
145
+
146
+ # シンフォニー
147
+ def roll_symphony_check(command)
148
+ m = /^([1-6]+)PD([1-6])([+\-]\d+)?$/.match(command)
149
+ unless m
150
+ return nil
151
+ end
152
+
153
+ carries = m[1].chars.map(&:to_i).sort()
154
+ dice_times = m[2].to_i
155
+ modifier = m[3].to_i
156
+
157
+ dice_list = @randomizer.roll_barabara(dice_times, 6).sort()
158
+ uniqed = select_uniqs(carries + dice_list).sort()
159
+
160
+ is_perfect_miracle = uniqed == [1, 2, 3, 4, 5, 6]
161
+ is_miracle_synchro = uniqed.empty?
162
+ result_label =
163
+ if is_perfect_miracle
164
+ "【パーフェクトミラクル】#{30 + modifier}"
165
+ elsif is_miracle_synchro
166
+ "【ミラクルシンクロ】#{20 + modifier}"
167
+ else
168
+ (uniqed.sum() + modifier).to_s
169
+ end
170
+
171
+ Result.new.tap do |result|
172
+ result.critical = is_perfect_miracle || is_miracle_synchro
173
+ result.text = [
174
+ "(#{command})",
175
+ "シンフォニー",
176
+ "[#{carries.join(',')}],[#{dice_list.join(',')}]#{Format.modifier(modifier)}",
177
+ "[#{uniqed.join(',')}]#{Format.modifier(modifier)}",
178
+ result_label,
179
+ ].join(" > ")
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
@@ -18,11 +18,11 @@ module BCDice
18
18
  探偵用:【DT】…10面ダイスを2つ振って判定します。『有利』なら【3DT】、『不利』なら【1DT】を使います。
19
19
  助手用:【AS】…6面ダイスを2つ振って判定します。『有利』なら【3AS】、『不利』なら【1AS】を使います。
20
20
  ・各種表
21
- 【調査時】
21
+ 【セッション時】
22
22
  異常な癖決定表      SHRD/新・異常な癖決定表   SHND
23
23
  普通の?・異常な癖決定表 SHAD/ケイジ異常な癖決定表  SHKD
24
24
   口から出る表    SHFM/強引な捜査表      SHBT/すっとぼけ表       SHPI
25
-  事件に夢中表    SHEG/パートナーと……表    SHWP/何かしている表      SHDS
25
+  事件に夢中表    SHEG/パートナーと……表   SHWP/何かしている表      SHDS
26
26
   奇想天外表     SHFT/急なひらめき表     SHIN/喜怒哀楽表        SHEM
27
27
   人間エミュレート表 SHHE/人間エミュレート失敗表 SHHF/パートナーへのいたずら表 SHMP
28
28
   思わせぶり表    SHSB/もどかしい表      SHFR/突然どうした表      SHIS
@@ -38,15 +38,23 @@ module BCDice
38
38
   思わぬヒント EVH/実験をしてみよう EVX/ゲスト捜査 EVG
39
39
   ケイジ聞き込み捜査    EVQ/ケイジ大規模捜査      EVM/こっそり情報の受け渡し EVP
40
40
   同僚たちと一緒に捜査する EVO/頻染みの店シチュエーション EVF/ハードBデカアクション EVB
41
+ 感情表
42
+  感情表A/B   FLT66・FLT10
43
+  気に入っているところ  FLTL66 /気に入らないところ  FLTD66
44
+  ランダム感情決定表(あなた)  FLTRA
45
+  顔のパーツ     FLTF66/体のパーツ  FLTB66/生活習慣    FLTH66
46
+  ふわっとした感覚  FLTS66/他人への態度 FLTA66/ヘビーウェイト FLTW66
47
+  同僚     FLTC66/部下     FLTU66/上司     FLTO66
48
+  捜査のやり方 FLTI66
41
49
  調査の障害表 OBT  変調表 ACT  目撃者表 EWT  迷宮入り表 WMT
50
+ 思い出の品決定表 MIT  エピソード付き思い出の品表 MITE  呼び名表A・B  NCT66・NCT10
42
51
  【設定時】
43
52
  背景表
44
53
   探偵 運命の血統 BGDD/天性の才能 BGDG/マニア     BGDM
45
54
   助手 正義の人  BGAJ/情熱の人  BGAP/巻き込まれの人 BGAI
46
- 身長表 HT  たまり場表 BT  関係表 GRT  思い出の品決定表 MIT
55
+ 身長表 HT  たまり場表 BT  関係表 GRT
47
56
  職業表A・B  JBT66・JBT10  ファッション特徴表A・B    FST66・FST10
48
- 感情表A/B  FLT66・FLT10  好きなもの/嫌いなもの表A・B LDT66・LDT10
49
- 呼び名表A・B NCT66・NCT10
57
+ 好きなもの/嫌いなもの表A・B LDT66・LDT10
50
58
  MESSAGETEXT
51
59
 
52
60
  def initialize(command)
@@ -169,6 +177,9 @@ module BCDice
169
177
  "EVO" => DiceTable::Table.from_i18n("FutariSousa.table.EVO", locale),
170
178
  "EVF" => DiceTable::Table.from_i18n("FutariSousa.table.EVF", locale),
171
179
  "EVB" => DiceTable::Table.from_i18n("FutariSousa.table.EVB", locale),
180
+ "EVL" => DiceTable::Table.from_i18n("FutariSousa.table.EVL", locale),
181
+ "EVZ" => DiceTable::Table.from_i18n("FutariSousa.table.EVZ", locale),
182
+ "EVR" => DiceTable::Table.from_i18n("FutariSousa.table.EVR", locale),
172
183
  "OBT" => DiceTable::D66Table.from_i18n("FutariSousa.table.OBT", locale),
173
184
  "ACT" => DiceTable::Table.from_i18n("FutariSousa.table.ACT", locale),
174
185
  "EWT" => DiceTable::Table.from_i18n("FutariSousa.table.EWT", locale),
@@ -183,6 +194,7 @@ module BCDice
183
194
  "BT" => DiceTable::Table.from_i18n("FutariSousa.table.BT", locale),
184
195
  "GRT" => DiceTable::D66Table.from_i18n("FutariSousa.table.GRT", locale),
185
196
  "MIT" => DiceTable::D66Table.from_i18n("FutariSousa.table.MIT", locale),
197
+ "MITE" => DiceTable::Table.from_i18n("FutariSousa.table.MITE", locale),
186
198
  "JBT66" => DiceTable::D66Table.from_i18n("FutariSousa.table.JBT66", locale),
187
199
  "JBT10" => DiceTable::Table.from_i18n("FutariSousa.table.JBT10", locale),
188
200
  "FST66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FST66", locale),
@@ -191,6 +203,19 @@ module BCDice
191
203
  "LDT10" => DiceTable::Table.from_i18n("FutariSousa.table.LDT10", locale),
192
204
  "FLT66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLT66", locale),
193
205
  "FLT10" => DiceTable::Table.from_i18n("FutariSousa.table.FLT10", locale),
206
+ "FLTL66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTL66", locale),
207
+ "FLTD66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTD66", locale),
208
+ "FLTRA" => DiceTable::Table.from_i18n("FutariSousa.table.FLTRA", locale),
209
+ "FLTF66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTF66", locale),
210
+ "FLTB66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTB66", locale),
211
+ "FLTH66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTH66", locale),
212
+ "FLTS66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTS66", locale),
213
+ "FLTA66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTA66", locale),
214
+ "FLTW66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTW66", locale),
215
+ "FLTC66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTC66", locale),
216
+ "FLTU66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTU66", locale),
217
+ "FLTO66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTO66", locale),
218
+ "FLTI66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTI66", locale),
194
219
  "NCT66" => DiceTable::D66Table.from_i18n("FutariSousa.table.NCT66", locale),
195
220
  "NCT10" => DiceTable::Table.from_i18n("FutariSousa.table.NCT10", locale),
196
221
  }
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class GhostLive < Base
6
+ # ゲームシステムの識別子
7
+ ID = 'GhostLive'
8
+
9
+ # ゲームシステム名
10
+ NAME = '実況ゴーストライヴ'
11
+
12
+ # ゲームシステム名の読みがな
13
+ SORT_KEY = 'しつきようこおすとらいふ'
14
+
15
+ # ダイスボットの使い方
16
+ HELP_MESSAGE = <<~MESSAGE
17
+ ■追加目標表(p11)
18
+ ATT, AdditionalTargetTable
19
+
20
+ ■種別:地縛霊(p26)
21
+ □A.霊障リスト
22
+ JHA, JibakuHauntA
23
+ □B.霊障効果リスト
24
+ JHB, JibakuHauntB
25
+
26
+ ■種別:シャイな幽霊(p27)
27
+ □A.霊障リスト
28
+ SHA, ShyHauntA
29
+ □B.霊障効果リスト
30
+ SHB, ShyHauntB
31
+
32
+ ■種別:ぐちゃぐちゃ(p28)
33
+ □A.霊障リスト
34
+ GHA, GuchaHauntA
35
+ □B.霊障効果リスト
36
+ GHB, GuchaHauntB
37
+ MESSAGE
38
+
39
+ def eval_game_system_specific_command(command)
40
+ command = ALIAS[command] || command
41
+ roll_tables(command, TABLES)
42
+ end
43
+
44
+ TABLES = {
45
+ "AdditionalTargetTable" => DiceTable::Table.new(
46
+ "追加目標表",
47
+ "1D6",
48
+ [
49
+ "オバケを撮影する。(依頼主:専門家/報酬:1L)",
50
+ "誰かひとりが霊障を[サイクル数]回受ける。(依頼主:専門家/報酬:[サイクル数]L)",
51
+ "誰かひとりが[精神力]を10以下の状態で帰る。(依頼主:専門家/報酬:3L)",
52
+ "[精神力]の平均が20以下の状態で帰る。(依頼主:リスナー/報酬:[視聴回数]を10倍)",
53
+ "全員がスマホ以外の[アイテム]を1個だけ持ち込んで生還する。(依頼主:リスナー/報酬:[視聴回数]を10倍)",
54
+ "すべての[回収品]を集める。(依頼主:専門家/報酬:5L)",
55
+ ]
56
+ ),
57
+ "JibakuHauntA" => DiceTable::Table.new(
58
+ "地縛霊:霊障リスト",
59
+ "1D6",
60
+ [
61
+ "隙間――家具の隙間、扉の隙間、そんな暗がりから視線を感じる。",
62
+ "腐臭――吐き気を催すような、下水に似た臭いが漂ってくる。",
63
+ "吐息――「ハァ……」耳元に、やけに湿った吐息が吹きかけられる。",
64
+ "足音――立ち止まる度に、ひとつ多く足音が響く。誰か、いる……?",
65
+ "背後――振り向いても、そこには誰もいない。それなのに、ずっと後ろに気配を感じる。",
66
+ "鏡――鏡に背を向けた瞬間、あり得ない強さでそちらへ引き寄せられた。肩には手の形のアザができている。",
67
+ ]
68
+ ),
69
+ "JibakuHauntB" => DiceTable::Table.new(
70
+ "地縛霊:霊障効果リスト",
71
+ "1D6",
72
+ [
73
+ "[精神力]減少:[1D2+PC人数]点/[視聴回数]増加:とくになし/特殊効果:とくになし",
74
+ "[精神力]減少:[1D4+PC人数]点/[視聴回数]増加:とくになし/特殊効果:とくになし",
75
+ "[精神力]減少:[1D6+PC人数]点/[視聴回数]増加:2倍/特殊効果:とくになし",
76
+ "[精神力]減少:[1D10+PC人数]点/[視聴回数]増加:3倍/特殊効果:シーンに登場しているPCの[アイテム]を1つ破壊する。",
77
+ "[精神力]減少:[1D20+PC人数]点/[視聴回数]増加:5倍/特殊効果:シーンに登場しているPCのスマホを破壊する。",
78
+ "[精神力]減少:[1D100+PC人数]点/[視聴回数]増加:10倍/特殊効果:シーンに登場しているPCのスマホを破壊する。",
79
+ ]
80
+ ),
81
+ "ShyHauntA" => DiceTable::Table.new(
82
+ "シャイな幽霊:霊障リスト",
83
+ "1D6",
84
+ [
85
+ "倦怠感――歩くのも辛いくらいの倦怠感。生きているのも辛い。",
86
+ "ラップ音――弾けるような、叩くような音が連続して聞こえる。",
87
+ "空飛ぶ皿――棚に収まっていた食器が、不意に飛び出し、けたたましい音を立てて砕けていく。",
88
+ "頭痛――頭が、割れそうに痛い。小さな物音ですら頭に響いてくる。",
89
+ "点滅――灯りが明滅する。……あれ、ここ電気通ってたっけ?",
90
+ "血文字――壁に、床に、赤⿊い液体が滲み出す。それは文字を形作った。「か え れ」",
91
+ ]
92
+ ),
93
+ "ShyHauntB" => DiceTable::Table.new(
94
+ "シャイな幽霊:霊障効果リスト",
95
+ "1D6",
96
+ [
97
+ "[精神力]減少:[2+PC人数]点/[視聴回数]増加:とくになし/特殊効果:とくになし",
98
+ "[精神力]減少:[4+PC人数]点/[視聴回数]増加:2倍/特殊効果:シーンに登場しているPCがふたりの場合、追加で[精神力]を2減少させる。",
99
+ "[精神力]減少:[6+PC人数]点/[視聴回数]増加:3倍/特殊効果:シーンに登場しているPCがひとりの場合、追加で[精神力]を4減少させる。",
100
+ "[精神力]減少:[10+PC人数]点/[視聴回数]増加:5倍/特殊効果:シーンに登場しているPCがふたりの場合、追加で[精神力]を6減少させる。",
101
+ "[精神力]減少:[20+PC人数]点/[視聴回数]増加:10倍/特殊効果:シーンに登場しているPCがひとりの場合、追加で[精神力]を2減少させる。",
102
+ "[精神力]減少:[40+PC人数]点/[視聴回数]増加:20倍/特殊効果:シーンに登場しているPCのスマホを破壊する。",
103
+ ]
104
+ ),
105
+ "GuchaHauntA" => DiceTable::Table.new(
106
+ "ぐちゃぐちゃ:霊障リスト",
107
+ "1D6",
108
+ [
109
+ "走る人形――ひび割れた人形が落ちている。一瞬視線をそらした瞬間、それはありえない動きで走り去っていった。",
110
+ "血痕――天井から血が滴ってくる。その量は、おおよそ人一人分……いや、それ以上だ。",
111
+ "着信――スマホの着信音が鳴る。こんな時に誰が――表示されていたのは、死んだはずの知り合いの名前だった。",
112
+ "自分に似た他人――自分にそっくりな人が目の前に立っていた、気がする。",
113
+ "衝撃――誰かに思いっきり押された気がしたのに誰もいない。",
114
+ "記憶がない――数分間のことを何も覚えてない。コメント欄がリスナーの心配する声でいっぱいだ。いったい何が……?",
115
+ ]
116
+ ),
117
+ "GuchaHauntB" => DiceTable::Table.new(
118
+ "ぐちゃぐちゃ:霊障効果リスト",
119
+ "1D6",
120
+ [
121
+ "[精神力]減少:[5+PC人数]点/[視聴回数]増加:2倍/特殊効果:とくになし",
122
+ "[精神力]減少:[10+PC人数]点/[視聴回数]増加:3倍/特殊効果:とくになし",
123
+ "[精神力]減少:[2D10+PC人数]点/[視聴回数]増加:4倍/特殊効果:シーンに登場しているPCがふたりの場合、追加で[精神力]を5減少させる。",
124
+ "[精神力]減少:[3D10+PC人数]点/[視聴回数]増加:5倍/特殊効果:シーンに登場しているPCがひとりの場合、[アイテム]をランダムに1つ壊す。",
125
+ "[精神力]減少:[1D100+PC人数]点/[視聴回数]増加:10倍/特殊効果:シーンに登場しているPCのスマホを破壊する。",
126
+ "[精神力]減少:[1D100+10+PC人数]点/[視聴回数]増加:20倍/特殊効果:すべてのPCのスマホを破壊する。",
127
+ ]
128
+ ),
129
+ }.transform_keys(&:upcase).freeze
130
+
131
+ ALIAS = {
132
+ "ATT" => "AdditionalTargetTable",
133
+ "JHA" => "JibakuHauntA",
134
+ "JHB" => "JibakuHauntB",
135
+ "SHA" => "ShyHauntA",
136
+ "SHB" => "ShyHauntB",
137
+ "GHA" => "GuchaHauntA",
138
+ "GHB" => "GuchaHauntB",
139
+ }.transform_values(&:upcase).freeze
140
+
141
+ register_prefix(TABLES.keys, ALIAS.keys)
142
+ end
143
+ end
144
+ end
@@ -14,11 +14,13 @@ module BCDice
14
14
 
15
15
  # ダイスボットの使い方
16
16
  HELP_MESSAGE = <<~MESSAGETEXT
17
- ・判定 GS(x)>=y
17
+ ・判定 GS(x)@c#f>=y
18
18
   2d6の判定を行い、達成値を出力します。
19
-  xは基準値、yは目標値です。いずれも省略可能です。
19
+  xは基準値、yは目標値、cは大成功の下限、fは大失敗の上限です。いずれも省略可能です。
20
+  cが未指定の場合には c=12 、fが未指定の場合には f=2 となります。
20
21
   yが設定されている場合、大成功/成功/失敗/大失敗を自動判定します。
21
22
   例)GS>=12 GS>10 GS(10)>14 GS+10>=15 GS10>=15 GS(10) GS+10 GS10 GS
23
+    GS@10 GS@10#3 GS#3@10
22
24
 
23
25
  ・祈念 MCPI(n)$m
24
26
   祈念を行います。
@@ -62,15 +64,26 @@ module BCDice
62
64
  end
63
65
 
64
66
  def getCheckResult(command)
65
- m = /^GS([-+]?\d+)?((>=?)(\d+))?$/i.match(command)
67
+ m = /^GS([-+]?\d+)?(?:(?:([@#])([-+]?\d+))(?:([@#])([-+]?\d+))?)?(?:(>=?)(\d+))?$/i.match(command)
66
68
  unless m
67
69
  return nil
68
70
  end
69
71
 
70
72
  basis = m[1].to_i # 基準値
71
- target = m[4].to_i
72
- without_compare = m[2].nil? || target <= 0
73
- cmp_op = m[3]
73
+ target = m[7].to_i
74
+ cmp_op = m[6]
75
+ without_compare = cmp_op.nil?
76
+
77
+ option1 = m[2]
78
+ option1_value = m[3]
79
+ option2 = m[4]
80
+ option2_param = m[5]
81
+
82
+ if option1 && option1 == option2
83
+ return nil
84
+ end
85
+
86
+ threshold_critical, threshold_fumble = calc_threshold(option1, option1_value, option2, option2_param)
74
87
 
75
88
  dice_list = @randomizer.roll_barabara(2, 6)
76
89
  total = dice_list.sum()
@@ -78,8 +91,8 @@ module BCDice
78
91
 
79
92
  achievement = basis + total # 達成値
80
93
 
81
- fumble = diceText == "1,1"
82
- critical = diceText == "6,6"
94
+ fumble = total <= threshold_fumble
95
+ critical = total >= threshold_critical
83
96
 
84
97
  result = " > #{resultStr(achievement, target, cmp_op, fumble, critical)}"
85
98
  if without_compare && !fumble && !critical
@@ -90,6 +103,15 @@ module BCDice
90
103
  return "(#{command}) > #{basis_str}#{total}[#{diceText}] > #{achievement}#{result}"
91
104
  end
92
105
 
106
+ CRITICAL = 12
107
+ FUMBLE = 2
108
+
109
+ def calc_threshold(option1, option1_value, _option2, option2_value)
110
+ critical, fumble = option1 == "@" ? [option1_value, option2_value] : [option2_value, option1_value]
111
+
112
+ return [critical&.to_i || CRITICAL, fumble&.to_i || FUMBLE]
113
+ end
114
+
93
115
  def murmurChantPrayInvoke(command)
94
116
  m = /^MCPI(\+?\d+)?\$(\d+)$/i.match(command)
95
117
  unless m