bcdice 3.7.0 → 3.8.0

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