bcdice 3.10.0 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
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