bcdice 3.13.0 → 3.15.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.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +104 -0
  3. data/i18n/Arianrhod/ko_kr.yml +3 -0
  4. data/i18n/Cthulhu/en_us.yml +11 -0
  5. data/i18n/Cthulhu/zh_hant.yml +1 -1
  6. data/i18n/CyberpunkRed/ja_jp.yml +389 -0
  7. data/i18n/CyberpunkRed/ko_kr.yml +389 -0
  8. data/i18n/DoubleCross/ja_jp.yml +53 -0
  9. data/i18n/DoubleCross/ko_kr.yml +52 -0
  10. data/i18n/FinalFantasyXIV/en_us.yml +4 -0
  11. data/i18n/FinalFantasyXIV/ja_jp.yml +4 -0
  12. data/i18n/FutariSousa/ja_jp.yml +284 -127
  13. data/i18n/KillDeathBusiness/ja_jp.yml +198 -0
  14. data/i18n/KizunaBullet/ja_jp.yml +653 -0
  15. data/i18n/MagicaLogia/ko_kr.yml +2 -2
  16. data/i18n/MonotoneMuseum/ja_jp.yml +246 -14
  17. data/i18n/MonotoneMuseum/ko_kr.yml +227 -0
  18. data/i18n/SkynautsBouken/ja_jp.yml +114 -0
  19. data/i18n/SkynautsBouken/ko_kr.yml +114 -0
  20. data/i18n/StratoShout/ko_kr.yml +102 -102
  21. data/i18n/TensaiGunshiNiNaro/ja_jp.yml +126 -0
  22. data/i18n/UnsungDuet/ja_jp.yml +62 -0
  23. data/i18n/UnsungDuet/ko_kr.yml +62 -0
  24. data/i18n/en_us.yml +5 -0
  25. data/i18n/zh_hant.yml +5 -0
  26. data/lib/bcdice/dice_table/range_table.rb +24 -0
  27. data/lib/bcdice/game_system/Agnostos.rb +248 -0
  28. data/lib/bcdice/game_system/Aionia.rb +131 -0
  29. data/lib/bcdice/game_system/AniMalus.rb +99 -59
  30. data/lib/bcdice/game_system/Arianrhod_Korean.rb +32 -0
  31. data/lib/bcdice/game_system/ArknightsFan.rb +245 -87
  32. data/lib/bcdice/game_system/BlackJacket.rb +244 -0
  33. data/lib/bcdice/game_system/BlackJacket_Korean.rb +244 -0
  34. data/lib/bcdice/game_system/CharonSanctions.rb +112 -0
  35. data/lib/bcdice/game_system/ConvictorDrive.rb +5 -4
  36. data/lib/bcdice/game_system/Cthulhu7th.rb +1 -1
  37. data/lib/bcdice/game_system/Cthulhu7th_ChineseTraditional/full_auto.rb +293 -0
  38. data/lib/bcdice/game_system/Cthulhu7th_ChineseTraditional/rollable.rb +43 -0
  39. data/lib/bcdice/game_system/Cthulhu7th_ChineseTraditional.rb +470 -306
  40. data/lib/bcdice/game_system/Cthulhu_English.rb +59 -0
  41. data/lib/bcdice/game_system/CyberpunkRed.rb +15 -6
  42. data/lib/bcdice/game_system/CyberpunkRed_Korean.rb +66 -0
  43. data/lib/bcdice/game_system/DivineCharger.rb +841 -0
  44. data/lib/bcdice/game_system/DoubleCross.rb +18 -1
  45. data/lib/bcdice/game_system/DoubleCross_Korean.rb +5 -1
  46. data/lib/bcdice/game_system/FinalFantasyXIV.rb +115 -0
  47. data/lib/bcdice/game_system/FinalFantasyXIV_English.rb +39 -0
  48. data/lib/bcdice/game_system/FullFace.rb +63 -12
  49. data/lib/bcdice/game_system/FutariSousa.rb +105 -13
  50. data/lib/bcdice/game_system/Garactier.rb +479 -0
  51. data/lib/bcdice/game_system/GardenOrder.rb +390 -9
  52. data/lib/bcdice/game_system/GundogRevised.rb +8 -8
  53. data/lib/bcdice/game_system/Insane_Korean.rb +1 -1
  54. data/lib/bcdice/game_system/InvisibleLiar.rb +79 -0
  55. data/lib/bcdice/game_system/KillDeathBusiness.rb +155 -3
  56. data/lib/bcdice/game_system/KimitoYell.rb +568 -0
  57. data/lib/bcdice/game_system/KizunaBullet.rb +240 -0
  58. data/lib/bcdice/game_system/KyokoShinshoku.rb +51 -8
  59. data/lib/bcdice/game_system/Magius.rb +125 -0
  60. data/lib/bcdice/game_system/Magius_3rdNewTokyoCity.rb +62 -0
  61. data/lib/bcdice/game_system/MonotoneMuseum.rb +14 -1
  62. data/lib/bcdice/game_system/MonotoneMuseum_Korean.rb +4 -4
  63. data/lib/bcdice/game_system/NRR.rb +2 -2
  64. data/lib/bcdice/game_system/NSSQ.rb +17 -9
  65. data/lib/bcdice/game_system/NervWhitePaper.rb +129 -0
  66. data/lib/bcdice/game_system/RogueLikeHalf.rb +198 -0
  67. data/lib/bcdice/game_system/RuneQuestRoleplayingInGlorantha.rb +16 -13
  68. data/lib/bcdice/game_system/ShinobiGami.rb +73 -43
  69. data/lib/bcdice/game_system/ShuumatsuBargainWars.rb +203 -0
  70. data/lib/bcdice/game_system/SkynautsBouken.rb +49 -134
  71. data/lib/bcdice/game_system/SkynautsBouken_Korean.rb +57 -0
  72. data/lib/bcdice/game_system/StratoShout_Korean.rb +1 -1
  73. data/lib/bcdice/game_system/SwordWorld.rb +5 -1
  74. data/lib/bcdice/game_system/SwordWorld2_5.rb +3 -2
  75. data/lib/bcdice/game_system/TensaiGunshiNiNaro.rb +262 -0
  76. data/lib/bcdice/game_system/TheIndieHack.rb +82 -0
  77. data/lib/bcdice/game_system/TheOneRing2nd.rb +406 -0
  78. data/lib/bcdice/game_system/TheUnofficialHollowKnightRPG.rb +185 -0
  79. data/lib/bcdice/game_system/TorgEternity.rb +35 -6
  80. data/lib/bcdice/game_system/TrailOfCthulhu.rb +139 -0
  81. data/lib/bcdice/game_system/UnsungDuet.rb +17 -75
  82. data/lib/bcdice/game_system/UnsungDuet_Korean.rb +41 -0
  83. data/lib/bcdice/game_system/Utakaze.rb +55 -2
  84. data/lib/bcdice/game_system/Warhammer4.rb +124 -3
  85. data/lib/bcdice/game_system/WoW.rb +161 -0
  86. data/lib/bcdice/game_system/YankeeMustDie.rb +192 -0
  87. data/lib/bcdice/game_system/YearZeroEngine.rb +225 -28
  88. data/lib/bcdice/game_system/Yotabana.rb +62 -0
  89. data/lib/bcdice/game_system/YuMyoKishi.rb +141 -0
  90. data/lib/bcdice/game_system/cyberpunk_red/tables.rb +111 -473
  91. data/lib/bcdice/game_system/kizuna_bullet/tables.rb +171 -0
  92. data/lib/bcdice/game_system/sword_world/rating_lexer.rb +1 -0
  93. data/lib/bcdice/game_system/sword_world/rating_options.rb +4 -1
  94. data/lib/bcdice/game_system/sword_world/rating_parsed.rb +5 -0
  95. data/lib/bcdice/game_system/sword_world/rating_parser.rb +129 -115
  96. data/lib/bcdice/game_system.rb +31 -0
  97. data/lib/bcdice/version.rb +1 -1
  98. metadata +50 -6
@@ -0,0 +1,406 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class TheOneRing2nd < Base
6
+ # ゲームシステムの識別子
7
+ ID = "TheOneRing2nd"
8
+
9
+ # ゲームシステム名
10
+ NAME = "一つの指輪:指輪物語TRPG2版"
11
+
12
+ # ゲームシステム名の読みがな
13
+ SORT_KEY = "ひとつのゆひわゆひわものかたりTRPG2"
14
+
15
+ HELP_MESSAGE = <<~TEXT
16
+ ・判定コマンド(nRG[x][@y][Az][f[0|1]][i[0|1]][w[0|1]][m[0|1]])
17
+ 判定用に難易度nを指定して判定ダイスを振る。技量ダイスx、痛打判定値y、修正値zを指定可能。
18
+ 技量ダイス、痛打判定値、修正値は0、または未指定(0と同じ)にできる。
19
+ 痛打判定値の0、未指定は痛打判定を行わない。
20
+ 修正値は判定合計値に加算され、「ガンダルフ・ルーン」や「サウロンの目」はその影響を受けない。
21
+ 例1: 13RG (難易度13 技量ダイス0個)
22
+ 例2: 13RG3 (難易度13 技量ダイス3個)
23
+ 例3: 13RG3@10A1 (難易度13 技量ダイス3個、痛打判定10、結果に1を加算)
24
+
25
+ ・表用コマンド(FD[x][f[0|1]][i[0|1]])
26
+ 表用に判定ダイスを振る。修正値xが指定可能。修正値は0、あるいは未指定(0と同じ)にできる。
27
+ 「ガンダルフ・ルーン」や「サウロンの目」は修正値の影響を受けず、値が10を越えることもない。
28
+ 例1: FD (1d12で判定)
29
+ 例2: FD1 (1d12で判定し、ダイス目に+1修正)
30
+
31
+ ・コマンドオプション
32
+ オプションは、判定コマンドなら4個まで、表用コマンドなら2個まで、順不同で指定可能(重複可)。
33
+ f: 有利(favoured)オプション。不利と同時指定時は相殺。選択された値に◎。
34
+ i: 不利(ill-favoured)オプション。有利と同時指定時は相殺。選択された値に◎。
35
+ 例1: 13RG3f (難易度13 技量ダイス3個、有利)
36
+ 例2: FD1f (1修正、有利)
37
+ 例3: 13RG3if (難易度13 技量ダイス3個、不利、有利)
38
+ ※有利/不利は相殺。
39
+
40
+ 判定コマンドでは更に下記のオプションを同じ条件で指定可能。
41
+ w: 疲労(weary)状態オプション。
42
+ m: 絶望(miserable)状態オプション。
43
+ 例1: 13RG3wf (難易度13 技量ダイス3個、疲労状態、有利)
44
+ 例2: 13RG3fiwm (難易度13 技量ダイス3個、有利、不利、疲労状態、絶望状態)
45
+ ※有利/不利は相殺。最大オプション数である4つを指定。
46
+
47
+ ・オプションスイッチ
48
+ 指定したオプションのon/offを1/0で指定可能。1はon、0はoffを表す。
49
+ 複数の同じオプションへのスイッチ指定は、最後のスイッチが有効となる。
50
+ 例1: 13RG3if0 (難易度13 技量ダイス3個、不利はon、有利はoff)
51
+ ※ 有利指定がoffのため、相殺されず不利となる。
52
+ 例2: 13RG3f1f0 (難易度13 技量ダイス3個、有利は最終的にoff)
53
+ TEXT
54
+
55
+ register_prefix('\d+RG', 'FD')
56
+
57
+ SAURONS_EYE_NUMBER = 11 # サウロンの目
58
+ GANDALF_RUNE_NUMBER = 12 # ガンダルフ・ルーン
59
+
60
+ CHOICE_DIE_MARK = '◎' # 有利/不利の状態で選択されたダイスにつけるマーク
61
+
62
+ # 有利/不利状態のenum
63
+ module FavouredState
64
+ NORMAL = -98 # 通常状態
65
+ FAVOURED = -99 # 有利状態
66
+ ILLFAVOURED = -100 # 不利状態
67
+ end
68
+
69
+ def eval_game_system_specific_command(command)
70
+ case command
71
+ when /^\d+RG/i
72
+ return rg_command_exec(command)
73
+ when /^FD/i
74
+ return fd_command_exec(command)
75
+ end
76
+ return "Error" # 到達しないはずだが、念のため
77
+ end
78
+
79
+ private
80
+
81
+ # ================ RG/FGコマンド共有メソッド等 ================#
82
+
83
+ # オプションデータクラス
84
+ class OptionData
85
+ attr_reader :favoured_state, :weary, :miserable
86
+
87
+ def initialize(favoured_state_value: FavouredState::NORMAL, weary_condition: false, miserable_condition: false)
88
+ @favoured_state = favoured_state_value
89
+ @weary = weary_condition
90
+ @miserable = miserable_condition
91
+ end
92
+ end
93
+
94
+ # 指定された修正値文字列を取得
95
+ def get_adjust_number_text(adjust_number)
96
+ adjust_number_text = ""
97
+
98
+ if adjust_number > 0
99
+ adjust_number_text = "+#{adjust_number}"
100
+ elsif adjust_number < 0
101
+ adjust_number_text = adjust_number.to_s
102
+ end
103
+
104
+ return adjust_number_text
105
+ end
106
+
107
+ # 指定された状態文字列を取得
108
+ def get_condition_text(opts)
109
+ if opts.favoured_state == FavouredState::NORMAL && !opts.weary && !opts.miserable
110
+ return ""
111
+ end
112
+
113
+ condition_text = "\n状態:"
114
+ if opts.favoured_state == FavouredState::FAVOURED
115
+ condition_text = "#{condition_text}有利 "
116
+ elsif opts.favoured_state == FavouredState::ILLFAVOURED
117
+ condition_text = "#{condition_text}不利 "
118
+ end
119
+ if opts.weary
120
+ condition_text = "#{condition_text}疲労 "
121
+ end
122
+ if opts.miserable
123
+ condition_text = "#{condition_text}絶望 "
124
+ end
125
+
126
+ return condition_text.rstrip
127
+ end
128
+
129
+ # 指定された状態文字列を取得
130
+ def get_options(opt_params)
131
+ favoured_state_value = FavouredState::NORMAL
132
+ miserable_condition = false
133
+ weary_condition = false
134
+
135
+ opt_params.each do |x|
136
+ case x[/[WFIM]/]
137
+ when "W"
138
+ weary_condition = on_option_switch?(x)
139
+ when "F"
140
+ favoured_state_value = get_favoured_state(on_option_switch?(x), favoured_state_value, FavouredState::FAVOURED)
141
+ when "I"
142
+ favoured_state_value = get_favoured_state(on_option_switch?(x), favoured_state_value, FavouredState::ILLFAVOURED)
143
+ when "M"
144
+ miserable_condition = on_option_switch?(x)
145
+ end
146
+ end
147
+
148
+ return OptionData.new(favoured_state_value: favoured_state_value, weary_condition: weary_condition, miserable_condition: miserable_condition)
149
+ end
150
+
151
+ # オプションから有利/不利状態指定を取得
152
+ def get_favoured_state(option_switch, before_favoured_state_value, tagert_state_type)
153
+ if option_switch
154
+ if before_favoured_state_value == tagert_state_type || before_favoured_state_value == FavouredState::NORMAL
155
+ return tagert_state_type
156
+ end
157
+
158
+ return FavouredState::NORMAL
159
+ else
160
+ if before_favoured_state_value == tagert_state_type
161
+ return FavouredState::NORMAL
162
+ end
163
+ end
164
+ return before_favoured_state_value
165
+ end
166
+
167
+ # オプションのON/OFFを取得
168
+ def on_option_switch?(opt_value)
169
+ if opt_value.length == 1 || opt_value[1..opt_value.length].to_i > 0
170
+ return true
171
+ end
172
+
173
+ return false
174
+ end
175
+
176
+ # 技量ダイスロールを行う
177
+ def make_successdice_roll(success_dice_count, weary_condition)
178
+ dice_list = @randomizer.roll_barabara(success_dice_count, 6)
179
+ success_count = dice_list.count(6)
180
+ if weary_condition
181
+ success_total_number = dice_list.reject { |i| i <= 3 }.sum
182
+ else
183
+ success_total_number = dice_list.sum
184
+ end
185
+ return dice_list.to_s, success_total_number, success_count
186
+ end
187
+
188
+ # 判定ダイスロールを行う
189
+ def make_featdice_roll(favoured_state_value)
190
+ feat_dice_count = favoured_state_value == FavouredState::NORMAL ? 1 : 2
191
+ dice_list = @randomizer.roll_barabara(feat_dice_count, 12)
192
+
193
+ choice_die_number = die_choice(dice_list, favoured_state_value)
194
+ if feat_dice_count > 1
195
+
196
+ choice_index = dice_list.find_index(choice_die_number)
197
+
198
+ first_die = "#{CHOICE_DIE_MARK if choice_index == 0}#{get_specal_die_str(dice_list[0])}"
199
+ second_die = "#{CHOICE_DIE_MARK if choice_index == 1}#{get_specal_die_str(dice_list[1])}"
200
+
201
+ feat_result_text = "[#{first_die}, #{second_die}]"
202
+ else
203
+ feat_result_text = "[#{get_specal_die_str(choice_die_number)}]"
204
+ end
205
+
206
+ return feat_result_text, choice_die_number, feat_dice_count
207
+ end
208
+
209
+ # 有利/不利を含めて判定ダイスの結果を取得
210
+ def die_choice(dice_list, favoured_state_value)
211
+ if favoured_state_value == FavouredState::ILLFAVOURED
212
+ if dice_list.count(SAURONS_EYE_NUMBER) >= 1
213
+ return SAURONS_EYE_NUMBER
214
+ else
215
+ return dice_list.min
216
+ end
217
+ elsif favoured_state_value == FavouredState::FAVOURED
218
+ if dice_list.count(GANDALF_RUNE_NUMBER) >= 1
219
+ return GANDALF_RUNE_NUMBER
220
+ else
221
+ # ガンダルフ・ルーンが無ければサウロンの目を除いた最大値を選ぶ
222
+ # ※ どちらもサウロンの目ならサウロンの目を返す
223
+ return dice_list.count(SAURONS_EYE_NUMBER) == 2 ? SAURONS_EYE_NUMBER : dice_list.reject { |x| x == SAURONS_EYE_NUMBER }.max
224
+ end
225
+ end
226
+ return dice_list[0]
227
+ end
228
+
229
+ # 判定ダイスが特殊ダイス目だった場合、該当文字列に変換する
230
+ def get_specal_die_str(die_number)
231
+ if die_number == GANDALF_RUNE_NUMBER
232
+ return "ガンダルフ・ルーン"
233
+ elsif die_number == SAURONS_EYE_NUMBER
234
+ return "サウロンの目"
235
+ end
236
+
237
+ return die_number
238
+ end
239
+
240
+ # ================ FDコマンド ================#
241
+
242
+ FD_ADJUST_NUMBER_INDEX = 2
243
+ FD_OPTION_START_INDEX = 3
244
+
245
+ # FDコマンド実行
246
+ def fd_command_exec(command)
247
+ m = /\A(FD)(-?\d*)?([FI]-?\d*)?([FI]-?\d*)?$/.match(command)
248
+ unless m
249
+ return ''
250
+ end
251
+
252
+ # コマンドパラメータ取得
253
+ adjust_number, opts = get_fd_params(m)
254
+
255
+ # 判定ダイス処理
256
+ feat_result_text, feat_die_number, feat_dice_count = make_featdice_roll(opts.favoured_state)
257
+ result_header_text = "(#{feat_dice_count}D12"
258
+
259
+ return get_fd_roll_result(result_header_text, feat_result_text, feat_die_number, feat_dice_count, adjust_number)
260
+ end
261
+
262
+ # FDコマンド判定結果の取得
263
+ def get_fd_roll_result(result_header_text, feat_result_text, feat_die_number, feat_dice_count, adjust_number)
264
+ # 修正値処理
265
+ reslt_die_number, adjust_number_text = get_fd_adjust(feat_die_number, adjust_number)
266
+
267
+ result_header_text = "#{result_header_text}#{adjust_number_text}) > #{feat_result_text}#{adjust_number_text}"
268
+ if adjust_number != 0 || feat_dice_count != 1
269
+ return "#{result_header_text} > [#{get_specal_die_str(reslt_die_number)}]"
270
+ end
271
+
272
+ return result_header_text
273
+ end
274
+
275
+ # FDコマンドの修正値取得
276
+ def get_fd_adjust(feat_die_number, adjust_number)
277
+ if [SAURONS_EYE_NUMBER, GANDALF_RUNE_NUMBER].include?(feat_die_number)
278
+ return feat_die_number, get_adjust_number_text(adjust_number)
279
+ end
280
+
281
+ res_total_num = feat_die_number + adjust_number
282
+ if res_total_num > 10
283
+ res_total_num = 10
284
+ elsif res_total_num < 1
285
+ res_total_num = 1
286
+ end
287
+ return res_total_num, get_adjust_number_text(adjust_number)
288
+ end
289
+
290
+ # FDコマンドパラメータの取得
291
+ def get_fd_params(m)
292
+ adjust_number = m[FD_ADJUST_NUMBER_INDEX].to_i
293
+
294
+ opt_params = m[FD_OPTION_START_INDEX..m.length].compact
295
+
296
+ return adjust_number, get_options(opt_params)
297
+ end
298
+
299
+ # ================ RGコマンド ================#
300
+
301
+ RG_DIFFICULTY_INDEX = 1
302
+ RG_SUCCESS_DICE_INDEX = 3
303
+ RG_PIERCING_BLOWS_INDEX = 5
304
+ RG_ADJUST_NUMBER_INDEX = 7
305
+ RG_OPTION_START_INDEX = 8
306
+
307
+ # RGコマンド実行
308
+ def rg_command_exec(command)
309
+ m = /\A(\d+)(RG)(\d*)(@(\d{0,2}))?(A(-?\d*))?([WFIM]-?\d*)?([WFIM]-?\d*)?([WFIM]-?\d*)?([WFIM]-?\d*)?$/.match(command)
310
+ unless m
311
+ return ''
312
+ end
313
+
314
+ success_count = 0
315
+
316
+ # コマンドパラメータ取得
317
+ difficulty, success_dice_count, piercing_blows_number, adjust_number, opts = get_rg_params(m)
318
+
319
+ # 判定ダイス処理
320
+ feat_result_text, feat_die_number, feat_dice_count = make_featdice_roll(opts.favoured_state)
321
+ total_dice_number = (feat_die_number != SAURONS_EYE_NUMBER ? feat_die_number : 0)
322
+
323
+ result_header_text = "(#{feat_dice_count}D12"
324
+ result_dice_text = feat_result_text
325
+
326
+ # 技量ダイスが指定されているならその処理
327
+ if success_dice_count > 0
328
+ success_result_text, success_total_number, success_count = make_successdice_roll(success_dice_count, opts.weary)
329
+ total_dice_number += success_total_number
330
+
331
+ result_header_text = "#{result_header_text}+#{success_dice_count}D6"
332
+ result_dice_text = "#{result_dice_text}+#{success_result_text}"
333
+ end
334
+
335
+ # 修正値処理
336
+ total_dice_number, adjust_number_text = get_rg_adjust(total_dice_number, adjust_number)
337
+
338
+ result_header_text = "#{result_header_text}#{adjust_number_text}) > #{result_dice_text}#{adjust_number_text}"
339
+
340
+ return get_rg_roll_result(result_header_text, difficulty, feat_die_number, piercing_blows_number, total_dice_number, success_count, opts)
341
+ end
342
+
343
+ # 修正値と修正値テキストの取得
344
+ def get_rg_adjust(total_dice_number, adjust_number)
345
+ total_dice_number += adjust_number
346
+ total_dice_number = 0 if total_dice_number < 0
347
+
348
+ return total_dice_number, get_adjust_number_text(adjust_number)
349
+ end
350
+
351
+ # RGコマンド判定結果の取得
352
+ def get_rg_roll_result(result_header_text, difficulty, feat_die_number, piercing_blows_number, total_dice_number, success_count, opts)
353
+ # 状態表示取得
354
+ condition_text = get_condition_text(opts)
355
+
356
+ success_count_text = "成功度 #{success_count}"
357
+
358
+ # 痛打判定をブロック化
359
+ piercing_blows = lambda() { |feat_die_num, piercing_blows_num, res_text, cond_text|
360
+ if piercing_blows_num > 0 && feat_die_num >= piercing_blows_num && feat_die_num != SAURONS_EYE_NUMBER
361
+ res_text = "#{res_text} 痛打発生!"
362
+ end
363
+ return "#{res_text}#{cond_text}"
364
+ }
365
+
366
+ if feat_die_number == GANDALF_RUNE_NUMBER
367
+ gandalf_rune_text = "#{result_header_text}:自動成功[#{success_count_text}]"
368
+ gandalf_rune_text = piercing_blows.call(feat_die_number, piercing_blows_number, gandalf_rune_text, condition_text)
369
+ if success_count >= 2
370
+ return Result.critical(gandalf_rune_text)
371
+ end
372
+
373
+ return Result.success(gandalf_rune_text)
374
+ elsif opts.miserable && feat_die_number == SAURONS_EYE_NUMBER
375
+ return Result.failure("#{result_header_text}:自動失敗#{condition_text}")
376
+ end
377
+
378
+ result_detail_text = "難易度 #{difficulty} 達成値 #{total_dice_number}"
379
+ if difficulty > total_dice_number
380
+ return Result.failure("#{result_header_text} #{result_detail_text}:失敗#{condition_text}")
381
+ end
382
+
383
+ success_text = "#{result_header_text} #{result_detail_text}:成功[#{success_count_text}]"
384
+ success_text = piercing_blows.call(feat_die_number, piercing_blows_number, success_text, condition_text)
385
+ if success_count >= 2
386
+ return Result.critical(success_text)
387
+ end
388
+
389
+ return Result.success(success_text)
390
+ end
391
+
392
+ # RGコマンドパラメータの取得
393
+ def get_rg_params(m)
394
+ difficulty = m[RG_DIFFICULTY_INDEX].to_i
395
+
396
+ success_dice_count = m[RG_SUCCESS_DICE_INDEX].to_i
397
+ adjust_number = m[RG_ADJUST_NUMBER_INDEX].to_i
398
+ piercing_blows_number = m[RG_PIERCING_BLOWS_INDEX]&.to_i || -1 # -1は痛打判定自体を行わない
399
+
400
+ opt_params = m[RG_OPTION_START_INDEX..m.length].compact
401
+
402
+ return difficulty, success_dice_count, piercing_blows_number, adjust_number, get_options(opt_params)
403
+ end
404
+ end
405
+ end
406
+ end
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class TheUnofficialHollowKnightRPG < Base
6
+ # ゲームシステムの識別子
7
+ ID = 'TheUnofficialHollowKnightRPG'
8
+
9
+ # ゲームシステム名
10
+ NAME = 'The Unofficial Hollow Knight RPG'
11
+
12
+ # ゲームシステム名の読みがな
13
+ SORT_KEY = 'しあんおふいしやるほろうないとRPG'
14
+
15
+ # ダイスボットの使い方
16
+ HELP_MESSAGE = <<~INFO_MESSAGE_TEXT
17
+ ・能力値判定 [n]AD[+b][#r][>=t]
18
+  n: 能力値。小数可。省略不可。
19
+  b: ボーナス、ペナルティダイス。省略可。
20
+  r: 追加リロールダイス数。省略可。
21
+  t: 目標値。>=含めて省略可。
22
+  成功数を判定。
23
+  例)1AD, 2.5AD, 1.5AD+1, 2AD#1, 2.5AD+2#2>=4
24
+
25
+ ・イニシアチブ [n]INTI[+b][#r]
26
+  n: イニシアチブに使う能力値。省略不可。
27
+ b: ボーナス、ペナルティダイス。省略可。
28
+ r: 追加リロールダイス数。省略可。
29
+  振り直しを行ったうえでイニシアチブ値を計算。
30
+  例)1INTI, 2.5INTI, 1.5INTI+1, 2INTI#1, 2.5INTI+2#2
31
+ INFO_MESSAGE_TEXT
32
+
33
+ register_prefix('(\d+\.?\d*)?AD([+-](\d+))?(#(\d+))?(>=(\d+))?', '(\d+\.?\d*)?(INTI|inti)([+-](\d+))?(#(\d+))?')
34
+
35
+ def initialize(command)
36
+ super(command)
37
+
38
+ @sort_barabara_dice = false # バラバラロール(Bコマンド)でソート無
39
+ end
40
+
41
+ def eval_game_system_specific_command(command)
42
+ ability_roll(command) || initiative_roll(command)
43
+ end
44
+
45
+ def number_with_sign_from_int(number)
46
+ if number == 0
47
+ return ""
48
+ elsif number > 0
49
+ return "+#{number.abs}"
50
+ elsif number < 0
51
+ return "-#{number.abs}"
52
+ else
53
+ return number.to_s
54
+ end
55
+ end
56
+
57
+ def number_with_reroll_from_int(number)
58
+ if number == 0
59
+ return ""
60
+ elsif number > 0
61
+ return "\##{number}"
62
+ else
63
+ return number.to_s
64
+ end
65
+ end
66
+
67
+ # 能力値ロール
68
+ def ability_roll(command)
69
+ m = /^(\d+\.?\d*)?AD([+-](\d+))?(#(\d*))?(>=(\d+))?/.match(command)
70
+ unless m
71
+ return nil
72
+ end
73
+
74
+ num_of_die = m[1].to_f
75
+ bonus = m[3].to_i
76
+ reroll = m[5].to_i
77
+ difficulty = m[7].to_i
78
+
79
+ if /\.[1-9]+/ =~ num_of_die.to_s
80
+ dice_command = "#{num_of_die}AD#{number_with_sign_from_int(bonus)}#{number_with_reroll_from_int(reroll)}"
81
+ reroll += 1
82
+ else
83
+ dice_command = "#{num_of_die.to_i}AD#{number_with_sign_from_int(bonus)}#{number_with_reroll_from_int(reroll)}"
84
+ end
85
+
86
+ if difficulty == 0
87
+ difficulty = 5
88
+ else
89
+ dice_command += ">=#{difficulty}"
90
+ end
91
+
92
+ # 振られたダイスを入れる
93
+ values = @randomizer.roll_barabara(num_of_die.to_i + bonus, 6)
94
+ # 成功数
95
+ result = values.count { |num| num >= difficulty }
96
+ failed_roll = num_of_die.to_i - result
97
+
98
+ # ロールの結果の文字列
99
+ rolled_text = "[" + values.join(",") + "]"
100
+
101
+ reroll_values = []
102
+
103
+ if reroll == 1
104
+ reroll_values.push(@randomizer.roll_once(6))
105
+ elsif reroll > 1
106
+ reroll_values += @randomizer.roll_barabara(reroll, 6)
107
+ end
108
+
109
+ reroll_result = reroll_values.count { |num| num >= difficulty }
110
+ if failed_roll < reroll_result
111
+ reroll_result = failed_roll
112
+ end
113
+ result += reroll_result
114
+
115
+ # リロールの結果の文字列をロールの結果の文字列に追加する
116
+ unless reroll_values.empty?
117
+ rolled_text += " Reroll [" + reroll_values.join(",") + "]"
118
+ end
119
+
120
+ # 結果
121
+ return "(#{dice_command}) > #{rolled_text} > #{result}成功"
122
+ end
123
+
124
+ # イニシアチブロール
125
+ def initiative_roll(command)
126
+ m = /^(\d+\.?\d*)?(INTI|inti)([+-](\d+))?(#(\d+))?/.match(command)
127
+ unless m
128
+ return nil
129
+ end
130
+
131
+ grace = m[1].to_f
132
+ bonus = m[3].to_i
133
+ reroll = m[6].to_i
134
+
135
+ if /\.[1-9]+/ =~ grace.to_s
136
+ dice_command = "(#{grace}INTI#{number_with_sign_from_int(bonus)}#{number_with_reroll_from_int(reroll)})"
137
+ reroll += 1
138
+ else
139
+ dice_command = "(#{grace.to_i}INTI#{number_with_sign_from_int(bonus)}#{number_with_reroll_from_int(reroll)})"
140
+ end
141
+
142
+ values = @randomizer.roll_barabara(grace + bonus, 6)
143
+
144
+ revalue = []
145
+ unless reroll == 0
146
+ revalue = @randomizer.roll_barabara(reroll, 6)
147
+ end
148
+ revalue = revalue.sort
149
+
150
+ result = 0
151
+
152
+ res_text = "["
153
+ values.each do |value|
154
+ if revalue.empty? # リロールがなければ
155
+ res_text += value.to_s
156
+ result += value
157
+ else # リロールがあったら
158
+ is_min = false
159
+ index = -1
160
+ revalue.each do |re|
161
+ index += 1
162
+ next unless re > value # リロールしたダイス最小値か
163
+
164
+ res_text += "#{value}<<#{re}"
165
+ result += re
166
+ revalue.delete_at(index)
167
+ is_min = true
168
+ break
169
+ end
170
+ unless is_min # 最小値でなかったら
171
+ res_text += value.to_s
172
+ result += value
173
+ end
174
+ end
175
+
176
+ res_text += ","
177
+ end
178
+ res_text = res_text.chop
179
+ res_text += "]"
180
+
181
+ return "#{dice_command} > #{res_text} > #{result}"
182
+ end
183
+ end
184
+ end
185
+ end
@@ -31,6 +31,11 @@ module BCDice
31
31
    mはポシビリティを使用する前のロール結果を入れて下さい。
32
32
    出目が10未満の場合は、10への読み替えが行われます。
33
33
    また、振り足しを自動で行い、20の出目が出たときには技能無し値も並記します。
34
+  ・CPOS
35
+   "CPOSm"で、コズムポシビリティ使用による1d20のロールを行います。
36
+   mはポシビリティを使用する前のロール結果を入れて下さい。
37
+   出目が10未満の場合でも、10への読み替えが行われません。
38
+   振り足しは自動で行い、20の出目が出たときには技能無し値も並記します。
34
39
  ・ボーナスダメージロール
35
40
   "xBD[+y]"でロールします。[]内は省略可能。
36
41
   xはダメージダイス数。yはダメージ基本値 or 式を入れて下さい。
@@ -42,13 +47,14 @@ module BCDice
42
47
   ・ダメージ結果表「DTx or DAMAGEx」
43
48
   ・ロールボーナス表「BTx+y or BONUSx+y or TOTALx+y」 xは数値, yは技能基本値
44
49
  INFO_MESSAGE_TEXT
45
- register_prefix('TE', 'UP', 'POS', '\d+BD', 'TG', 'RT', 'Result', 'DT', 'damage', 'BT', 'bonus', 'total', '1R20')
50
+ register_prefix('TE', 'UP', 'POS', 'CPOS', '\d+BD', 'TG', 'RT', 'Result', 'DT', 'damage', 'BT', 'bonus', 'total', '1R20')
46
51
 
47
52
  def eval_game_system_specific_command(command)
48
53
  torg_check(command) ||
49
54
  getRolld20DiceCommandResult(command) ||
50
55
  getUpRollDiceCommandResult(command) ||
51
56
  getPossibilityRollDiceCommandResult(command) ||
57
+ getCosmPossibilityRollDiceCommandResult(command) ||
52
58
  getBonusDamageDiceCommandResult(command) ||
53
59
  getSuccessLevelDiceCommandResult(command) ||
54
60
  getDamageResultDiceCommandResult(command) ||
@@ -75,7 +81,7 @@ module BCDice
75
81
  mod = m[1]
76
82
 
77
83
  debug(mod)
78
- mod = ArithmeticEvaluator.eval(mod) if mod
84
+ mod = mod ? Arithmetic.eval(mod, @round_type) : 0
79
85
  debug(mod)
80
86
  mod = mod.to_i
81
87
  skilled, unskilled, dice_str, = torg_eternity_dice(false, false)
@@ -191,7 +197,7 @@ module BCDice
191
197
  return nil
192
198
  end
193
199
 
194
- output_modifier = ArithmeticEvaluator.eval(m[1])
200
+ output_modifier = m[1] ? Arithmetic.eval(m[1], @round_type) : 0
195
201
  skilled, unskilled, dice_str, = torg_eternity_dice(true, false)
196
202
  subtotal_skilled = skilled + output_modifier
197
203
  subtotal_unskilled = unskilled + output_modifier
@@ -206,6 +212,29 @@ module BCDice
206
212
  return output
207
213
  end
208
214
 
215
+ # ロールコマンド (コズムポシビリティロール)
216
+ def getCosmPossibilityRollDiceCommandResult(command)
217
+ debug("Torg Eternity CosmPossibility Roll Command ? ", command)
218
+ m = /^CPOS((\d+)(\+\d+)?)$/i.match(command)
219
+ unless m
220
+ return nil
221
+ end
222
+
223
+ output_modifier = m[1] ? Arithmetic.eval(m[1], @round_type) : 0
224
+ skilled, unskilled, dice_str, = torg_eternity_dice(false, false)
225
+ subtotal_skilled = skilled + output_modifier
226
+ subtotal_unskilled = unskilled + output_modifier
227
+ value_skilled = format("%+d", get_torg_eternity_bonus(subtotal_skilled))
228
+ if subtotal_skilled != subtotal_unskilled
229
+ value_unskilled = format("%+d", get_torg_eternity_bonus(subtotal_unskilled))
230
+ output = "d20ロール(ポシビリティ) > #{output_modifier}+1d20[#{dice_str}] > #{value_skilled}[#{subtotal_skilled}](技能有) / #{value_unskilled}[#{subtotal_unskilled}](技能無)"
231
+ else
232
+ output = "d20ロール(ポシビリティ) > #{output_modifier}+1d20[#{dice_str}] > #{value_skilled}[#{subtotal_skilled}]"
233
+ end
234
+
235
+ return output
236
+ end
237
+
209
238
  # ダメージボーナスコマンド
210
239
  def getBonusDamageDiceCommandResult(command)
211
240
  debug("TorgEternity Bonus Damage Roll Command ? ", command)
@@ -234,7 +263,7 @@ module BCDice
234
263
  return nil
235
264
  end
236
265
 
237
- value = ArithmeticEvaluator.eval(m[2])
266
+ value = m[2] ? Arithmetic.eval(m[2], @round_type) : 0
238
267
  debug(value)
239
268
  if value < 0
240
269
  output = "Failure."
@@ -254,7 +283,7 @@ module BCDice
254
283
  return nil
255
284
  end
256
285
 
257
- value = ArithmeticEvaluator.eval(m[2])
286
+ value = m[2] ? Arithmetic.eval(m[2], @round_type) : 0
258
287
  debug(value)
259
288
  output = get_torg_eternity_damage_result(value)
260
289
  output = "ダメージ結果表[#{value}] > #{output}"
@@ -307,7 +336,7 @@ module BCDice
307
336
  value_modifier = 0
308
337
  output_modifier = ""
309
338
  else
310
- value_modifier = ArithmeticEvaluator.eval(string_modifier)
339
+ value_modifier = string_modifier ? Arithmetic.eval(string_modifier, @round_type) : 0
311
340
  output_modifier = format("%+d", value_modifier)
312
341
  end
313
342
  debug(value_modifier)