bcdice 3.6.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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -0
  3. data/i18n/Arianrhod/ja_jp.yml +3 -0
  4. data/i18n/Emoklore/ja_jp.yml +7 -0
  5. data/i18n/FutariSousa/ja_jp.yml +431 -0
  6. data/i18n/LogHorizon/ja_jp.yml +1 -1
  7. data/i18n/SwordWorld/ja_jp.yml +1 -0
  8. data/i18n/SwordWorld/zh_hans.yml +7 -0
  9. data/i18n/SwordWorld2_0/zh_hans.yml +37 -0
  10. data/i18n/SwordWorld2_5/ja_jp.yml +1 -1
  11. data/i18n/SwordWorld2_5/zh_hans.yml +57 -0
  12. data/lib/bcdice/arithmetic/parser.rb +1 -1
  13. data/lib/bcdice/command/parser.rb +1 -1
  14. data/lib/bcdice/common_command/add_dice/parser.rb +106 -74
  15. data/lib/bcdice/common_command/add_dice.rb +1 -1
  16. data/lib/bcdice/common_command/barabara_dice/parser.rb +1 -1
  17. data/lib/bcdice/common_command/calc/parser.rb +1 -1
  18. data/lib/bcdice/common_command/d66_dice.rb +1 -1
  19. data/lib/bcdice/common_command/reroll_dice/parser.rb +1 -1
  20. data/lib/bcdice/common_command/tally_dice/parser.rb +1 -1
  21. data/lib/bcdice/common_command/upper_dice/parser.rb +1 -1
  22. data/lib/bcdice/game_system/AngelGear.rb +1 -1
  23. data/lib/bcdice/game_system/AnimaAnimus.rb +1 -1
  24. data/lib/bcdice/game_system/Arianrhod.rb +4 -4
  25. data/lib/bcdice/game_system/AssaultEngine.rb +3 -3
  26. data/lib/bcdice/game_system/Ayabito.rb +170 -4
  27. data/lib/bcdice/game_system/BeastBindTrinity.rb +1 -1
  28. data/lib/bcdice/game_system/BeginningIdol.rb +4 -2
  29. data/lib/bcdice/game_system/BeginningIdol2022.rb +184 -0
  30. data/lib/bcdice/game_system/BladeOfArcana.rb +3 -3
  31. data/lib/bcdice/game_system/Bloodorium.rb +68 -0
  32. data/lib/bcdice/game_system/ConvictorDrive.rb +117 -0
  33. data/lib/bcdice/game_system/Cthulhu7th_ChineseTraditional.rb +1 -1
  34. data/lib/bcdice/game_system/Cthulhu7th_Korean.rb +1 -1
  35. data/lib/bcdice/game_system/CyberpunkRed.rb +2 -6
  36. data/lib/bcdice/game_system/DeadlineHeroes.rb +1 -1
  37. data/lib/bcdice/game_system/DungeonsAndDragons5.rb +196 -0
  38. data/lib/bcdice/game_system/EmbryoMachine.rb +4 -4
  39. data/lib/bcdice/game_system/Emoklore.rb +8 -8
  40. data/lib/bcdice/game_system/FutariSousa.rb +31 -5
  41. data/lib/bcdice/game_system/GURPS.rb +1 -1
  42. data/lib/bcdice/game_system/GehennaAn.rb +2 -2
  43. data/lib/bcdice/game_system/GhostLive.rb +144 -0
  44. data/lib/bcdice/game_system/GoblinSlayer.rb +30 -8
  45. data/lib/bcdice/game_system/HunterTheReckoning5th.rb +152 -0
  46. data/lib/bcdice/game_system/IfIfIf.rb +648 -0
  47. data/lib/bcdice/game_system/Kutulu.rb +95 -0
  48. data/lib/bcdice/game_system/LostRoyal.rb +1 -1
  49. data/lib/bcdice/game_system/NobunagasBlackCastle.rb +237 -0
  50. data/lib/bcdice/game_system/OracleEngine.rb +1 -1
  51. data/lib/bcdice/game_system/Raisondetre.rb +1 -1
  52. data/lib/bcdice/game_system/RokumonSekai2.rb +1 -1
  53. data/lib/bcdice/game_system/SajinsenkiAGuS.rb +154 -0
  54. data/lib/bcdice/game_system/Sengensyou.rb +59 -0
  55. data/lib/bcdice/game_system/ShinkuuGakuen.rb +1 -1
  56. data/lib/bcdice/game_system/ShinobiGami.rb +14 -1
  57. data/lib/bcdice/game_system/SkynautsBouken.rb +93 -2
  58. data/lib/bcdice/game_system/StellarLife.rb +1 -1
  59. data/lib/bcdice/game_system/StrangerOfSwordCity.rb +1 -1
  60. data/lib/bcdice/game_system/SwordWorld.rb +2 -2
  61. data/lib/bcdice/game_system/SwordWorld2_0_SimplifiedChinese.rb +84 -0
  62. data/lib/bcdice/game_system/SwordWorld2_5_SimplifiedChinese.rb +95 -0
  63. data/lib/bcdice/game_system/SwordWorld_SimplifiedChinese.rb +29 -0
  64. data/lib/bcdice/game_system/TherapieSein.rb +2 -2
  65. data/lib/bcdice/game_system/Torg.rb +1 -1
  66. data/lib/bcdice/game_system/TorgEternity.rb +54 -18
  67. data/lib/bcdice/game_system/VampireTheMasquerade5th.rb +26 -12
  68. data/lib/bcdice/game_system/VisionConnect.rb +135 -0
  69. data/lib/bcdice/game_system/Warhammer4.rb +4 -0
  70. data/lib/bcdice/game_system/WorldOfDarkness.rb +54 -50
  71. data/lib/bcdice/game_system/cthulhu7th/full_auto.rb +1 -1
  72. data/lib/bcdice/game_system/filled_with/tresure_tables.rb +1 -1
  73. data/lib/bcdice/game_system/satasupe/tables.rb +1 -1
  74. data/lib/bcdice/game_system/sword_world/rating_parser.rb +1 -1
  75. data/lib/bcdice/game_system.rb +15 -0
  76. data/lib/bcdice/version.rb +1 -1
  77. metadata +26 -6
@@ -207,7 +207,7 @@ module BCDice
207
207
  }
208
208
 
209
209
  # 난이도 변경용 루프
210
- (0..3).each do |more_difficlty|
210
+ 4.times do |more_difficlty|
211
211
  output += getNextDifficltyMessage(more_difficlty)
212
212
 
213
213
  # 패널티 다이스를 줄이면서 굴리는 용 루프
@@ -67,7 +67,7 @@ module BCDice
67
67
  def cp_roll_result(command)
68
68
  parser = Command::Parser.new('CP', round_type: RoundType::FLOOR)
69
69
  .enable_suffix_number
70
- .restrict_cmp_op_to(nil, :>=)
70
+ .restrict_cmp_op_to(nil, :>)
71
71
  parsed = parser.parse(command)
72
72
  return nil if parsed.nil?
73
73
 
@@ -96,11 +96,7 @@ module BCDice
96
96
  end
97
97
 
98
98
  if parsed.target_number
99
- if total >= parsed.target_number
100
- result.success = true
101
- else
102
- result.failure = true
103
- end
99
+ result.condition = total > parsed.target_number
104
100
  end
105
101
 
106
102
  result.text = "(#{dice_cnt}D#{dice_face}#{Format.modifier(modify_number)}#{parsed.cmp_op}#{parsed.target_number})"
@@ -55,7 +55,7 @@ module BCDice
55
55
  private
56
56
 
57
57
  def resolute_action(command)
58
- m = /^DLH(\d+([+\-]\d+)*)$/.match(command)
58
+ m = /^DLH(\d+([+-]\d+)*)$/.match(command)
59
59
  unless m
60
60
  return nil
61
61
  end
@@ -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
 
@@ -18,11 +18,11 @@ module BCDice
18
18
  探偵用:【DT】…10面ダイスを2つ振って判定します。『有利』なら【3DT】、『不利』なら【1DT】を使います。
19
19
  助手用:【AS】…6面ダイスを2つ振って判定します。『有利』なら【3AS】、『不利』なら【1AS】を使います。
20
20
  ・各種表
21
- 【調査時】
21
+ 【セッション時】
22
22
  異常な癖決定表      SHRD/新・異常な癖決定表   SHND
23
23
  普通の?・異常な癖決定表 SHAD/ケイジ異常な癖決定表  SHKD
24
24
   口から出る表    SHFM/強引な捜査表      SHBT/すっとぼけ表       SHPI
25
-  事件に夢中表    SHEG/パートナーと……表    SHWP/何かしている表      SHDS
25
+  事件に夢中表    SHEG/パートナーと……表   SHWP/何かしている表      SHDS
26
26
   奇想天外表     SHFT/急なひらめき表     SHIN/喜怒哀楽表        SHEM
27
27
   人間エミュレート表 SHHE/人間エミュレート失敗表 SHHF/パートナーへのいたずら表 SHMP
28
28
   思わせぶり表    SHSB/もどかしい表      SHFR/突然どうした表      SHIS
@@ -38,15 +38,24 @@ module BCDice
38
38
   思わぬヒント EVH/実験をしてみよう EVX/ゲスト捜査 EVG
39
39
   ケイジ聞き込み捜査    EVQ/ケイジ大規模捜査      EVM/こっそり情報の受け渡し EVP
40
40
   同僚たちと一緒に捜査する EVO/頻染みの店シチュエーション EVF/ハードBデカアクション EVB
41
+  探偵を大人しくさせる捜査 EVL/伝統的捜査 EVZ/原始的捜査 EVR
42
+ 感情表
43
+  感情表A/B   FLT66・FLT10
44
+  気に入っているところ  FLTL66 /気に入らないところ  FLTD66
45
+  ランダム感情決定表(あなた)  FLTRA
46
+  顔のパーツ     FLTF66/体のパーツ  FLTB66/生活習慣    FLTH66
47
+  ふわっとした感覚  FLTS66/他人への態度 FLTA66/ヘビーウェイト FLTW66
48
+  同僚     FLTC66/部下     FLTU66/上司     FLTO66
49
+  捜査のやり方 FLTI66
41
50
  調査の障害表 OBT  変調表 ACT  目撃者表 EWT  迷宮入り表 WMT
51
+ 思い出の品決定表 MIT  エピソード付き思い出の品表 MITE  呼び名表A・B  NCT66・NCT10
42
52
  【設定時】
43
53
  背景表
44
54
   探偵 運命の血統 BGDD/天性の才能 BGDG/マニア     BGDM
45
55
   助手 正義の人  BGAJ/情熱の人  BGAP/巻き込まれの人 BGAI
46
- 身長表 HT  たまり場表 BT  関係表 GRT  思い出の品決定表 MIT
56
+ 身長表 HT  たまり場表 BT  関係表 GRT
47
57
  職業表A・B  JBT66・JBT10  ファッション特徴表A・B    FST66・FST10
48
- 感情表A/B  FLT66・FLT10  好きなもの/嫌いなもの表A・B LDT66・LDT10
49
- 呼び名表A・B NCT66・NCT10
58
+ 好きなもの/嫌いなもの表A・B LDT66・LDT10
50
59
  MESSAGETEXT
51
60
 
52
61
  def initialize(command)
@@ -169,6 +178,9 @@ module BCDice
169
178
  "EVO" => DiceTable::Table.from_i18n("FutariSousa.table.EVO", locale),
170
179
  "EVF" => DiceTable::Table.from_i18n("FutariSousa.table.EVF", locale),
171
180
  "EVB" => DiceTable::Table.from_i18n("FutariSousa.table.EVB", locale),
181
+ "EVL" => DiceTable::Table.from_i18n("FutariSousa.table.EVL", locale),
182
+ "EVZ" => DiceTable::Table.from_i18n("FutariSousa.table.EVZ", locale),
183
+ "EVR" => DiceTable::Table.from_i18n("FutariSousa.table.EVR", locale),
172
184
  "OBT" => DiceTable::D66Table.from_i18n("FutariSousa.table.OBT", locale),
173
185
  "ACT" => DiceTable::Table.from_i18n("FutariSousa.table.ACT", locale),
174
186
  "EWT" => DiceTable::Table.from_i18n("FutariSousa.table.EWT", locale),
@@ -183,6 +195,7 @@ module BCDice
183
195
  "BT" => DiceTable::Table.from_i18n("FutariSousa.table.BT", locale),
184
196
  "GRT" => DiceTable::D66Table.from_i18n("FutariSousa.table.GRT", locale),
185
197
  "MIT" => DiceTable::D66Table.from_i18n("FutariSousa.table.MIT", locale),
198
+ "MITE" => DiceTable::Table.from_i18n("FutariSousa.table.MITE", locale),
186
199
  "JBT66" => DiceTable::D66Table.from_i18n("FutariSousa.table.JBT66", locale),
187
200
  "JBT10" => DiceTable::Table.from_i18n("FutariSousa.table.JBT10", locale),
188
201
  "FST66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FST66", locale),
@@ -191,6 +204,19 @@ module BCDice
191
204
  "LDT10" => DiceTable::Table.from_i18n("FutariSousa.table.LDT10", locale),
192
205
  "FLT66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLT66", locale),
193
206
  "FLT10" => DiceTable::Table.from_i18n("FutariSousa.table.FLT10", locale),
207
+ "FLTL66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTL66", locale),
208
+ "FLTD66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTD66", locale),
209
+ "FLTRA" => DiceTable::Table.from_i18n("FutariSousa.table.FLTRA", locale),
210
+ "FLTF66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTF66", locale),
211
+ "FLTB66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTB66", locale),
212
+ "FLTH66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTH66", locale),
213
+ "FLTS66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTS66", locale),
214
+ "FLTA66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTA66", locale),
215
+ "FLTW66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTW66", locale),
216
+ "FLTC66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTC66", locale),
217
+ "FLTU66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTU66", locale),
218
+ "FLTO66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTO66", locale),
219
+ "FLTI66" => DiceTable::D66Table.from_i18n("FutariSousa.table.FLTI66", locale),
194
220
  "NCT66" => DiceTable::D66Table.from_i18n("FutariSousa.table.NCT66", locale),
195
221
  "NCT10" => DiceTable::Table.from_i18n("FutariSousa.table.NCT10", locale),
196
222
  }
@@ -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,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class GhostLive < Base
6
+ # ゲームシステムの識別子
7
+ ID = 'GhostLive'
8
+
9
+ # ゲームシステム名
10
+ NAME = '実況ゴーストライヴ'
11
+
12
+ # ゲームシステム名の読みがな
13
+ SORT_KEY = 'しつきようこおすとらいふ'
14
+
15
+ # ダイスボットの使い方
16
+ HELP_MESSAGE = <<~MESSAGE
17
+ ■追加目標表(p11)
18
+ ATT, AdditionalTargetTable
19
+
20
+ ■種別:地縛霊(p26)
21
+ □A.霊障リスト
22
+ JHA, JibakuHauntA
23
+ □B.霊障効果リスト
24
+ JHB, JibakuHauntB
25
+
26
+ ■種別:シャイな幽霊(p27)
27
+ □A.霊障リスト
28
+ SHA, ShyHauntA
29
+ □B.霊障効果リスト
30
+ SHB, ShyHauntB
31
+
32
+ ■種別:ぐちゃぐちゃ(p28)
33
+ □A.霊障リスト
34
+ GHA, GuchaHauntA
35
+ □B.霊障効果リスト
36
+ GHB, GuchaHauntB
37
+ MESSAGE
38
+
39
+ def eval_game_system_specific_command(command)
40
+ command = ALIAS[command] || command
41
+ roll_tables(command, TABLES)
42
+ end
43
+
44
+ TABLES = {
45
+ "AdditionalTargetTable" => DiceTable::Table.new(
46
+ "追加目標表",
47
+ "1D6",
48
+ [
49
+ "オバケを撮影する。(依頼主:専門家/報酬:1L)",
50
+ "誰かひとりが霊障を[サイクル数]回受ける。(依頼主:専門家/報酬:[サイクル数]L)",
51
+ "誰かひとりが[精神力]を10以下の状態で帰る。(依頼主:専門家/報酬:3L)",
52
+ "[精神力]の平均が20以下の状態で帰る。(依頼主:リスナー/報酬:[視聴回数]を10倍)",
53
+ "全員がスマホ以外の[アイテム]を1個だけ持ち込んで生還する。(依頼主:リスナー/報酬:[視聴回数]を10倍)",
54
+ "すべての[回収品]を集める。(依頼主:専門家/報酬:5L)",
55
+ ]
56
+ ),
57
+ "JibakuHauntA" => DiceTable::Table.new(
58
+ "地縛霊:霊障リスト",
59
+ "1D6",
60
+ [
61
+ "隙間――家具の隙間、扉の隙間、そんな暗がりから視線を感じる。",
62
+ "腐臭――吐き気を催すような、下水に似た臭いが漂ってくる。",
63
+ "吐息――「ハァ……」耳元に、やけに湿った吐息が吹きかけられる。",
64
+ "足音――立ち止まる度に、ひとつ多く足音が響く。誰か、いる……?",
65
+ "背後――振り向いても、そこには誰もいない。それなのに、ずっと後ろに気配を感じる。",
66
+ "鏡――鏡に背を向けた瞬間、あり得ない強さでそちらへ引き寄せられた。肩には手の形のアザができている。",
67
+ ]
68
+ ),
69
+ "JibakuHauntB" => DiceTable::Table.new(
70
+ "地縛霊:霊障効果リスト",
71
+ "1D6",
72
+ [
73
+ "[精神力]減少:[1D2+PC人数]点/[視聴回数]増加:とくになし/特殊効果:とくになし",
74
+ "[精神力]減少:[1D4+PC人数]点/[視聴回数]増加:とくになし/特殊効果:とくになし",
75
+ "[精神力]減少:[1D6+PC人数]点/[視聴回数]増加:2倍/特殊効果:とくになし",
76
+ "[精神力]減少:[1D10+PC人数]点/[視聴回数]増加:3倍/特殊効果:シーンに登場しているPCの[アイテム]を1つ破壊する。",
77
+ "[精神力]減少:[1D20+PC人数]点/[視聴回数]増加:5倍/特殊効果:シーンに登場しているPCのスマホを破壊する。",
78
+ "[精神力]減少:[1D100+PC人数]点/[視聴回数]増加:10倍/特殊効果:シーンに登場しているPCのスマホを破壊する。",
79
+ ]
80
+ ),
81
+ "ShyHauntA" => DiceTable::Table.new(
82
+ "シャイな幽霊:霊障リスト",
83
+ "1D6",
84
+ [
85
+ "倦怠感――歩くのも辛いくらいの倦怠感。生きているのも辛い。",
86
+ "ラップ音――弾けるような、叩くような音が連続して聞こえる。",
87
+ "空飛ぶ皿――棚に収まっていた食器が、不意に飛び出し、けたたましい音を立てて砕けていく。",
88
+ "頭痛――頭が、割れそうに痛い。小さな物音ですら頭に響いてくる。",
89
+ "点滅――灯りが明滅する。……あれ、ここ電気通ってたっけ?",
90
+ "血文字――壁に、床に、赤⿊い液体が滲み出す。それは文字を形作った。「か え れ」",
91
+ ]
92
+ ),
93
+ "ShyHauntB" => DiceTable::Table.new(
94
+ "シャイな幽霊:霊障効果リスト",
95
+ "1D6",
96
+ [
97
+ "[精神力]減少:[2+PC人数]点/[視聴回数]増加:とくになし/特殊効果:とくになし",
98
+ "[精神力]減少:[4+PC人数]点/[視聴回数]増加:2倍/特殊効果:シーンに登場しているPCがふたりの場合、追加で[精神力]を2減少させる。",
99
+ "[精神力]減少:[6+PC人数]点/[視聴回数]増加:3倍/特殊効果:シーンに登場しているPCがひとりの場合、追加で[精神力]を4減少させる。",
100
+ "[精神力]減少:[10+PC人数]点/[視聴回数]増加:5倍/特殊効果:シーンに登場しているPCがふたりの場合、追加で[精神力]を6減少させる。",
101
+ "[精神力]減少:[20+PC人数]点/[視聴回数]増加:10倍/特殊効果:シーンに登場しているPCがひとりの場合、追加で[精神力]を2減少させる。",
102
+ "[精神力]減少:[40+PC人数]点/[視聴回数]増加:20倍/特殊効果:シーンに登場しているPCのスマホを破壊する。",
103
+ ]
104
+ ),
105
+ "GuchaHauntA" => DiceTable::Table.new(
106
+ "ぐちゃぐちゃ:霊障リスト",
107
+ "1D6",
108
+ [
109
+ "走る人形――ひび割れた人形が落ちている。一瞬視線をそらした瞬間、それはありえない動きで走り去っていった。",
110
+ "血痕――天井から血が滴ってくる。その量は、おおよそ人一人分……いや、それ以上だ。",
111
+ "着信――スマホの着信音が鳴る。こんな時に誰が――表示されていたのは、死んだはずの知り合いの名前だった。",
112
+ "自分に似た他人――自分にそっくりな人が目の前に立っていた、気がする。",
113
+ "衝撃――誰かに思いっきり押された気がしたのに誰もいない。",
114
+ "記憶がない――数分間のことを何も覚えてない。コメント欄がリスナーの心配する声でいっぱいだ。いったい何が……?",
115
+ ]
116
+ ),
117
+ "GuchaHauntB" => DiceTable::Table.new(
118
+ "ぐちゃぐちゃ:霊障効果リスト",
119
+ "1D6",
120
+ [
121
+ "[精神力]減少:[5+PC人数]点/[視聴回数]増加:2倍/特殊効果:とくになし",
122
+ "[精神力]減少:[10+PC人数]点/[視聴回数]増加:3倍/特殊効果:とくになし",
123
+ "[精神力]減少:[2D10+PC人数]点/[視聴回数]増加:4倍/特殊効果:シーンに登場しているPCがふたりの場合、追加で[精神力]を5減少させる。",
124
+ "[精神力]減少:[3D10+PC人数]点/[視聴回数]増加:5倍/特殊効果:シーンに登場しているPCがひとりの場合、[アイテム]をランダムに1つ壊す。",
125
+ "[精神力]減少:[1D100+PC人数]点/[視聴回数]増加:10倍/特殊効果:シーンに登場しているPCのスマホを破壊する。",
126
+ "[精神力]減少:[1D100+10+PC人数]点/[視聴回数]増加:20倍/特殊効果:すべてのPCのスマホを破壊する。",
127
+ ]
128
+ ),
129
+ }.transform_keys(&:upcase).freeze
130
+
131
+ ALIAS = {
132
+ "ATT" => "AdditionalTargetTable",
133
+ "JHA" => "JibakuHauntA",
134
+ "JHB" => "JibakuHauntB",
135
+ "SHA" => "ShyHauntA",
136
+ "SHB" => "ShyHauntB",
137
+ "GHA" => "GuchaHauntA",
138
+ "GHB" => "GuchaHauntB",
139
+ }.transform_values(&:upcase).freeze
140
+
141
+ register_prefix(TABLES.keys, ALIAS.keys)
142
+ end
143
+ end
144
+ end
@@ -14,11 +14,13 @@ module BCDice
14
14
 
15
15
  # ダイスボットの使い方
16
16
  HELP_MESSAGE = <<~MESSAGETEXT
17
- ・判定 GS(x)>=y
17
+ ・判定 GS(x)@c#f>=y
18
18
   2d6の判定を行い、達成値を出力します。
19
-  xは基準値、yは目標値です。いずれも省略可能です。
19
+  xは基準値、yは目標値、cは大成功の下限、fは大失敗の上限です。いずれも省略可能です。
20
+  cが未指定の場合には c=12 、fが未指定の場合には f=2 となります。
20
21
   yが設定されている場合、大成功/成功/失敗/大失敗を自動判定します。
21
22
   例)GS>=12 GS>10 GS(10)>14 GS+10>=15 GS10>=15 GS(10) GS+10 GS10 GS
23
+    GS@10 GS@10#3 GS#3@10
22
24
 
23
25
  ・祈念 MCPI(n)$m
24
26
   祈念を行います。
@@ -62,15 +64,26 @@ module BCDice
62
64
  end
63
65
 
64
66
  def getCheckResult(command)
65
- m = /^GS([-+]?\d+)?((>=?)(\d+))?$/i.match(command)
67
+ m = /^GS([-+]?\d+)?(?:(?:([@#])([-+]?\d+))(?:([@#])([-+]?\d+))?)?(?:(>=?)(\d+))?$/i.match(command)
66
68
  unless m
67
69
  return nil
68
70
  end
69
71
 
70
72
  basis = m[1].to_i # 基準値
71
- target = m[4].to_i
72
- without_compare = m[2].nil? || target <= 0
73
- cmp_op = m[3]
73
+ target = m[7].to_i
74
+ cmp_op = m[6]
75
+ without_compare = cmp_op.nil?
76
+
77
+ option1 = m[2]
78
+ option1_value = m[3]
79
+ option2 = m[4]
80
+ option2_param = m[5]
81
+
82
+ if option1 && option1 == option2
83
+ return nil
84
+ end
85
+
86
+ threshold_critical, threshold_fumble = calc_threshold(option1, option1_value, option2, option2_param)
74
87
 
75
88
  dice_list = @randomizer.roll_barabara(2, 6)
76
89
  total = dice_list.sum()
@@ -78,8 +91,8 @@ module BCDice
78
91
 
79
92
  achievement = basis + total # 達成値
80
93
 
81
- fumble = diceText == "1,1"
82
- critical = diceText == "6,6"
94
+ fumble = total <= threshold_fumble
95
+ critical = total >= threshold_critical
83
96
 
84
97
  result = " > #{resultStr(achievement, target, cmp_op, fumble, critical)}"
85
98
  if without_compare && !fumble && !critical
@@ -90,6 +103,15 @@ module BCDice
90
103
  return "(#{command}) > #{basis_str}#{total}[#{diceText}] > #{achievement}#{result}"
91
104
  end
92
105
 
106
+ CRITICAL = 12
107
+ FUMBLE = 2
108
+
109
+ def calc_threshold(option1, option1_value, _option2, option2_value)
110
+ critical, fumble = option1 == "@" ? [option1_value, option2_value] : [option2_value, option1_value]
111
+
112
+ return [critical&.to_i || CRITICAL, fumble&.to_i || FUMBLE]
113
+ end
114
+
93
115
  def murmurChantPrayInvoke(command)
94
116
  m = /^MCPI(\+?\d+)?\$(\d+)$/i.match(command)
95
117
  unless m