bcdice 3.10.0 → 3.11.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c6faf3616617f8dc79b147cd5d93341a8538d1aafac34809b349dafb599f3d64
4
- data.tar.gz: a5a01580edebb96d05325a26eedcd0828549920bc96335e36161016cd0440dce
3
+ metadata.gz: 5804af6296cb509b73b2489953c75a3ebc2c88a6be93f398cfa6b46d69eabe26
4
+ data.tar.gz: a1b868d3706fd20040ed1720f572cbbc60479b7b43f500361cd49af5d8dce5b2
5
5
  SHA512:
6
- metadata.gz: 2521e3a500b0a0d5f4144dfce4bdbc6b214353481af1b2a0b3ea3e24de957ca9be8bf7816cfedf6c58b22ec61a523bce912acb086273c2d8c0d3417cee61d314
7
- data.tar.gz: 4bc0186552baaa10cd0c4d7d87b23f7573c3eddfcb8000be5a79ed8ffee859d0776940c5397e24755a14ec6f1b1ee3087de5a83a4a2c1060398f4739a5776a31
6
+ metadata.gz: c84ded2153b972ef840a345142fa2d599f6e023eb309ee3e5761bd8f74b9a8e2f6c2437b0c8cbbbf40b8dffe273910179e0baac2db10c9194b65dab5ea630c35
7
+ data.tar.gz: 744050243951df6df9b0816f3d7c6f71ff82796278b42438a828c1e9797224d18cf830ad6818ee8715fe4c6f799ef5d2cf4a7fad18655d09a086bde44d133ce4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Change Log
2
2
 
3
+ ## 3.11.0 2023/08/23
4
+
5
+ ### 対応ゲームシステムの追加
6
+ - ガンダム・センチネルRPG (GundamSentinel): Facelessさんありがとうっ! ([#633](https://github.com/bcdice/BCDice/pull/633), [#634](https://github.com/bcdice/BCDice/pull/634))
7
+ - マモノスクランブル (MamonoScramble): ([#635](https://github.com/bcdice/BCDice/pull/635))
8
+ - デモンスパイク (DemonSpike): ([#636](https://github.com/bcdice/BCDice/pull/636))
9
+ - RuneQuest:Roleplaying in Glorantha (RuneQuestRoleplayingInGlorantha): フレッド緑野さんありがとうっ! ([#637](https://github.com/bcdice/BCDice/pull/637))
10
+ - ゾンビライン (ZombiLine): 蜂紫さんありがとうっ! ([#638](https://github.com/bcdice/BCDice/pull/638/files))
11
+
3
12
  ## 3.10.0 2023/07/15
4
13
 
5
14
  ### 対応ゲームシステムの追加
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class DemonSpike < Base
6
+ # ゲームシステムの識別子
7
+ ID = 'DemonSpike'
8
+
9
+ # ゲームシステム名
10
+ NAME = 'デモンスパイク'
11
+
12
+ # ゲームシステム名の読みがな
13
+ SORT_KEY = 'てもんすはいく'
14
+
15
+ # ダイスボットの使い方
16
+ HELP_MESSAGE = <<~INFO_MESSAGE_TEXT
17
+ ・行為判定 xDS+y
18
+  行為判定を行い、達成値、成否、成功度を出力する。
19
+  x: ダイス数(省略:2)
20
+  y: 能力値やスパイク能力による達成値の修正(省略可)
21
+ INFO_MESSAGE_TEXT
22
+
23
+ register_prefix('\d*DS')
24
+
25
+ def eval_game_system_specific_command(command)
26
+ roll_action(command)
27
+ end
28
+
29
+ private
30
+
31
+ def roll_action(command)
32
+ parser = Command::Parser.new("DS", round_type: @round_type)
33
+ .enable_prefix_number
34
+ .restrict_cmp_op_to(nil)
35
+ parsed = parser.parse(command)
36
+ unless parsed
37
+ return nil
38
+ end
39
+
40
+ parsed.prefix_number ||= 2
41
+ if parsed.prefix_number < 2
42
+ return nil
43
+ end
44
+
45
+ step = roll_step(parsed.prefix_number)
46
+ step_list = [step]
47
+ while step[:dice_sum] == 10
48
+ step = roll_step(parsed.prefix_number)
49
+ step_list.push(step)
50
+ end
51
+
52
+ is_fumble = step_list[0][:dice_sum] == 2
53
+ total = is_fumble ? 0 : step_list.sum { |s| s[:dice_sum] } + parsed.modify_number
54
+ success_level = total / 10
55
+ is_success = total >= 10
56
+
57
+ res =
58
+ if is_success
59
+ "成功, 成功度#{success_level}"
60
+ elsif is_fumble
61
+ "自動的失敗"
62
+ else
63
+ "失敗"
64
+ end
65
+
66
+ sequence = [
67
+ "(#{parsed})",
68
+ step_list.map { |s| "#{s[:dice_sum]}[#{s[:dice_list].join(',')}]" },
69
+ total,
70
+ res,
71
+ ].flatten
72
+
73
+ return Result.new.tap do |r|
74
+ r.condition = is_success
75
+ r.critical = step_list.length > 1
76
+ r.fumble = is_fumble
77
+ r.text = sequence.join(" > ")
78
+ end
79
+ end
80
+
81
+ def roll_step(times)
82
+ dice_list = @randomizer.roll_barabara(times, 6).sort.reverse
83
+ dice_sum = (dice_list[0] + dice_list[1]).clamp(2, 10)
84
+
85
+ return {dice_list: dice_list, dice_sum: dice_sum}
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,377 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class GundamSentinel < Base
6
+ # ゲームシステムの識別子
7
+ ID = 'GundamSentinel'
8
+
9
+ # ゲームシステム名
10
+ NAME = 'ガンダム・センチネルRPG'
11
+
12
+ # ゲームシステム名の読みがな
13
+ SORT_KEY = 'かんたむせんちねる'
14
+
15
+ # ダイスボットの使い方
16
+ HELP_MESSAGE = <<~INFO_MESSAGE_TEXT
17
+ ・基本戦闘(BB, BBM)
18
+  BB[+修正][>回避値]で基本戦闘を判定します。回避値を指定すると、命中・回避も表示します。
19
+  BBM[+修正][>回避値]でモブ用の基本戦闘を判定します。クリティカルを判定します。回避値を指定すると、命中・回避も表示します。
20
+
21
+  例)BB BBM BB+5>14 BBM+5>15
22
+
23
+ ・一般技能(GS)
24
+  GS[+修正][>目標値]で一般技能を判定します。目標値を指定しない場合は、目標値10で判定します。
25
+
26
+  例)GS GS+5 GS+5>10
27
+
28
+
29
+ ・各種表
30
+  敵MSクリティカルヒットチャート (ECHC)
31
+  PC用脱出判定チャート      (PEJC[+m] m:修正)
32
+  艦船追加ダメージ決定チャート  (ASDC)
33
+  対空砲結果チャート       (AARC[+m]=t m:修正, t:対空防御力)
34
+  リハビリ判定チャート      (RTJC[+m] m:修正)
35
+  二次被害判定チャート      (SDDC)
36
+ INFO_MESSAGE_TEXT
37
+
38
+ def initialize(command)
39
+ super(command)
40
+
41
+ @round_type = RoundType::CEIL
42
+ @d66_sort_type = D66SortType::NO_SORT
43
+ end
44
+
45
+ def eval_game_system_specific_command(command)
46
+ roll_basic_battle(command) ||
47
+ roll_general_skill(command) ||
48
+ roll_anti_aircraft_gun_result_chart(command) ||
49
+ roll_escape_chart(command) ||
50
+ roll_rehabilitation_chart(command) ||
51
+ roll_tables(command, TABLES)
52
+ end
53
+
54
+ private
55
+
56
+ # 基本戦闘ロール
57
+ def roll_basic_battle(command)
58
+ m = /^BB(M)?([-+][-+\d]+)?(>([-+\d]+))?/.match(command)
59
+ return nil unless m
60
+
61
+ mob = m[1]
62
+ modify = ArithmeticEvaluator.eval(m[2])
63
+ have_modify = false
64
+ have_modify = true if m[2]
65
+ avoid = ArithmeticEvaluator.eval(m[4])
66
+ have_avoid = false
67
+ have_avoid = true if m[4]
68
+
69
+ d60 = @randomizer.roll_once(6)
70
+ d06 = @randomizer.roll_once(6)
71
+ total_d = d60 * 10 + d06
72
+ d60 += (d06 + modify - 1).div(6)
73
+ d06 = (d06 + modify - 1).modulo(6) + 1
74
+ total = d60 * 10 + d06
75
+ total = 11 if total < 11
76
+
77
+ success = false
78
+ failure = false
79
+ critical = false
80
+
81
+ modify_label = nil
82
+ if have_modify
83
+ if modify >= 0
84
+ modify_label = "#{total_d}+#{modify}"
85
+ else
86
+ modify_label = "#{total_d}#{modify}"
87
+ end
88
+ end
89
+
90
+ critical_label = nil
91
+ if mob && (total >= 66)
92
+ critical_label = "クリティカル"
93
+ critical = true
94
+ end
95
+
96
+ result = nil
97
+ if have_avoid
98
+ if total > avoid
99
+ result = "命中(+" + count_success(total, avoid).to_s + ")"
100
+ success = true
101
+ else
102
+ result = "回避"
103
+ failure = true
104
+ end
105
+ end
106
+
107
+ sequence = [
108
+ "(#{command})",
109
+ modify_label,
110
+ total,
111
+ result,
112
+ critical_label,
113
+ ].compact
114
+
115
+ Result.new(sequence.join(" > ")).tap do |r|
116
+ r.success = success
117
+ r.failure = failure
118
+ r.critical = critical
119
+ end
120
+ end
121
+
122
+ def count_success(dice, avoid)
123
+ d60 = dice.div(10)
124
+ d06 = dice.modulo(10)
125
+ a60 = avoid.div(10)
126
+ a06 = avoid.modulo(10)
127
+
128
+ return ((d60 * 6 + d06) - (a60 * 6 + a06))
129
+ end
130
+
131
+ # 一般技能ロール
132
+ def roll_general_skill(command)
133
+ m = /^GS([-+][-+\d]+)?(>([-+\d]+))?/.match(command)
134
+ return nil unless m
135
+
136
+ modify = ArithmeticEvaluator.eval(m[1])
137
+ have_modify = false
138
+ have_modify = true if m[1]
139
+ target = ArithmeticEvaluator.eval(m[3])
140
+ target = 10 unless m[3]
141
+
142
+ success = false
143
+ failure = false
144
+
145
+ dice = @randomizer.roll_sum(2, 6)
146
+
147
+ modify_label = nil
148
+ if have_modify
149
+ if modify >= 0
150
+ modify_label = "#{dice}+#{modify}"
151
+ else
152
+ modify_label = "#{dice}#{modify}"
153
+ end
154
+ end
155
+ total = dice + modify
156
+ if total > target
157
+ result = "成功"
158
+ success = true
159
+ else
160
+ result = "失敗"
161
+ failure = true
162
+ end
163
+
164
+ sequence = [
165
+ "(#{command})",
166
+ modify_label,
167
+ total,
168
+ result,
169
+ ].compact
170
+
171
+ Result.new(sequence.join(" > ")).tap do |r|
172
+ r.success = success
173
+ r.failure = failure
174
+ end
175
+ end
176
+
177
+ # 各種表
178
+
179
+ # 対空砲結果チャート
180
+ GUN_RESULT_CHART = [
181
+ ["D", "H", "H", "H", 10, 8, 6, 5, 4, 2, 1, "-", "-"],
182
+ ["D", "H", "H", "H", 12, 10, 9, 8, 6, 5, 3, 2, "-"],
183
+ ["D", "D", "H", "H", "H", 12, 10, 9, 7, 6, 4, 3, 1],
184
+ ["D", "D", "H", "H", "H", 14, 13, 12, 10, 8, 6, 5, 3],
185
+ ["D", "D", "D", "H", "H", "H", 14, 13, 11, 9, 7, 6, 4],
186
+ ["D", "D", "D", "H", "H", "H", "H", 16, 14, 12, 11, 8, 6],
187
+ ].freeze
188
+
189
+ def roll_anti_aircraft_gun_result_chart(command)
190
+ parser = Command::Parser.new("AARC", round_type: @round_type).restrict_cmp_op_to(:==)
191
+ parsed = parser.parse(command)
192
+
193
+ return nil unless parsed
194
+
195
+ target = parsed.target_number.clamp(1, 6)
196
+ dice = @randomizer.roll_sum(2, 6)
197
+ total = (dice + parsed.modify_number).clamp(1, 13)
198
+
199
+ cmd =
200
+ if parsed.modify_number != 0
201
+ "(#{dice}#{Format.modifier(parsed.modify_number)}=#{total})"
202
+ else
203
+ total.to_s
204
+ end
205
+ result = GUN_RESULT_CHART[target - 1][total - 1]
206
+
207
+ Result.new().tap do |r|
208
+ r.text = "対空砲結果チャート(#{cmd}vs#{target}) > 結果「#{result}」"
209
+ r.condition = result.is_a?(Integer)
210
+ end
211
+ end
212
+
213
+ # PC用脱出判定チャート
214
+ ESCAPE_CHART = [
215
+ '*',
216
+ '*',
217
+ '無傷で脱出',
218
+ '無傷で脱出',
219
+ '無傷で脱出',
220
+ '軽傷で脱出「1D6ダメージ。」',
221
+ '中傷で脱出「2D6ダメージ。」',
222
+ '重傷で脱出「3D6ダメージ。」',
223
+ '重体で脱出「1D3の耐久力が残る。」',
224
+ '戦死「二階級特進。」',
225
+ '戦死「二階級特進。」',
226
+ '戦死「二階級特進。」',
227
+ '戦死「二階級特進。」',
228
+ ].freeze
229
+
230
+ def roll_escape_chart(command)
231
+ parser = Command::Parser.new("PEJC", round_type: @round_type).restrict_cmp_op_to(nil)
232
+ parsed = parser.parse(command)
233
+ return nil unless parsed
234
+
235
+ dice = @randomizer.roll_sum(2, 6)
236
+ total = (dice + parsed.modify_number).clamp(2, 12)
237
+
238
+ cmd =
239
+ if parsed.modify_number != 0
240
+ "#{dice}#{Format.modifier(parsed.modify_number)}=#{total}"
241
+ else
242
+ total.to_s
243
+ end
244
+ result = ESCAPE_CHART[total]
245
+
246
+ Result.new("PC用脱出判定チャート(#{cmd}) > #{result}")
247
+ end
248
+
249
+ REHABILITATION_CHART = [
250
+ '*',
251
+ '*',
252
+ 'なし',
253
+ '1ヶ月',
254
+ '2ヶ月',
255
+ '3ヶ月',
256
+ '4ヶ月',
257
+ '5ヶ月',
258
+ '6ヶ月',
259
+ '10ヶ月',
260
+ '1年',
261
+ '1年6ヶ月',
262
+ '1年と、もう一度このチャートで振った結果分を足した期間',
263
+ ].freeze
264
+
265
+ def roll_rehabilitation_chart(command)
266
+ parser = Command::Parser.new("RTJC", round_type: @round_type).restrict_cmp_op_to(nil)
267
+ parsed = parser.parse(command)
268
+ return nil unless parsed
269
+
270
+ dice = @randomizer.roll_sum(2, 6)
271
+ total = (dice + parsed.modify_number).clamp(2, 12)
272
+
273
+ cmd =
274
+ if parsed.modify_number != 0
275
+ "#{dice}#{Format.modifier(parsed.modify_number)}=#{total}"
276
+ else
277
+ total.to_s
278
+ end
279
+ result = REHABILITATION_CHART[total]
280
+
281
+ Result.new("リハビリ判定チャート(#{cmd}) > #{result}")
282
+ end
283
+
284
+ TABLES = {
285
+ 'ECHC' => DiceTable::Table.new(
286
+ '敵MSクリティカルヒットチャート',
287
+ '2D6',
288
+ [
289
+ 'コックピット直撃:目標MSは残骸となる。',
290
+ '腕破損:同時に携帯武器も失う。携帯武装の交換も行えない。直ちにモラル判定を-4で行う。',
291
+ '射撃武装破損:目標MSはその時点で使用しているナンバーの若い武装を1つ失う。全ての武装を失った場合、モラル判定を行う。',
292
+ '頭部直撃:目標MSはメインカメラを失い、以後射撃、格闘の命中判定に-6の修正を受ける。頭部に装備されている武装も失われる。',
293
+ 'パイロット気絶:目標MSは回復するまで行動不能。',
294
+ '目標MSへのダメージ2倍。',
295
+ '目標MSへのダメージ2倍。',
296
+ '目標MSへのダメージ3倍。',
297
+ '脚破損:目標MSは、以後の回避値に-6の修正を受ける。',
298
+ 'コントロール不能:目標MSは1D6ラウンドの間、行動不能。',
299
+ '熱核ジェネレーター直撃:目標MSは直ちに爆発(耐久力0)する。',
300
+ ]
301
+ ),
302
+ 'ASDC' => DiceTable::Table.new(
303
+ '艦船追加ダメージ決定チャート',
304
+ '2D6',
305
+ [
306
+ 'ブリッジ損傷「複数ある艦は、総てのブリッジが損傷すると以後の対空防御は修正を+5する。」',
307
+ 'カタパルト損傷「複数ある艦は、総てのカタパルトが損傷すると、MSの発着艦ができなくなる。」',
308
+ '追加ダメージ「追加2D6×2ダメージ。」',
309
+ '主砲大破「主砲1門を失う。」',
310
+ '副砲大破「副砲1門を失う。」',
311
+ '追加ダメージ「追加2D6ダメージ。」',
312
+ '追加ダメージ「追加2D6ダメージ。」',
313
+ '追加ダメージ「追加2D6ダメージ。」',
314
+ '1ターン行動不能「1ターンはその艦は何も行動ができない。」',
315
+ '航行不能「その艦はそのヘックスから動けなくなる。」',
316
+ 'エンジン誘爆「1D6×10%の耐久力を失う。」',
317
+ ]
318
+ ),
319
+ 'SDDC' => DiceTable::D66GridTable.new(
320
+ '二次被害判定チャート',
321
+ [
322
+ [
323
+ '奇蹟的に無傷「不発!?今回のダメージは0。」',
324
+ 'メインカメラ破損「以後、射撃、格闘の命中判定に-3の修正を受ける。」',
325
+ 'コクピット破損「以後の追加ダメージ判定に-1の修正を受ける。」',
326
+ '右腕損傷「携帯していた武装も失う。また右腕での武器の使用はできなくなる。」',
327
+ '左腕損傷「携帯していた武装も失う。また左腕での武器の使用はできなくなる。」',
328
+ '気絶「気絶判定の余地無く、必ず気絶する。」',
329
+ ],
330
+ [
331
+ '気絶「気絶判定を-6の修正で行う。」',
332
+ '気絶「気絶判定を-4の修正で行う。」',
333
+ '気絶「気絶判定を-2の修正で行う。」',
334
+ '気絶「気絶判定を行う。」',
335
+ '予備弾倉破損「携帯している予備弾倉かEパックを1つ失う。」',
336
+ 'サブカメラ破損「以後、射撃、格闘の命中判定に-1の修正を受ける。」',
337
+ ],
338
+ [
339
+ '固定武装破損「固定されている武装を1つ失う。」',
340
+ '予備武装破損「携帯している以外の武装を1つ失う。」',
341
+ '頭部破損「メインカメラも失い、以後、射撃、格闘の命中判定に-3の修正を受ける。」',
342
+ '右脚破損「以後、回避値が1D3低下する。」',
343
+ '左脚破損「以後、回避値が1D3低下する。」',
344
+ '操縦機構破損「以後、すべての行動は消費行動ポイントを1ポイント余分に消費する。」',
345
+ ],
346
+ [
347
+ '軽傷「パイロットは1D6のダメージを受ける。また気絶判定を行う。」',
348
+ '中傷「パイロットは2D6のダメージを受ける。また気絶判定を-6修正で行う。」',
349
+ '重傷「パイロットは3D6のダメージを受ける。また気絶判定を-9修正で行う。」',
350
+ '操縦伝達部破損「以後すべての射撃、格闘の命中判定と回避値に-1の修正を受ける。」',
351
+ 'センサー破損「イニシアティブ決定に-1の修正を受ける。」',
352
+ '脱出機構破損「脱出判定に+3の修正を受ける。」',
353
+ ],
354
+ [
355
+ '熱核ジェネレーター損傷「行動の「追加移動」が行えなくなる。」',
356
+ '右腕の携帯武装破損「右腕に持っていた武装を1つ失う。」',
357
+ '左腕の携帯武装破損「左腕に持っていた武装を1つ失う。」',
358
+ 'サブスラスター破損「回避値が1低下する。」',
359
+ 'プロペラントタンク破損「プロペラントタンクを1つ失う。」',
360
+ 'バックパック破損「推進剤3D6ポイント失う。」',
361
+ ],
362
+ [
363
+ 'メインスラスター破損「回避値が1D6低下する。」',
364
+ '動力パイプ破損「以後、行動ポイント決定のダイスに-1の修正を受ける。」',
365
+ '動力伝達機構破損「以後、行動ポイント決定のサイコロに-1D3の修正を受ける。」',
366
+ 'サブスラスター破損「旋回が120度までしかできなくなる。」',
367
+ 'メインスラスター破損「旋回が60度までしかできなくなる。」',
368
+ '熱核ジェネレーター直撃「そのMSは爆発する。PCは直ちに脱出判定を行う。」',
369
+ ]
370
+ ]
371
+ ),
372
+ }.freeze
373
+
374
+ register_prefix('BBM?', 'GS', 'AARC', 'PEJC', 'RTJC', TABLES.keys)
375
+ end
376
+ end
377
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class MamonoScramble < Base
6
+ # ゲームシステムの識別子
7
+ ID = 'MamonoScramble'
8
+
9
+ # ゲームシステム名
10
+ NAME = 'マモノスクランブル'
11
+
12
+ # ゲームシステム名の読みがな
13
+ SORT_KEY = 'まものすくらんふる'
14
+
15
+ # ダイスボットの使い方
16
+ HELP_MESSAGE = <<~INFO_MESSAGE_TEXT
17
+ ・判定 xMS<=t
18
+  [判定]を行う。成否と[マリョク]の上昇量を表示する。
19
+  x: ダイス数
20
+  t: 能力値(目標値)
21
+
22
+ ・アクシデント表 ACC
23
+ INFO_MESSAGE_TEXT
24
+
25
+ def initialize(command)
26
+ super(command)
27
+
28
+ @sides_implicit_d = 12
29
+ @round_type = RoundType::CEIL
30
+ end
31
+
32
+ def eval_game_system_specific_command(command)
33
+ roll_ability(command) || roll_tables(command, TABLES)
34
+ end
35
+
36
+ private
37
+
38
+ def roll_ability(command)
39
+ parser = Command::Parser.new("MS", round_type: @round_type)
40
+ .has_prefix_number
41
+ .disable_modifier
42
+ .restrict_cmp_op_to(:<=)
43
+ parsed = parser.parse(command)
44
+ unless parsed
45
+ return nil
46
+ end
47
+
48
+ dice_list = @randomizer.roll_barabara(parsed.prefix_number, 12).sort
49
+ count_success = dice_list.count { |value| value <= parsed.target_number }
50
+ count_one = dice_list.count(1)
51
+ is_critical = count_one > 0
52
+ has_twelve = dice_list.include?(12)
53
+
54
+ maryoku =
55
+ if has_twelve && !is_critical
56
+ 0
57
+ else
58
+ count_success + count_one
59
+ end
60
+
61
+ sequence = [
62
+ "(#{parsed})",
63
+ "[#{dice_list.join(',')}]",
64
+ count_success > 0 ? "成功, [マリョク]が#{maryoku}上がる" : "失敗"
65
+ ]
66
+
67
+ return Result.new.tap do |r|
68
+ r.text = sequence.join(" > ")
69
+ r.condition = count_success > 0
70
+ r.critical = r.success? && is_critical
71
+ end
72
+ end
73
+
74
+ TABLES = {
75
+ "ACC" => DiceTable::Table.new(
76
+ "アクシデント表",
77
+ "1D12",
78
+ [
79
+ "思わぬ対立:[判定]で10〜12の出目を1個でも出した場合、【耐久値】を2点減らす。",
80
+ "都市の迷宮化:[判定]に【社会】を使用できない。",
81
+ "不穏な天気:特別な効果は発生しない。",
82
+ "突然の雷雨:エリアの[特性]に[雨]や[水たまり]などを足してもいい。",
83
+ "関係ない危機:[判定]に失敗したPCの【耐久値】を2点減らす。",
84
+ "からりと晴天:エリアの[特性]に[強い日光]や[日だまり]などを足してもいい。",
85
+ "謎のお祭り:[判定]で1〜3の出目を1個でも出した場合、【耐久値】を2点回復する。",
86
+ "すごい人ごみ:エリアの[特性]に[野次馬]や[観光客]などを足してもいい。",
87
+ "マリョク乱気流:[判定]に【異質】を使用できない。",
88
+ "魔術テロ事件:GMが1Dをロールする。出目が1〜3なら【身体】、出目が4〜6なら【異質】、出目が7〜9なら【社会】が[判定]で使えない。10〜12は何も起きない。",
89
+ "マリョク低気圧:[判定]に【身体】を使用できない。",
90
+ "平穏な時間:特別な効果は発生しない。",
91
+ ]
92
+ )
93
+ }.freeze
94
+
95
+ register_prefix('\d+MS', TABLES.keys)
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class RuneQuestRoleplayingInGlorantha < Base
6
+ # ゲームシステムの識別子
7
+ ID = 'RuneQuestRoleplayingInGlorantha'
8
+
9
+ # ゲームシステム名
10
+ NAME = 'RuneQuest:Roleplaying in Glorantha'
11
+
12
+ # ゲームシステム名の読みがな
13
+ SORT_KEY = 'るうんくえすと4'
14
+
15
+ # ダイスボットの使い方
16
+ HELP_MESSAGE = <<~MESSAGETEXT
17
+ ・判定コマンド クリティカル、スペシャル、ファンブルを含めた判定を行う。
18
+ RQG<=成功率
19
+
20
+ 例1:RQG<=80 (技能値80で判定)
21
+ 例2:RQG<=80+20 (技能値100で判定)
22
+
23
+ ・抵抗判定コマンド(能動-受動) クリティカル、スペシャル、ファンブルを含めた判定を行う。
24
+ RES(能動能力-受動能力)m増強値
25
+ 増強値は省略可能。
26
+
27
+ 例1:RES(9-11) (能動能力9 vs 受動能力11で判定)
28
+ 例2:RES(9-11)m20 (能動能力9 vs 受動能力11、+20%の増強が能動側に入る判定)
29
+ 例3:RES(9)m50 (能動能力と受動能力の差が9で、+50%の増強が能動側に入る判定)
30
+
31
+ ・抵抗判定コマンド(能動側のみ) クリティカル、スペシャル、ファンブルは含めず判定を行う。
32
+ RSA(能動能力)m増強値
33
+ 増強値は省略可能。
34
+
35
+ 例1:RSA(9) (能動能力9で判定)
36
+ 例2:RSA(9)m20 (能動能力9で判定、+20%の増強が能動側に入る判定)
37
+
38
+ MESSAGETEXT
39
+
40
+ register_prefix('RQG', 'RES', 'RSA')
41
+
42
+ def eval_game_system_specific_command(command)
43
+ case command
44
+ when /RQG/i
45
+ return do_ability_roll(command)
46
+ when /RES/i
47
+ return do_resistance_roll(command)
48
+ when /RSA/i
49
+ return do_resistance_active_characteristic_roll(command)
50
+ end
51
+ return nil
52
+ end
53
+
54
+ private
55
+
56
+ # 技能などの一般判定
57
+ def do_ability_roll(command)
58
+ m = %r{\A(RQG)(<=([+-/*\d]+))?$}.match(command)
59
+ unless m
60
+ return nil
61
+ end
62
+
63
+ roll_value = @randomizer.roll_once(100)
64
+ unless m[3]
65
+ # RQGのみ指定された場合は1d100を振ったのと同じ挙動
66
+ return "(1D100) > #{roll_value}"
67
+ end
68
+
69
+ ability_value = Arithmetic.eval(m[3], RoundType::ROUND)
70
+ result_prefix_str = "(1D100<=#{ability_value}) >"
71
+
72
+ if ability_value == 0
73
+ # 0%は判定なしで失敗
74
+ return Result.failure("#{result_prefix_str} 失敗")
75
+ end
76
+
77
+ result_str = "#{result_prefix_str} #{roll_value} >"
78
+
79
+ # 判定
80
+ get_roll_result(result_str, ability_value, roll_value)
81
+ end
82
+
83
+ # 抵抗判定
84
+ def do_resistance_roll(command)
85
+ m = %r{\A(RES)([+-/*\d]+)(M([+-/*\d]+))?$}.match(command)
86
+ unless m
87
+ return nil
88
+ end
89
+
90
+ unless m[2]
91
+ return nil
92
+ end
93
+
94
+ difference_value = Arithmetic.eval(m[2], RoundType::ROUND)
95
+ difference_value = -10 if difference_value < -10
96
+
97
+ resistance_velue = 50 + (difference_value * 5)
98
+ resistance_velue += Arithmetic.eval(m[4], RoundType::ROUND) if m[4]
99
+
100
+ roll_value = @randomizer.roll_once(100)
101
+ result_str = "(1D100<=#{resistance_velue}) > #{roll_value} >"
102
+
103
+ # 判定
104
+ get_roll_result(result_str, resistance_velue, roll_value)
105
+ end
106
+
107
+ # 能動側のみの対抗判定
108
+ def do_resistance_active_characteristic_roll(command)
109
+ m = %r{\A(RSA)(\d+)(M([+-/*\d]+))?$}.match(command)
110
+ unless m
111
+ return nil
112
+ end
113
+
114
+ unless m[2]
115
+ return nil
116
+ end
117
+
118
+ active_ability_value = m[2].to_i
119
+ if active_ability_value == 0
120
+ return "0は指定できません。"
121
+ end
122
+
123
+ modifiy_value = m[4] ? Arithmetic.eval(m[4], RoundType::ROUND) : 0
124
+ roll_value = @randomizer.roll_once(100)
125
+ active_value = active_ability_value * 5 + modifiy_value
126
+ result_prefix_str = "(1D100<=#{active_value}) > #{roll_value} >"
127
+
128
+ note_str = "クリティカル/スペシャル、ファンブルは未処理。必要なら確認すること。"
129
+
130
+ if roll_value >= 96
131
+ # 96-99は無条件で失敗
132
+ Result.failure("#{result_prefix_str} 失敗\n#{note_str}")
133
+ elsif roll_value <= 5 || roll_value <= modifiy_value
134
+ # 02-05あるいは修正値以下は無条件で成功
135
+ Result.success("#{result_prefix_str} 成功\n#{note_str}")
136
+ else
137
+ # 上記全てが当てはまらない時に突破可能な能力値を算出
138
+ "#{result_prefix_str} 相手側能力値#{active_ability_value + (50 + modifiy_value - roll_value) / 5}まで成功\n#{note_str}"
139
+ end
140
+ end
141
+
142
+ # 判定結果の取得
143
+ def get_roll_result(result_str, success_value, roll_value)
144
+ critical_value = (success_value.to_f / 20).round
145
+ special_value = (success_value.to_f / 5).round
146
+ funmble_value = ((100 - success_value.to_f) / 20).round
147
+
148
+ if (roll_value == 1) || (roll_value <= critical_value)
149
+ # クリティカル(01は必ずクリティカル)
150
+ Result.critical("#{result_str} クリティカル/スペシャル")
151
+ elsif (roll_value == 100) || (roll_value >= (100 - funmble_value + 1))
152
+ # ファンブル(00は必ずファンブル)
153
+ Result.fumble("#{result_str} ファンブル")
154
+ elsif roll_value <= special_value
155
+ # スペシャル
156
+ Result.success("#{result_str} スペシャル")
157
+ elsif (roll_value <= 95) && ((roll_value <= 5) || (roll_value <= success_value))
158
+ # 成功(02-05は必ず成功で、96-99は必ず失敗)
159
+ Result.success("#{result_str} 成功")
160
+ else
161
+ # 失敗
162
+ Result.failure("#{result_str} 失敗")
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class ZombiLine < Base
6
+ # ゲームシステムの識別子
7
+ ID = "ZombiLine"
8
+
9
+ # ゲームシステム名
10
+ NAME = "ゾンビライン"
11
+
12
+ # ゲームシステム名の読みがな
13
+ SORT_KEY = "そんひらいん"
14
+
15
+ HELP_MESSAGE = <<~TEXT
16
+ ■ 判定 (xZL<=y)
17
+  x:ダイス数(省略時は1)
18
+  y:成功率
19
+
20
+ ■ 各種表
21
+  ストレス症状表 SST
22
+  食材表 IT
23
+ TEXT
24
+
25
+ def initialize(command)
26
+ super(command)
27
+ @sides_implicit_d = 10
28
+ end
29
+
30
+ def eval_game_system_specific_command(command)
31
+ return check_action(command) || roll_tables(command, TABLES)
32
+ end
33
+
34
+ def check_action(command)
35
+ parser = Command::Parser.new("ZL", round_type: @round_type)
36
+ .enable_prefix_number
37
+ .disable_modifier
38
+ .restrict_cmp_op_to(:<=)
39
+ parsed = parser.parse(command)
40
+ unless parsed
41
+ return nil
42
+ end
43
+
44
+ dice_count = parsed.prefix_number || 1
45
+ target_num = parsed.target_number
46
+
47
+ debug(dice_count)
48
+
49
+ dice_list = @randomizer.roll_barabara(dice_count, 100).sort
50
+ is_success = dice_list.any? { |i| i <= target_num }
51
+ is_critical = dice_list.any? { |i| i <= 5 }
52
+ is_fumble = dice_list.any? { |i| i >= 96 && i > target_num }
53
+ if is_critical && is_fumble
54
+ is_critical = false
55
+ is_fumble = false
56
+ end
57
+
58
+ success_message =
59
+ if is_success && is_critical
60
+ "成功(クリティカル)"
61
+ elsif is_success && is_fumble
62
+ "成功(ファンブル)"
63
+ elsif is_success
64
+ "成功"
65
+ elsif is_fumble
66
+ "失敗(ファンブル)"
67
+ else
68
+ "失敗"
69
+ end
70
+ sequence = [
71
+ "(#{parsed})",
72
+ "[#{dice_list.join(',')}]",
73
+ success_message
74
+ ]
75
+
76
+ Result.new.tap do |r|
77
+ r.text = sequence.join(" > ")
78
+ r.condition = is_success
79
+ r.critical = is_critical
80
+ r.fumble = is_fumble
81
+ end
82
+ end
83
+
84
+ TABLES = {
85
+ 'SST' => DiceTable::Table.new(
86
+ 'ストレス症状表',
87
+ '1D10',
88
+ [
89
+ '憤怒:一番近い敵を攻撃(成功率+20%)しにいきます。近くに敵がいない場合、誰かのストレスを+1させます。 頭に血が上り、誰かに怒りをぶつけます。',
90
+ '逃避:落下してでも敵から逃げるように移動します。周囲に敵が居ない場合、現実逃避します。 耐えられなくなり、逃げ出します。',
91
+ '幻覚:戦闘中は、「行動放棄(全AP)」します。戦闘以外なら、幻覚を見て笑います。 自分が望む幻覚が見えます。',
92
+ '絶叫:戦闘中は、「注目を集める(2AP)」をします。戦闘以外なら、無意味に叫びます。 思わず叫んでしまいます。',
93
+ '自傷:自ら【怪我】を負います。戦闘中は「自傷行為(1AP)」をして自分が【怪我】します。 思わず自分を傷つけます。',
94
+ '不安:誰かのストレスを1上げます。近くに誰も居ない場合、泣き出します。 不安にかられて余計なことを言います。',
95
+ '忌避:その場から一番近い対象に「石(1AP)」を投げます。それができない場合、【転倒】してうずくまります。 嫌悪感から全てを拒みます。',
96
+ '暴走:一番近い敵を攻撃しにいきます。近くに敵がいない場合、周りの意見も聞かずに安直な行動をします。 冷静でいられなくなり、直情的になります。',
97
+ '混乱:近くにいるランダムな対象に格闘で攻撃しにいきます。それができない場合、「行動放棄(全 AP)」します。 世界全てが敵に見えて攻撃します。',
98
+ '開眼:ストレスは0まで下がります。あなたは教祖となって教義をひとつつくって「布教」できます。次の症状が出るまで効果は続きます。 ゾンビだらけの世界の真理を見つけます。',
99
+ ]
100
+ ),
101
+ 'IT' => DiceTable::RangeTable.new(
102
+ '食材表',
103
+ '1d100',
104
+ [
105
+ [1..50, '生モノ食材'],
106
+ [51..80, '怪しい食材'],
107
+ [81..100, '危ない食材']
108
+ ]
109
+ )
110
+ }.freeze
111
+
112
+ register_prefix('\d*ZL', TABLES.keys)
113
+ end
114
+ end
115
+ end
@@ -56,6 +56,7 @@ require "bcdice/game_system/DarkDaysDrive"
56
56
  require "bcdice/game_system/DarkSouls"
57
57
  require "bcdice/game_system/DeadlineHeroes"
58
58
  require "bcdice/game_system/DemonParasite"
59
+ require "bcdice/game_system/DemonSpike"
59
60
  require "bcdice/game_system/DesperateRun"
60
61
  require "bcdice/game_system/DetatokoSaga"
61
62
  require "bcdice/game_system/DetatokoSaga_Korean"
@@ -93,6 +94,7 @@ require "bcdice/game_system/GoblinSlayer"
93
94
  require "bcdice/game_system/GoldenSkyStories"
94
95
  require "bcdice/game_system/Gorilla"
95
96
  require "bcdice/game_system/GranCrest"
97
+ require "bcdice/game_system/GundamSentinel"
96
98
  require "bcdice/game_system/Gundog"
97
99
  require "bcdice/game_system/GundogRevised"
98
100
  require "bcdice/game_system/GundogZero"
@@ -133,6 +135,7 @@ require "bcdice/game_system/LostRoyal"
133
135
  require "bcdice/game_system/MagicaLogia"
134
136
  require "bcdice/game_system/MagicaLogia_Korean"
135
137
  require "bcdice/game_system/MagicaLogia_SimplifiedChinese"
138
+ require "bcdice/game_system/MamonoScramble"
136
139
  require "bcdice/game_system/MeikyuDays"
137
140
  require "bcdice/game_system/MeikyuKingdom"
138
141
  require "bcdice/game_system/MeikyuKingdomBasic"
@@ -175,6 +178,7 @@ require "bcdice/game_system/RokumonSekai2"
175
178
  require "bcdice/game_system/RoleMaster"
176
179
  require "bcdice/game_system/RuinBreakers"
177
180
  require "bcdice/game_system/RuneQuest"
181
+ require "bcdice/game_system/RuneQuestRoleplayingInGlorantha"
178
182
  require "bcdice/game_system/RyuTuber"
179
183
  require "bcdice/game_system/Ryutama"
180
184
  require "bcdice/game_system/SajinsenkiAGuS"
@@ -241,3 +245,4 @@ require "bcdice/game_system/YankeeYogSothoth"
241
245
  require "bcdice/game_system/YearZeroEngine"
242
246
  require "bcdice/game_system/Yggdrasill"
243
247
  require "bcdice/game_system/ZettaiReido"
248
+ require "bcdice/game_system/ZombiLine"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BCDice
4
- VERSION = "3.10.0"
4
+ VERSION = "3.11.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bcdice
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.10.0
4
+ version: 3.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SAKATA Sinji
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-15 00:00:00.000000000 Z
11
+ date: 2023-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -193,6 +193,7 @@ files:
193
193
  - lib/bcdice/game_system/DarkSouls.rb
194
194
  - lib/bcdice/game_system/DeadlineHeroes.rb
195
195
  - lib/bcdice/game_system/DemonParasite.rb
196
+ - lib/bcdice/game_system/DemonSpike.rb
196
197
  - lib/bcdice/game_system/DesperateRun.rb
197
198
  - lib/bcdice/game_system/DetatokoSaga.rb
198
199
  - lib/bcdice/game_system/DetatokoSaga_Korean.rb
@@ -231,6 +232,7 @@ files:
231
232
  - lib/bcdice/game_system/GoldenSkyStories.rb
232
233
  - lib/bcdice/game_system/Gorilla.rb
233
234
  - lib/bcdice/game_system/GranCrest.rb
235
+ - lib/bcdice/game_system/GundamSentinel.rb
234
236
  - lib/bcdice/game_system/Gundog.rb
235
237
  - lib/bcdice/game_system/GundogRevised.rb
236
238
  - lib/bcdice/game_system/GundogZero.rb
@@ -270,6 +272,7 @@ files:
270
272
  - lib/bcdice/game_system/MagicaLogia.rb
271
273
  - lib/bcdice/game_system/MagicaLogia_Korean.rb
272
274
  - lib/bcdice/game_system/MagicaLogia_SimplifiedChinese.rb
275
+ - lib/bcdice/game_system/MamonoScramble.rb
273
276
  - lib/bcdice/game_system/MeikyuDays.rb
274
277
  - lib/bcdice/game_system/MeikyuKingdom.rb
275
278
  - lib/bcdice/game_system/MeikyuKingdomBasic.rb
@@ -312,6 +315,7 @@ files:
312
315
  - lib/bcdice/game_system/RoleMaster.rb
313
316
  - lib/bcdice/game_system/RuinBreakers.rb
314
317
  - lib/bcdice/game_system/RuneQuest.rb
318
+ - lib/bcdice/game_system/RuneQuestRoleplayingInGlorantha.rb
315
319
  - lib/bcdice/game_system/RyuTuber.rb
316
320
  - lib/bcdice/game_system/Ryutama.rb
317
321
  - lib/bcdice/game_system/SRS.rb
@@ -378,6 +382,7 @@ files:
378
382
  - lib/bcdice/game_system/YearZeroEngine.rb
379
383
  - lib/bcdice/game_system/Yggdrasill.rb
380
384
  - lib/bcdice/game_system/ZettaiReido.rb
385
+ - lib/bcdice/game_system/ZombiLine.rb
381
386
  - lib/bcdice/game_system/beginning_idol/accessories_table.rb
382
387
  - lib/bcdice/game_system/beginning_idol/bad_status_table.rb
383
388
  - lib/bcdice/game_system/beginning_idol/chain_d66_table.rb
@@ -452,7 +457,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
452
457
  - !ruby/object:Gem::Version
453
458
  version: '0'
454
459
  requirements: []
455
- rubygems_version: 3.4.12
460
+ rubygems_version: 3.4.1
456
461
  signing_key:
457
462
  specification_version: 4
458
463
  summary: BCDice is a rolling dice engine for TRPG