bcdice 3.7.0 → 3.8.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/i18n/Arianrhod/ja_jp.yml +3 -0
  4. data/i18n/Emoklore/ja_jp.yml +7 -0
  5. data/i18n/SwordWorld/ja_jp.yml +1 -0
  6. data/i18n/SwordWorld/zh_hans.yml +7 -0
  7. data/i18n/SwordWorld2_0/zh_hans.yml +37 -0
  8. data/i18n/SwordWorld2_5/ja_jp.yml +1 -1
  9. data/i18n/SwordWorld2_5/zh_hans.yml +57 -0
  10. data/lib/bcdice/arithmetic/parser.rb +1 -1
  11. data/lib/bcdice/command/parser.rb +1 -1
  12. data/lib/bcdice/common_command/add_dice/parser.rb +1 -1
  13. data/lib/bcdice/common_command/barabara_dice/parser.rb +1 -1
  14. data/lib/bcdice/common_command/calc/parser.rb +1 -1
  15. data/lib/bcdice/common_command/reroll_dice/parser.rb +1 -1
  16. data/lib/bcdice/common_command/tally_dice/parser.rb +1 -1
  17. data/lib/bcdice/common_command/upper_dice/parser.rb +1 -1
  18. data/lib/bcdice/game_system/AngelGear.rb +1 -1
  19. data/lib/bcdice/game_system/AnimaAnimus.rb +1 -1
  20. data/lib/bcdice/game_system/Arianrhod.rb +4 -4
  21. data/lib/bcdice/game_system/AssaultEngine.rb +3 -3
  22. data/lib/bcdice/game_system/BeastBindTrinity.rb +1 -1
  23. data/lib/bcdice/game_system/BeginningIdol.rb +2 -2
  24. data/lib/bcdice/game_system/BeginningIdol2022.rb +2 -2
  25. data/lib/bcdice/game_system/BladeOfArcana.rb +3 -3
  26. data/lib/bcdice/game_system/Bloodorium.rb +68 -0
  27. data/lib/bcdice/game_system/ConvictorDrive.rb +117 -0
  28. data/lib/bcdice/game_system/Cthulhu7th_ChineseTraditional.rb +1 -1
  29. data/lib/bcdice/game_system/Cthulhu7th_Korean.rb +1 -1
  30. data/lib/bcdice/game_system/CyberpunkRed.rb +2 -6
  31. data/lib/bcdice/game_system/DeadlineHeroes.rb +1 -1
  32. data/lib/bcdice/game_system/DungeonsAndDragons5.rb +196 -0
  33. data/lib/bcdice/game_system/EmbryoMachine.rb +4 -4
  34. data/lib/bcdice/game_system/Emoklore.rb +8 -8
  35. data/lib/bcdice/game_system/FutariSousa.rb +1 -0
  36. data/lib/bcdice/game_system/GURPS.rb +1 -1
  37. data/lib/bcdice/game_system/GehennaAn.rb +2 -2
  38. data/lib/bcdice/game_system/HunterTheReckoning5th.rb +152 -0
  39. data/lib/bcdice/game_system/LostRoyal.rb +1 -1
  40. data/lib/bcdice/game_system/NobunagasBlackCastle.rb +237 -0
  41. data/lib/bcdice/game_system/OracleEngine.rb +1 -1
  42. data/lib/bcdice/game_system/Raisondetre.rb +1 -1
  43. data/lib/bcdice/game_system/RokumonSekai2.rb +1 -1
  44. data/lib/bcdice/game_system/SajinsenkiAGuS.rb +154 -0
  45. data/lib/bcdice/game_system/ShinkuuGakuen.rb +1 -1
  46. data/lib/bcdice/game_system/ShinobiGami.rb +14 -1
  47. data/lib/bcdice/game_system/SkynautsBouken.rb +93 -2
  48. data/lib/bcdice/game_system/StellarLife.rb +1 -1
  49. data/lib/bcdice/game_system/StrangerOfSwordCity.rb +1 -1
  50. data/lib/bcdice/game_system/SwordWorld.rb +2 -2
  51. data/lib/bcdice/game_system/SwordWorld2_0_SimplifiedChinese.rb +84 -0
  52. data/lib/bcdice/game_system/SwordWorld2_5_SimplifiedChinese.rb +95 -0
  53. data/lib/bcdice/game_system/SwordWorld_SimplifiedChinese.rb +29 -0
  54. data/lib/bcdice/game_system/TherapieSein.rb +2 -2
  55. data/lib/bcdice/game_system/Torg.rb +1 -1
  56. data/lib/bcdice/game_system/TorgEternity.rb +54 -18
  57. data/lib/bcdice/game_system/VampireTheMasquerade5th.rb +26 -12
  58. data/lib/bcdice/game_system/Warhammer4.rb +4 -0
  59. data/lib/bcdice/game_system/WorldOfDarkness.rb +17 -9
  60. data/lib/bcdice/game_system/cthulhu7th/full_auto.rb +1 -1
  61. data/lib/bcdice/game_system/filled_with/tresure_tables.rb +1 -1
  62. data/lib/bcdice/game_system/sword_world/rating_parser.rb +1 -1
  63. data/lib/bcdice/game_system.rb +9 -0
  64. data/lib/bcdice/version.rb +1 -1
  65. metadata +17 -3
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class DungeonsAndDragons5 < Base
6
+ # ゲームシステムの識別子
7
+ ID = 'DungeonsAndDragons5'
8
+
9
+ # ゲームシステム名
10
+ NAME = 'ダンジョンズ&ドラゴンズ第5版'
11
+
12
+ # ゲームシステム名の読みがな
13
+ SORT_KEY = 'たんしよんすあんととらこんす5'
14
+
15
+ # ダイスボットの使い方
16
+ HELP_MESSAGE = <<~INFO_MESSAGE_TEXT
17
+ ・攻撃ロール AT[x][>=t][y]
18
+  x:+-修正。省略可。
19
+  y:有利(A), 不利(D)。省略可。
20
+  t:敵のアーマークラス。>=を含めて省略可。
21
+  ファンブル/失敗/成功/クリティカル を自動判定。
22
+  例)AT AT>=10 AT+5>=18 AT-3>=16 ATA AT>=10A AT+3>=18A AT-3>=16 ATD AT>=10D AT+5>=18D AT-5>=16D
23
+
24
+ ・能力値判定 AR[x][>=t][y]
25
+  攻撃ロールと同様。失敗/成功を自動判定。
26
+  例)AR AR>=10 AR+5>=18 AR-3>=16 ARA AR>=10A AR+3>=18A AR-3>=16 ARD AR>=10D AR+5>=18D AR-5>=16D
27
+
28
+ INFO_MESSAGE_TEXT
29
+
30
+ register_prefix('AT([+-]\d+)?(>=\d+)?[AD]?', 'AR([+-]\d+)?(>=\d+)?[AD]?')
31
+
32
+ def initialize(command)
33
+ super(command)
34
+
35
+ @sort_barabara_dice = false # バラバラロール(Bコマンド)でソート無
36
+ end
37
+
38
+ def eval_game_system_specific_command(command)
39
+ attack_roll(command) || ability_roll(command)
40
+ end
41
+
42
+ def number_with_sign_from_int(number)
43
+ if number == 0
44
+ return ""
45
+ elsif number > 0
46
+ return "+#{number}"
47
+ else
48
+ return number.to_s
49
+ end
50
+ end
51
+
52
+ def attack_roll(command)
53
+ m = /^AT([-+]\d+)?(>=(\d+))?([AD]?)/.match(command)
54
+ unless m
55
+ return nil
56
+ end
57
+
58
+ modify = m[1].to_i
59
+ difficulty = m[3].to_i
60
+ advantage = m[4]
61
+
62
+ usedie = 0
63
+ roll_die = ""
64
+
65
+ dice_command = "AT#{number_with_sign_from_int(modify)}"
66
+ if difficulty > 0
67
+ dice_command += ">=#{difficulty}"
68
+ end
69
+ unless advantage.empty?
70
+ dice_command += advantage.to_s
71
+ end
72
+
73
+ output = ["(#{dice_command})"]
74
+
75
+ if advantage.empty?
76
+ usedie = @randomizer.roll_once(20)
77
+ roll_die = usedie
78
+ else
79
+ dice = @randomizer.roll_barabara(2, 20)
80
+ roll_die = "[" + dice.join(",") + "]"
81
+ if advantage == "A"
82
+ usedie = dice.max
83
+ else
84
+ usedie = dice.min
85
+ end
86
+ end
87
+
88
+ if modify != 0
89
+ output.push("#{roll_die}#{number_with_sign_from_int(modify)}")
90
+ output.push((usedie + modify).to_s)
91
+ else
92
+ unless advantage.empty?
93
+ output.push(roll_die)
94
+ end
95
+ output.push(usedie.to_s)
96
+ end
97
+
98
+ result = Result.new
99
+ if usedie == 20
100
+ result.critical = true
101
+ result.success = true
102
+ output.push("クリティカル")
103
+ elsif usedie == 1
104
+ result.fumble = true
105
+ output.push("ファンブル")
106
+ elsif difficulty > 0
107
+ if usedie + modify >= difficulty
108
+ result.success = true
109
+ output.push("成功")
110
+ else
111
+ output.push("失敗")
112
+ end
113
+ end
114
+
115
+ Result.new.tap do |r|
116
+ r.text = output.join(" > ")
117
+ if result
118
+ if difficulty > 0 || result.critical? || result.fumble?
119
+ r.condition = result.success?
120
+ end
121
+ r.critical = result.critical?
122
+ r.fumble = result.fumble?
123
+ end
124
+ end
125
+ end
126
+
127
+ def ability_roll(command)
128
+ m = /^AR([-+]\d+)?(>=(\d+))?([AD]?)/.match(command)
129
+ unless m
130
+ return nil
131
+ end
132
+
133
+ modify = m[1].to_i
134
+ difficulty = m[3].to_i
135
+ advantage = m[4]
136
+
137
+ usedie = 0
138
+ roll_die = ""
139
+
140
+ dice_command = "AR#{number_with_sign_from_int(modify)}"
141
+ if difficulty > 0
142
+ dice_command += ">=#{difficulty}"
143
+ end
144
+ unless advantage.empty?
145
+ dice_command += advantage.to_s
146
+ end
147
+
148
+ output = ["(#{dice_command})"]
149
+
150
+ if advantage.empty?
151
+ usedie = @randomizer.roll_once(20)
152
+ roll_die = usedie
153
+ else
154
+ dice = @randomizer.roll_barabara(2, 20)
155
+ roll_die = "[" + dice.join(",") + "]"
156
+ if advantage == "A"
157
+ usedie = dice.max
158
+ else
159
+ usedie = dice.min
160
+ end
161
+ end
162
+
163
+ if modify != 0
164
+ output.push("#{roll_die}#{number_with_sign_from_int(modify)}")
165
+ output.push((usedie + modify).to_s)
166
+ else
167
+ unless advantage.empty?
168
+ output.push(roll_die)
169
+ end
170
+ output.push(usedie.to_s)
171
+ end
172
+
173
+ result = Result.new
174
+ if difficulty > 0
175
+ if usedie + modify >= difficulty
176
+ result.success = true
177
+ output.push("成功")
178
+ else
179
+ output.push("失敗")
180
+ end
181
+ end
182
+
183
+ Result.new.tap do |r|
184
+ r.text = output.join(" > ")
185
+ if result
186
+ if difficulty > 0
187
+ r.condition = result.success?
188
+ end
189
+ r.critical = result.critical?
190
+ r.fumble = result.fumble?
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
@@ -34,10 +34,10 @@ module BCDice
34
34
 
35
35
  def replace_text(string)
36
36
  string
37
- .gsub(/EM(\d+)([+\-][+\-\d]+)(@(\d+))(\#(\d+))/i) { "2R10#{Regexp.last_match(2)}>=#{Regexp.last_match(1)}[#{Regexp.last_match(4)},#{Regexp.last_match(6)}]" }
38
- .gsub(/EM(\d+)([+\-][+\-\d]+)(\#(\d+))/i) { "2R10#{Regexp.last_match(2)}>=#{Regexp.last_match(1)}[20,#{Regexp.last_match(4)}]" }
39
- .gsub(/EM(\d+)([+\-][+\-\d]+)(@(\d+))/i) { "2R10#{Regexp.last_match(2)}>=#{Regexp.last_match(1)}[#{Regexp.last_match(4)},2]" }
40
- .gsub(/EM(\d+)([+\-][+\-\d]+)/i) { "2R10#{Regexp.last_match(2)}>=#{Regexp.last_match(1)}[20,2]" }
37
+ .gsub(/EM(\d+)([+-][+\-\d]+)(@(\d+))(\#(\d+))/i) { "2R10#{Regexp.last_match(2)}>=#{Regexp.last_match(1)}[#{Regexp.last_match(4)},#{Regexp.last_match(6)}]" }
38
+ .gsub(/EM(\d+)([+-][+\-\d]+)(\#(\d+))/i) { "2R10#{Regexp.last_match(2)}>=#{Regexp.last_match(1)}[20,#{Regexp.last_match(4)}]" }
39
+ .gsub(/EM(\d+)([+-][+\-\d]+)(@(\d+))/i) { "2R10#{Regexp.last_match(2)}>=#{Regexp.last_match(1)}[#{Regexp.last_match(4)},2]" }
40
+ .gsub(/EM(\d+)([+-][+\-\d]+)/i) { "2R10#{Regexp.last_match(2)}>=#{Regexp.last_match(1)}[20,2]" }
41
41
  .gsub(/EM(\d+)(@(\d+))(\#(\d+))/i) { "2R10>=#{Regexp.last_match(1)}[#{Regexp.last_match(3)},#{Regexp.last_match(5)}]" }
42
42
  .gsub(/EM(\d+)(\#(\d+))/i) { "2R10>=#{Regexp.last_match(1)}[20,#{Regexp.last_match(3)}]" }
43
43
  .gsub(/EM(\d+)(@(\d+))/i) { "2R10>=#{Regexp.last_match(1)}[#{Regexp.last_match(3)},2]" }
@@ -67,7 +67,7 @@ module BCDice
67
67
  success_value = 2 * critical + success - fumble
68
68
  result = compare_result(success_value)
69
69
 
70
- result.text = "#{values} > #{success_value} > 成功数#{success_value} #{result.text}"
70
+ result.text = "#{values} > #{success_value} > #{translate('Emoklore.success_count', count: success_value)} #{result.text}"
71
71
  return result
72
72
  end
73
73
 
@@ -75,19 +75,19 @@ module BCDice
75
75
  # @return [Result]
76
76
  def compare_result(success)
77
77
  if success < 0
78
- Result.fumble("ファンブル!")
78
+ Result.fumble(translate("fumble"))
79
79
  elsif success == 0
80
- Result.failure("失敗!")
80
+ Result.failure(translate("failure"))
81
81
  elsif success == 1
82
- Result.success("成功!")
82
+ Result.success(translate("success"))
83
83
  elsif success == 2
84
- Result.critical("ダブル!")
84
+ Result.critical(translate("Emoklore.double"))
85
85
  elsif success == 3
86
- Result.critical("トリプル!")
86
+ Result.critical(translate("Emoklore.triple"))
87
87
  elsif success <= 9
88
- Result.critical("ミラクル!")
88
+ Result.critical(translate("Emoklore.miracle"))
89
89
  else
90
- Result.critical("カタストロフ!")
90
+ Result.critical(translate("Emoklore.catastrophe"))
91
91
  end
92
92
  end
93
93
 
@@ -38,6 +38,7 @@ module BCDice
38
38
   思わぬヒント EVH/実験をしてみよう EVX/ゲスト捜査 EVG
39
39
   ケイジ聞き込み捜査    EVQ/ケイジ大規模捜査      EVM/こっそり情報の受け渡し EVP
40
40
   同僚たちと一緒に捜査する EVO/頻染みの店シチュエーション EVF/ハードBデカアクション EVB
41
+  探偵を大人しくさせる捜査 EVL/伝統的捜査 EVZ/原始的捜査 EVR
41
42
  感情表
42
43
   感情表A/B   FLT66・FLT10
43
44
   気に入っているところ  FLTL66 /気に入らないところ  FLTD66
@@ -75,7 +75,7 @@ module BCDice
75
75
  end
76
76
 
77
77
  def roll_3d6(command)
78
- m = /^([\d+\-]+)-3D6?([\d+\-]*)$/.match(command)
78
+ m = /^([\d+-]+)-3D6?([\d+-]*)$/.match(command)
79
79
  return nil unless m
80
80
 
81
81
  target_number = ArithmeticEvaluator.eval(m[1])
@@ -35,9 +35,9 @@ module BCDice
35
35
 
36
36
  def replace_text(string)
37
37
  string
38
- .gsub(/(\d+)GA(\d+)([+\-][+\-\d]+)/) { "#{Regexp.last_match(1)}R6#{Regexp.last_match(3)}>=#{Regexp.last_match(2)}[1]" }
38
+ .gsub(/(\d+)GA(\d+)([+-][+\-\d]+)/) { "#{Regexp.last_match(1)}R6#{Regexp.last_match(3)}>=#{Regexp.last_match(2)}[1]" }
39
39
  .gsub(/(\d+)GA(\d+)/) { "#{Regexp.last_match(1)}R6>=#{Regexp.last_match(2)}[1]" }
40
- .gsub(/(\d+)G(\d+)([+\-][+\-\d]+)/) { "#{Regexp.last_match(1)}R6#{Regexp.last_match(3)}>=#{Regexp.last_match(2)}[0]" }
40
+ .gsub(/(\d+)G(\d+)([+-][+\-\d]+)/) { "#{Regexp.last_match(1)}R6#{Regexp.last_match(3)}>=#{Regexp.last_match(2)}[0]" }
41
41
  .gsub(/(\d+)G(\d+)/) { "#{Regexp.last_match(1)}R6>=#{Regexp.last_match(2)}[0]" }
42
42
  end
43
43
 
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class HunterTheReckoning5th < Base
6
+ # ゲームシステムの識別子
7
+ ID = 'HunterTheReckoning5th'
8
+
9
+ # ゲームシステム名
10
+ NAME = 'Hunter: The Reckoning 5th Edition'
11
+
12
+ # ゲームシステム名の読みがな
13
+ SORT_KEY = 'はんあたされこにんく5'
14
+
15
+ # ダイスボットの使い方
16
+ HELP_MESSAGE = <<~MESSAGETEXT
17
+ ・判定コマンド(nHRFx+x)
18
+ 注意:難易度は必要成功数を表す
19
+
20
+ 難易度指定:成功数のカウント、判定成功と失敗、Critical処理、Critical Winのチェックを行う
21
+ (Desperationダイスがある場合)OverreachとDespairの発生チェックを行う
22
+ 例) (難易度)HRF(通常ダイス)+(Desperationダイス)
23
+ (難易度)HRF(通常ダイス)
24
+
25
+ 難易度省略:成功数のカウント、判定失敗、Critical処理、(Desperationダイスがある場合)Despairチェックを行う
26
+ 判定成功、Overreachのチェックを行わない
27
+ Critical Win、(Desperationダイスがある場合)Despair、Overreachのヒントを出力
28
+ 例) HRF(通常ダイス)+(Desperationダイス)
29
+ HRF(通常ダイス)
30
+
31
+ 難易度0指定:全てのチェックを行わない
32
+ 例) 0HRF(通常ダイス)+(Desperationダイス)
33
+ 0HRF(通常ダイス)
34
+
35
+ MESSAGETEXT
36
+
37
+ DIFFICULTY_INDEX = 1
38
+ DICE_POOL_INDEX = 3
39
+ DESPERATION_DICE_INDEX = 5
40
+
41
+ # 難易度に指定可能な特殊値
42
+ NOT_CHECK_SUCCESS = -1 # 判定成功にかかわるチェックを行わない(判定失敗に関わるチェックは行う)
43
+
44
+ # ダイスボットで使用するコマンドを配列で列挙する
45
+ register_prefix('\d*HRF')
46
+
47
+ def eval_game_system_specific_command(command)
48
+ m = /\A(\d+)?(HRF)(\d+)(\+(\d+))?$/.match(command)
49
+ unless m
50
+ return ''
51
+ end
52
+
53
+ dice_pool = m[DICE_POOL_INDEX].to_i
54
+ dice_text, success_dice, ten_dice, = make_dice_roll(dice_pool)
55
+ result_text = "(#{dice_pool}D10"
56
+
57
+ desperaton_dice_pool = m[DESPERATION_DICE_INDEX]&.to_i
58
+ if desperaton_dice_pool
59
+ desperaton_dice_text, desperaton_success_dice, desperaton_ten_dice, desperaton_botch_dice = make_dice_roll(desperaton_dice_pool)
60
+
61
+ ten_dice += desperaton_ten_dice
62
+ success_dice += desperaton_success_dice
63
+
64
+ result_text = "#{result_text}+#{desperaton_dice_pool}D10) > [#{dice_text}]+[#{desperaton_dice_text}] "
65
+ else
66
+ desperaton_ten_dice = 0
67
+ desperaton_botch_dice = 0
68
+ result_text = "#{result_text}) > [#{dice_text}] "
69
+ end
70
+
71
+ success_dice += get_critical_success(ten_dice)
72
+
73
+ difficulty = m[DIFFICULTY_INDEX] ? m[DIFFICULTY_INDEX].to_i : NOT_CHECK_SUCCESS
74
+
75
+ return get_roll_result(result_text, success_dice, ten_dice, desperaton_ten_dice, desperaton_botch_dice, difficulty)
76
+ end
77
+
78
+ private
79
+
80
+ def get_roll_result(result_text, success_dice, ten_dice, _desperaton_ten_dice, desperaton_botch_dice, difficulty)
81
+ result_text = "#{result_text} 成功数=#{success_dice}"
82
+ is_critical = ten_dice >= 2
83
+ desperation_result = ""
84
+
85
+ if difficulty > 0
86
+ result_text = "#{result_text} 難易度=#{difficulty}"
87
+
88
+ if success_dice >= difficulty
89
+ result_text = "#{result_text} 差分=#{success_dice - difficulty}"
90
+
91
+ if desperaton_botch_dice > 0
92
+ desperation_result = " [Overreach or Despair?]"
93
+ end
94
+
95
+ if is_critical
96
+ return Result.critical("#{result_text}:判定成功! [Critical Win]#{desperation_result}")
97
+ else
98
+ return Result.success("#{result_text}:判定成功!#{desperation_result}")
99
+ end
100
+
101
+ else
102
+ if desperaton_botch_dice > 0
103
+ return Result.fumble("#{result_text}:判定失敗! [Despair]")
104
+ end
105
+
106
+ return Result.failure("#{result_text}:判定失敗!")
107
+ end
108
+ elsif difficulty < 0
109
+ if success_dice == 0
110
+ if desperaton_botch_dice > 0
111
+ return Result.fumble("#{result_text}:判定失敗! [Despair]")
112
+ end
113
+
114
+ return Result.failure("#{result_text}:判定失敗!")
115
+ else
116
+ if desperaton_botch_dice > 0
117
+ result_text = "#{result_text}\n 判定失敗なら [Despair]"
118
+ desperation_result = " [Overreach or Despair?]"
119
+ end
120
+
121
+ if is_critical
122
+ result_text = "#{result_text}\n 判定成功なら [Critical Win]"
123
+ elsif desperaton_botch_dice > 0
124
+ result_text = "#{result_text}\n 判定成功なら"
125
+ end
126
+
127
+ return "#{result_text}#{desperation_result}"
128
+ end
129
+ end
130
+
131
+ # 難易度0指定(=全ての判定チェックを行わない)
132
+ return result_text.to_s
133
+ end
134
+
135
+ def get_critical_success(ten_dice)
136
+ # 10の目が2個毎に追加2成功
137
+ return ((ten_dice / 2) * 2)
138
+ end
139
+
140
+ def make_dice_roll(dice_pool)
141
+ dice_list = @randomizer.roll_barabara(dice_pool, 10)
142
+
143
+ dice_text = dice_list.join(',')
144
+ success_dice = dice_list.count { |x| x >= 6 }
145
+ ten_dice = dice_list.count(10)
146
+ botch_dice = dice_list.count(1)
147
+
148
+ return dice_text, success_dice, ten_dice, botch_dice
149
+ end
150
+ end
151
+ end
152
+ end
@@ -64,7 +64,7 @@ module BCDice
64
64
  def check_lostroyal(checking_table)
65
65
  keys = []
66
66
 
67
- (0...3).each do |_i|
67
+ 3.times do |_i|
68
68
  key = @randomizer.roll_once(6)
69
69
  keys << key
70
70
  end
@@ -0,0 +1,237 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bcdice/dice_table/table'
4
+
5
+ module BCDice
6
+ module GameSystem
7
+ class NobunagasBlackCastle < Base
8
+ # ゲームシステムの識別子
9
+ ID = 'NobunagasBlackCastle'
10
+
11
+ # ゲームシステム名
12
+ NAME = '信長の黒い城'
13
+
14
+ # ゲームシステム名の読みがな
15
+ SORT_KEY = 'のふなかのくろいしろ'
16
+
17
+ # ダイスボットの使い方
18
+ HELP_MESSAGE = <<~INFO_MESSAGETEXT
19
+ ■判定 sDRt s: 能力値 t:目標値
20
+
21
+ 例)+3DR12: 能力値+3、DR12で1d20を振って、その結果を表示(クリティカル・ファンブルも表示)
22
+
23
+ ■イニシアティヴ INS
24
+
25
+ 例)INS: 1d6を振って、イニシアティヴの結果を表示(PC先行を成功として表示)
26
+
27
+ ■NPC能力値作成 NPCST
28
+
29
+ 例)NPCST: 3d6を4回振って、各能力値とHPを表示
30
+
31
+
32
+ ■各種表
33
+
34
+ ・遭遇反応表(ERT)
35
+ ・武器表(SWT)/その他の奇妙な武器表(OSWT)
36
+ ・鎧表(ART)
37
+
38
+ INFO_MESSAGETEXT
39
+
40
+ def initialize(command)
41
+ super(command)
42
+
43
+ @sort_add_dice = true
44
+ @d66_sort_type = D66SortType::NO_SORT
45
+ end
46
+
47
+ def eval_game_system_specific_command(command)
48
+ resolute_action(command) || resolute_initiative(command) || roll_tables(command, TABLES) || make_npc_status(command)
49
+ end
50
+
51
+ private
52
+
53
+ def result_dr(total, dice_total, target)
54
+ if dice_total <= 1
55
+ Result.fumble("ファンブル")
56
+ elsif dice_total >= 20
57
+ Result.critical("クリティカル")
58
+ elsif total >= target
59
+ Result.success("成功")
60
+ else
61
+ Result.failure("失敗")
62
+ end
63
+ end
64
+
65
+ # DR判定
66
+ # @param [String] command
67
+ # @return [Result]
68
+ def resolute_action(command)
69
+ m = /^([+-]?\d*)DR(\d+)$/.match(command)
70
+ return nil unless m
71
+
72
+ num_status = m[1].to_i
73
+ num_target = m[2].to_i
74
+
75
+ total = @randomizer.roll_once(20)
76
+ total_status = total.to_s + with_symbol(num_status)
77
+ result = result_dr(total + num_status, total, num_target)
78
+
79
+ sequence = [
80
+ "(#{command})",
81
+ total_status,
82
+ total + num_status,
83
+ result.text,
84
+ ]
85
+
86
+ result.text = sequence.join(" > ")
87
+ return result
88
+ end
89
+
90
+ def with_symbol(number)
91
+ if number == 0
92
+ return "+0"
93
+ elsif number > 0
94
+ return "+#{number}"
95
+ else
96
+ return number.to_s
97
+ end
98
+ end
99
+
100
+ # イニシアティヴ判定
101
+ # @param [String] command
102
+ # @return [Result]
103
+ def resolute_initiative(command)
104
+ unless command == "INS"
105
+ return nil
106
+ end
107
+
108
+ total = @randomizer.roll_once(6)
109
+ result =
110
+ if total >= 4
111
+ Result.success("PC先行")
112
+ else
113
+ Result.failure("敵先行")
114
+ end
115
+
116
+ result.text = "(#{command}) > #{total} > #{result.text}"
117
+ return result
118
+ end
119
+
120
+ # NPC能力値作成
121
+ # @param [String] command
122
+ # @return [String]
123
+ def make_npc_status(command)
124
+ unless command == "NPCST"
125
+ return nil
126
+ end
127
+
128
+ pre = @randomizer.roll_sum(3, 6)
129
+ agi = @randomizer.roll_sum(3, 6)
130
+ str = @randomizer.roll_sum(3, 6)
131
+ tgh = @randomizer.roll_sum(3, 6)
132
+ hpd = @randomizer.roll_once(8)
133
+ hp = hpd + calc_status(tgh)
134
+ hp = 1 if hp < 1
135
+
136
+ text = [
137
+ "心#{with_symbol(calc_status(pre))}(#{pre})",
138
+ "技#{with_symbol(calc_status(agi))}(#{agi})",
139
+ "体#{with_symbol(calc_status(str))}(#{str})",
140
+ "耐久#{with_symbol(calc_status(tgh))}(#{tgh})",
141
+ "HP#{hp}(#{hpd})",
142
+ ].join(", ")
143
+
144
+ return "(#{command}) > #{text}"
145
+ end
146
+
147
+ def calc_status(st)
148
+ if st <= 4
149
+ return -3
150
+ elsif st <= 6
151
+ return -2
152
+ elsif st <= 8
153
+ return -1
154
+ elsif st <= 12
155
+ return 0
156
+ elsif st <= 14
157
+ return 1
158
+ elsif st <= 16
159
+ return 2
160
+ elsif st <= 20
161
+ return 3
162
+ end
163
+ end
164
+
165
+ # 各種表
166
+
167
+ TABLES = {
168
+ 'OSWT' => DiceTable::Table.new(
169
+ 'その他の奇妙な武器表',
170
+ '1D10',
171
+ [
172
+ '六尺棒(D4)',
173
+ '手槍(D4)',
174
+ '弓矢(D6)',
175
+ '鉄扇(D4)',
176
+ '大鉞(D8)',
177
+ '吹き矢(D2)+感染',
178
+ '鞭(D3)',
179
+ '熊手(D4)',
180
+ '石つぶて(D3)',
181
+ '丸太(D4)',
182
+ ]
183
+ ),
184
+ 'SWT' => DiceTable::Table.new(
185
+ '武器表',
186
+ '1D12',
187
+ [
188
+ '尖らせた骨の杭(D3)',
189
+ '竹槍(D4)',
190
+ '百姓から奪った鍬(D4)',
191
+ '脇差し(D4)',
192
+ '手裏剣 D6本(D4)',
193
+ '刀(D6)',
194
+ '鎖鎌(D6)',
195
+ '太刀(D8)',
196
+ '種子島銃(2D6) 弾丸(心+5)発',
197
+ '大槍(D8)',
198
+ '爆裂弾(D4) 心+3発',
199
+ '斬馬刀(D10)',
200
+ ]
201
+ ),
202
+ 'ART' => DiceTable::Table.new(
203
+ '鎧表',
204
+ '1D6',
205
+ [
206
+ '防具は、何もない',
207
+ '防具は、何もない',
208
+ '部分鎧(腹巻き) -D2ダメージ',
209
+ 'お貸し具足 -D3ダメージ',
210
+ '武者鎧 -D4ダメージ',
211
+ '大鎧 -D6ダメージ',
212
+ ]
213
+ ),
214
+ # 無理に高度なことをしなくても、表は展開して実装しても動く
215
+ 'ERT' => DiceTable::Table.new(
216
+ '遭遇反応表',
217
+ '2D6',
218
+ [
219
+ 'お前ら、殺す!',
220
+ 'お前ら、殺す!',
221
+ '憎悪の視線で睨んでくる。すきを見せれば、攻撃してくる。',
222
+ '憎悪の視線で睨んでくる。すきを見せれば、攻撃してくる。',
223
+ '憎悪の視線で睨んでくる。すきを見せれば、攻撃してくる。',
224
+ '警戒はしているが、特に、戦闘は望んでいない。怒らせなければ、自分たちの目的に沿って動く。',
225
+ '警戒はしているが、特に、戦闘は望んでいない。怒らせなければ、自分たちの目的に沿って動く。',
226
+ '中立。何かを与えたり、取引の材料を提示したりできれば、交渉できそうだ。',
227
+ '中立。何かを与えたり、取引の材料を提示したりできれば、交渉できそうだ。',
228
+ '好意的に会話できそうだ。向こうも取引したがっている。',
229
+ '好意的に会話できそうだ。向こうも取引したがっている。',
230
+ ]
231
+ ),
232
+ }.freeze
233
+
234
+ register_prefix('[+-]?\d*DR[\d]+', 'INS', 'NPCST', TABLES.keys)
235
+ end
236
+ end
237
+ end
@@ -46,7 +46,7 @@ MESSAGETEXT
46
46
  case command
47
47
  when /\d+CL.*/i
48
48
  clutch_roll(command)
49
- when /\d+D6.*\$[+\-]?\d.*/
49
+ when /\d+D6.*\$[+-]?\d.*/
50
50
  damage_roll(command)
51
51
  when /\d+R6/
52
52
  r_roll(command)