bcdice 3.12.0 → 3.14.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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +72 -0
  3. data/i18n/Arianrhod/ko_kr.yml +3 -0
  4. data/i18n/Cthulhu/en_us.yml +11 -0
  5. data/i18n/CyberpunkRed/ja_jp.yml +389 -0
  6. data/i18n/FinalFantasyXIV/en_us.yml +4 -0
  7. data/i18n/FinalFantasyXIV/ja_jp.yml +4 -0
  8. data/i18n/FutariSousa/ja_jp.yml +158 -1
  9. data/i18n/KizunaBullet/ja_jp.yml +653 -0
  10. data/i18n/MonotoneMuseum/ja_jp.yml +246 -14
  11. data/i18n/SkynautsBouken/ja_jp.yml +114 -0
  12. data/i18n/TensaiGunshiNiNaro/ja_jp.yml +126 -0
  13. data/i18n/UnsungDuet/ja_jp.yml +62 -0
  14. data/i18n/UnsungDuet/ko_kr.yml +62 -0
  15. data/i18n/en_us.yml +5 -0
  16. data/lib/bcdice/arithmetic/parser.rb +4 -2
  17. data/lib/bcdice/base.rb +1 -1
  18. data/lib/bcdice/command/parser.rb +4 -2
  19. data/lib/bcdice/common_command/add_dice/parser.rb +4 -2
  20. data/lib/bcdice/common_command/barabara_dice/parser.rb +4 -2
  21. data/lib/bcdice/common_command/calc/parser.rb +4 -2
  22. data/lib/bcdice/common_command/reroll_dice/parser.rb +4 -2
  23. data/lib/bcdice/common_command/tally_dice/parser.rb +4 -2
  24. data/lib/bcdice/common_command/upper_dice/parser.rb +4 -2
  25. data/lib/bcdice/dice_table/range_table.rb +24 -0
  26. data/lib/bcdice/game_system/Agnostos.rb +248 -0
  27. data/lib/bcdice/game_system/Airgetlamh.rb +1 -1
  28. data/lib/bcdice/game_system/Alsetto.rb +2 -2
  29. data/lib/bcdice/game_system/AniMalus.rb +261 -0
  30. data/lib/bcdice/game_system/Arianrhod_Korean.rb +30 -0
  31. data/lib/bcdice/game_system/ArknightsFan.rb +182 -0
  32. data/lib/bcdice/game_system/Avandner.rb +1 -1
  33. data/lib/bcdice/game_system/BBN.rb +1 -1
  34. data/lib/bcdice/game_system/BadLife.rb +3 -3
  35. data/lib/bcdice/game_system/BarnaKronika.rb +1 -1
  36. data/lib/bcdice/game_system/BlackJacket.rb +244 -0
  37. data/lib/bcdice/game_system/Chill3.rb +6 -6
  38. data/lib/bcdice/game_system/ConvictorDrive.rb +5 -4
  39. data/lib/bcdice/game_system/Cthulhu_English.rb +59 -0
  40. data/lib/bcdice/game_system/CyberpunkRed.rb +14 -4
  41. data/lib/bcdice/game_system/DarkSouls.rb +1 -1
  42. data/lib/bcdice/game_system/DivineCharger.rb +841 -0
  43. data/lib/bcdice/game_system/Elric.rb +1 -1
  44. data/lib/bcdice/game_system/FinalFantasyXIV.rb +115 -0
  45. data/lib/bcdice/game_system/FinalFantasyXIV_English.rb +39 -0
  46. data/lib/bcdice/game_system/FullFace.rb +121 -0
  47. data/lib/bcdice/game_system/FutariSousa.rb +88 -8
  48. data/lib/bcdice/game_system/InfiniteFantasia.rb +4 -1
  49. data/lib/bcdice/game_system/Insane_Korean.rb +1 -1
  50. data/lib/bcdice/game_system/InvisibleLiar.rb +79 -0
  51. data/lib/bcdice/game_system/Irisbane.rb +1 -1
  52. data/lib/bcdice/game_system/KillDeathBusiness.rb +1 -1
  53. data/lib/bcdice/game_system/KizunaBullet.rb +240 -0
  54. data/lib/bcdice/game_system/KyokoShinshoku.rb +201 -0
  55. data/lib/bcdice/game_system/Liminal.rb +136 -0
  56. data/lib/bcdice/game_system/MetalHead.rb +1 -1
  57. data/lib/bcdice/game_system/MetalHeadExtream.rb +1 -1
  58. data/lib/bcdice/game_system/MonotoneMuseum.rb +14 -1
  59. data/lib/bcdice/game_system/NRR.rb +122 -0
  60. data/lib/bcdice/game_system/NSSQ.rb +1 -1
  61. data/lib/bcdice/game_system/NightWizard.rb +1 -1
  62. data/lib/bcdice/game_system/NightWizard3rd.rb +15 -2
  63. data/lib/bcdice/game_system/OneWayHeroics.rb +1 -1
  64. data/lib/bcdice/game_system/PastFutureParadox.rb +1238 -0
  65. data/lib/bcdice/game_system/Postman.rb +3 -3
  66. data/lib/bcdice/game_system/RecordOfSteam.rb +1 -1
  67. data/lib/bcdice/game_system/RuneQuestRoleplayingInGlorantha.rb +1 -1
  68. data/lib/bcdice/game_system/Ryutama.rb +4 -4
  69. data/lib/bcdice/game_system/ShuumatsuBargainWars.rb +203 -0
  70. data/lib/bcdice/game_system/Siren.rb +97 -0
  71. data/lib/bcdice/game_system/Skynauts.rb +1 -1
  72. data/lib/bcdice/game_system/SkynautsBouken.rb +45 -130
  73. data/lib/bcdice/game_system/Strave.rb +1 -1
  74. data/lib/bcdice/game_system/SwordWorld.rb +1 -1
  75. data/lib/bcdice/game_system/TensaiGunshiNiNaro.rb +228 -0
  76. data/lib/bcdice/game_system/TheOneRing2nd.rb +406 -0
  77. data/lib/bcdice/game_system/TheUnofficialHollowKnightRPG.rb +185 -0
  78. data/lib/bcdice/game_system/TherapieSein.rb +1 -1
  79. data/lib/bcdice/game_system/TrailOfCthulhu.rb +139 -0
  80. data/lib/bcdice/game_system/TrinitySeven.rb +1 -1
  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/VampireTheMasquerade5th.rb +12 -6
  85. data/lib/bcdice/game_system/Warhammer4.rb +124 -3
  86. data/lib/bcdice/game_system/WerewolfTheApocalypse5th.rb +12 -6
  87. data/lib/bcdice/game_system/Yotabana.rb +62 -0
  88. data/lib/bcdice/game_system/cthulhu7th/full_auto.rb +1 -1
  89. data/lib/bcdice/game_system/cyberpunk_red/tables.rb +111 -473
  90. data/lib/bcdice/game_system/kizuna_bullet/tables.rb +171 -0
  91. data/lib/bcdice/game_system/sword_world/rating_parser.rb +4 -2
  92. data/lib/bcdice/game_system.rb +24 -0
  93. data/lib/bcdice/loader.rb +1 -1
  94. data/lib/bcdice/version.rb +1 -1
  95. metadata +55 -5
@@ -113,7 +113,7 @@ module BCDice
113
113
  dice = 2 if dice < 2
114
114
  dice = 12 if dice > 12
115
115
 
116
- currentKey = [command.rate + round * command.rateup, keyMax].min
116
+ currentKey = (command.rate + round * command.rateup).clamp(0, keyMax)
117
117
  debug("currentKey", currentKey)
118
118
  rateValue = newRates[dice][currentKey]
119
119
  debug("rateValue", rateValue)
@@ -0,0 +1,228 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class TensaiGunshiNiNaro < Base
6
+ # ゲームシステムの識別子
7
+ ID = 'TensaiGunshiNiNaro'
8
+
9
+ # ゲームシステム名
10
+ NAME = '天才軍師になろう'
11
+
12
+ # ゲームシステム名の読みがな
13
+ SORT_KEY = 'てんさいくんしになろう'
14
+
15
+ # ダイスボットの使い方
16
+ HELP_MESSAGE = <<~MESSAGETEXT
17
+ ・行為判定
18
+ TN6…「有利」を得ていない場合、6面ダイスを2つ振って判定します。
19
+ TN10…「有利」を得ている場合、10面ダイスを2つ振って判定します。
20
+ 不調 気づかぬうちの不満【C】…判定のダイス目が「4」でも判定に成功しません。数字の後ろに【C】をつけます。
21
+  例)TN6C
22
+ 軍師スキル 〇〇サポート【S】…決戦フェイズの判定中「3」の出目を出しても判定に成功します。数字の後ろに【S】をつけます。
23
+  例)TN6S
24
+ 英傑スキル/武人 煌めく刃【B】…決戦フェイズの判定中「3」の出目を出しても判定に成功となり、スペシャルが発生します。数字の後ろに【B】をつけます。
25
+  例)TN6B
26
+ 英傑スキル/カリスマ 御身のためならば【Y】…「交流」「スカウト」の判定中「3」の出目を出しても判定に成功となり、スペシャルが発生します。数字の後ろに【Y】をつけます。
27
+  例)TN6Y
28
+ 英傑スキル/英傑汎用 凄腕エージェント【A】…活動フェイズの判定中「3」の出目を出しても判定に成功となり、スペシャルが発生します。数字の後ろに【A】をつけます。
29
+  例)TN6A
30
+ 数字の後ろに複数のコマンドを追加できます。
31
+  例)TN10CYA
32
+ ・ダメージ計算 xDM>=t
33
+  [ダメージ計算]を行う。成否と【HP】の減少量を表示する。
34
+  x: 6面ダイス数
35
+  t: 防御力
36
+ ・各種表
37
+ 関係決定表 RELA
38
+ 平時天才軍師表 PTGS
39
+ 平時英傑表 PTHE
40
+ スカウト表 SCOU
41
+ 変調表 BDST
42
+ MESSAGETEXT
43
+
44
+ def initialize(command)
45
+ super(command)
46
+
47
+ @d66_sort_type = D66SortType::ASC
48
+ @round_type = RoundType::FLOOR
49
+ end
50
+
51
+ register_prefix('TN(6|10)[CSBYA]*', '\d+DM')
52
+
53
+ def eval_game_system_specific_command(command)
54
+ roll_judge(command) || roll_damage(command) || roll_tables(command, self.class::TABLES)
55
+ end
56
+
57
+ private
58
+
59
+ # 行為判定
60
+ def roll_judge(command)
61
+ m = /^TN(6|10)([CSBYA]*)$/.match(command)
62
+ unless m
63
+ return nil
64
+ end
65
+
66
+ # 成功となる出目
67
+ success_dices = [4, 5, 6, 7, 8, 9, 10]
68
+
69
+ # スペシャルとなる出目
70
+ special_dices = [6, 10]
71
+
72
+ # ファンブルとなる出目
73
+ fumble_dices = [1]
74
+
75
+ # 有利
76
+ advantage = m[1] == "10"
77
+
78
+ # 不調 気づかぬうちの不満
79
+ complaints = m[2].include?("C")
80
+
81
+ # 軍師スキル 〇〇サポート
82
+ support = m[2].include?("S")
83
+
84
+ # 英傑スキル/武人 煌めく刃
85
+ blade = m[2].include?("B")
86
+
87
+ # 英傑スキル/カリスマ 御身のためならば
88
+ you = m[2].include?("Y")
89
+
90
+ # 英傑スキル/英傑汎用 凄腕エージェント
91
+ agent = m[2].include?("A")
92
+
93
+ # 〇〇サポート、煌めく刃、御身のためならば、凄腕エージェントいずれかの適用時
94
+ if support | blade | you | agent
95
+ # 成功となる出目に3を追加
96
+ success_dices.push(3)
97
+ end
98
+
99
+ # 煌めく刃、御身のためならば、凄腕エージェントいずれかの適用時
100
+ if blade | you | agent
101
+ # スペシャルとなる出目に3を追加
102
+ special_dices.push(3)
103
+ end
104
+
105
+ # 気づかぬうちの不満適用時
106
+ if complaints
107
+ # 成功となる出目から4を削除
108
+ success_dices.delete(4)
109
+ end
110
+
111
+ dice_size = advantage ? 10 : 6
112
+ dice_list = @randomizer.roll_barabara(2, dice_size)
113
+
114
+ texts = []
115
+ is_critical = false
116
+ is_fumble = false
117
+ is_success = false
118
+
119
+ # スペシャルとなる出目を含む場合
120
+ unless dice_list.intersection(special_dices).empty?
121
+ # クリティカルフラグを立てる
122
+ is_critical = true
123
+ # スペシャルのシステムメッセージを追加
124
+ texts.push(translate("TensaiGunshiNiNaro.JUDGE.critical"))
125
+ special_effects = []
126
+ # 通常時の追加効果
127
+ special_effects.push(translate("TensaiGunshiNiNaro.NORMAL.critical"))
128
+ # 英傑スキル/武人 煌めく刃による追加効果
129
+ special_effects.push(translate("TensaiGunshiNiNaro.BLADE.critical")) if blade
130
+ # 英傑スキル/カリスマ 御身のためならばによる追加効果
131
+ special_effects.push(translate("TensaiGunshiNiNaro.YOU.critical")) if you
132
+ # 追加効果を結合してカッコ内に格納
133
+ texts.push("(#{special_effects.join('')})")
134
+ end
135
+
136
+ # ファンブルとなる出目を含む場合
137
+ unless dice_list.intersection(fumble_dices).empty?
138
+ # ファンブルフラグを立てる
139
+ is_fumble = true
140
+ # ファンブルのシステムメッセージを追加
141
+ texts.push(translate("TensaiGunshiNiNaro.JUDGE.fumble"))
142
+ # ファンブルの追加効果をカッコ内に格納
143
+ texts.push("(#{translate('TensaiGunshiNiNaro.NORMAL.fumble')})")
144
+ end
145
+
146
+ if dice_list.intersection(success_dices).empty?
147
+ # 成功となる出目を含まない場合
148
+ # 失敗の汎用メッセージを追加
149
+ texts.push(translate("failure"))
150
+ else
151
+ # 成功となる出目を含む場合
152
+ # 成功フラグを立てる
153
+ is_success = true
154
+ # 成功の汎用メッセージを追加
155
+ texts.push(translate("success"))
156
+ end
157
+
158
+ return Result.new.tap do |r|
159
+ # テキストを整形
160
+ r.text = "#{command} > [#{dice_list.join(',')}] > #{texts.join('')}"
161
+ # 各種成否を格納
162
+ r.condition = is_success
163
+ r.critical = is_critical
164
+ r.fumble = is_fumble
165
+ end
166
+ end
167
+
168
+ # ダメージ計算
169
+ def roll_damage(command)
170
+ parser = Command::Parser.new("DM", round_type: @round_type)
171
+ .has_prefix_number
172
+ .disable_modifier
173
+ .restrict_cmp_op_to(:>=)
174
+ parsed = parser.parse(command)
175
+ unless parsed
176
+ return nil
177
+ end
178
+
179
+ text = ''
180
+ is_success = false
181
+
182
+ # ダメージ計算
183
+ damage = @randomizer.roll_sum(parsed.prefix_number, 6)
184
+ # HP減少量計算
185
+ dec = damage / parsed.target_number
186
+
187
+ # HP減少量の最大値は3
188
+ dec = 3 if dec > 3
189
+
190
+ if dec > 0
191
+ # HPを減らせた場合
192
+ # 成功フラグを立てる
193
+ is_success = true
194
+ # 成功メッセージを追加
195
+ text = translate("TensaiGunshiNiNaro.DAMAGE.success", damage: damage.to_s, dec: dec.to_s)
196
+ else
197
+ # 失敗メッセージを追加
198
+ text = translate("TensaiGunshiNiNaro.DAMAGE.failure", damage: damage.to_s)
199
+ end
200
+
201
+ return Result.new.tap do |r|
202
+ # テキストを整形
203
+ r.text = "#{command} > #{damage} > #{text}"
204
+ # 各種成否を格納
205
+ r.condition = is_success
206
+ end
207
+ end
208
+
209
+ class << self
210
+ private
211
+
212
+ def translate_tables(locale)
213
+ {
214
+ "RELA" => DiceTable::D66Table.from_i18n("TensaiGunshiNiNaro.table.RELA", locale),
215
+ "PTGS" => DiceTable::D66Table.from_i18n("TensaiGunshiNiNaro.table.PTGS", locale),
216
+ "PTHE" => DiceTable::D66Table.from_i18n("TensaiGunshiNiNaro.table.PTHE", locale),
217
+ "SCOU" => DiceTable::D66Table.from_i18n("TensaiGunshiNiNaro.table.SCOU", locale),
218
+ "BDST" => DiceTable::Table.from_i18n("TensaiGunshiNiNaro.table.BDST", locale),
219
+ }
220
+ end
221
+ end
222
+
223
+ TABLES = translate_tables(:ja_jp)
224
+
225
+ register_prefix(TABLES.keys)
226
+ end
227
+ end
228
+ end
@@ -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