bcdice 3.13.0 → 3.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +104 -0
- data/i18n/Arianrhod/ko_kr.yml +3 -0
- data/i18n/Cthulhu/en_us.yml +11 -0
- data/i18n/Cthulhu/zh_hant.yml +1 -1
- data/i18n/CyberpunkRed/ja_jp.yml +389 -0
- data/i18n/CyberpunkRed/ko_kr.yml +389 -0
- data/i18n/DoubleCross/ja_jp.yml +53 -0
- data/i18n/DoubleCross/ko_kr.yml +52 -0
- data/i18n/FinalFantasyXIV/en_us.yml +4 -0
- data/i18n/FinalFantasyXIV/ja_jp.yml +4 -0
- data/i18n/FutariSousa/ja_jp.yml +284 -127
- data/i18n/KillDeathBusiness/ja_jp.yml +198 -0
- data/i18n/KizunaBullet/ja_jp.yml +653 -0
- data/i18n/MagicaLogia/ko_kr.yml +2 -2
- data/i18n/MonotoneMuseum/ja_jp.yml +246 -14
- data/i18n/MonotoneMuseum/ko_kr.yml +227 -0
- data/i18n/SkynautsBouken/ja_jp.yml +114 -0
- data/i18n/SkynautsBouken/ko_kr.yml +114 -0
- data/i18n/StratoShout/ko_kr.yml +102 -102
- data/i18n/TensaiGunshiNiNaro/ja_jp.yml +126 -0
- data/i18n/UnsungDuet/ja_jp.yml +62 -0
- data/i18n/UnsungDuet/ko_kr.yml +62 -0
- data/i18n/en_us.yml +5 -0
- data/i18n/zh_hant.yml +5 -0
- data/lib/bcdice/dice_table/range_table.rb +24 -0
- data/lib/bcdice/game_system/Agnostos.rb +248 -0
- data/lib/bcdice/game_system/Aionia.rb +131 -0
- data/lib/bcdice/game_system/AniMalus.rb +99 -59
- data/lib/bcdice/game_system/Arianrhod_Korean.rb +32 -0
- data/lib/bcdice/game_system/ArknightsFan.rb +245 -87
- data/lib/bcdice/game_system/BlackJacket.rb +244 -0
- data/lib/bcdice/game_system/BlackJacket_Korean.rb +244 -0
- data/lib/bcdice/game_system/CharonSanctions.rb +112 -0
- data/lib/bcdice/game_system/ConvictorDrive.rb +5 -4
- data/lib/bcdice/game_system/Cthulhu7th.rb +1 -1
- data/lib/bcdice/game_system/Cthulhu7th_ChineseTraditional/full_auto.rb +293 -0
- data/lib/bcdice/game_system/Cthulhu7th_ChineseTraditional/rollable.rb +43 -0
- data/lib/bcdice/game_system/Cthulhu7th_ChineseTraditional.rb +470 -306
- data/lib/bcdice/game_system/Cthulhu_English.rb +59 -0
- data/lib/bcdice/game_system/CyberpunkRed.rb +15 -6
- data/lib/bcdice/game_system/CyberpunkRed_Korean.rb +66 -0
- data/lib/bcdice/game_system/DivineCharger.rb +841 -0
- data/lib/bcdice/game_system/DoubleCross.rb +18 -1
- data/lib/bcdice/game_system/DoubleCross_Korean.rb +5 -1
- data/lib/bcdice/game_system/FinalFantasyXIV.rb +115 -0
- data/lib/bcdice/game_system/FinalFantasyXIV_English.rb +39 -0
- data/lib/bcdice/game_system/FullFace.rb +63 -12
- data/lib/bcdice/game_system/FutariSousa.rb +105 -13
- data/lib/bcdice/game_system/Garactier.rb +479 -0
- data/lib/bcdice/game_system/GardenOrder.rb +390 -9
- data/lib/bcdice/game_system/GundogRevised.rb +8 -8
- data/lib/bcdice/game_system/Insane_Korean.rb +1 -1
- data/lib/bcdice/game_system/InvisibleLiar.rb +79 -0
- data/lib/bcdice/game_system/KillDeathBusiness.rb +155 -3
- data/lib/bcdice/game_system/KimitoYell.rb +568 -0
- data/lib/bcdice/game_system/KizunaBullet.rb +240 -0
- data/lib/bcdice/game_system/KyokoShinshoku.rb +51 -8
- data/lib/bcdice/game_system/Magius.rb +125 -0
- data/lib/bcdice/game_system/Magius_3rdNewTokyoCity.rb +62 -0
- data/lib/bcdice/game_system/MonotoneMuseum.rb +14 -1
- data/lib/bcdice/game_system/MonotoneMuseum_Korean.rb +4 -4
- data/lib/bcdice/game_system/NRR.rb +2 -2
- data/lib/bcdice/game_system/NSSQ.rb +17 -9
- data/lib/bcdice/game_system/NervWhitePaper.rb +129 -0
- data/lib/bcdice/game_system/RogueLikeHalf.rb +198 -0
- data/lib/bcdice/game_system/RuneQuestRoleplayingInGlorantha.rb +16 -13
- data/lib/bcdice/game_system/ShinobiGami.rb +73 -43
- data/lib/bcdice/game_system/ShuumatsuBargainWars.rb +203 -0
- data/lib/bcdice/game_system/SkynautsBouken.rb +49 -134
- data/lib/bcdice/game_system/SkynautsBouken_Korean.rb +57 -0
- data/lib/bcdice/game_system/StratoShout_Korean.rb +1 -1
- data/lib/bcdice/game_system/SwordWorld.rb +5 -1
- data/lib/bcdice/game_system/SwordWorld2_5.rb +3 -2
- data/lib/bcdice/game_system/TensaiGunshiNiNaro.rb +262 -0
- data/lib/bcdice/game_system/TheIndieHack.rb +82 -0
- data/lib/bcdice/game_system/TheOneRing2nd.rb +406 -0
- data/lib/bcdice/game_system/TheUnofficialHollowKnightRPG.rb +185 -0
- data/lib/bcdice/game_system/TorgEternity.rb +35 -6
- data/lib/bcdice/game_system/TrailOfCthulhu.rb +139 -0
- data/lib/bcdice/game_system/UnsungDuet.rb +17 -75
- data/lib/bcdice/game_system/UnsungDuet_Korean.rb +41 -0
- data/lib/bcdice/game_system/Utakaze.rb +55 -2
- data/lib/bcdice/game_system/Warhammer4.rb +124 -3
- data/lib/bcdice/game_system/WoW.rb +161 -0
- data/lib/bcdice/game_system/YankeeMustDie.rb +192 -0
- data/lib/bcdice/game_system/YearZeroEngine.rb +225 -28
- data/lib/bcdice/game_system/Yotabana.rb +62 -0
- data/lib/bcdice/game_system/YuMyoKishi.rb +141 -0
- data/lib/bcdice/game_system/cyberpunk_red/tables.rb +111 -473
- data/lib/bcdice/game_system/kizuna_bullet/tables.rb +171 -0
- data/lib/bcdice/game_system/sword_world/rating_lexer.rb +1 -0
- data/lib/bcdice/game_system/sword_world/rating_options.rb +4 -1
- data/lib/bcdice/game_system/sword_world/rating_parsed.rb +5 -0
- data/lib/bcdice/game_system/sword_world/rating_parser.rb +129 -115
- data/lib/bcdice/game_system.rb +31 -0
- data/lib/bcdice/version.rb +1 -1
- metadata +50 -6
@@ -7,159 +7,302 @@ module BCDice
|
|
7
7
|
ID = "ArknightsFan"
|
8
8
|
|
9
9
|
# ゲームシステム名
|
10
|
-
NAME = "アークナイツTRPG by
|
10
|
+
NAME = "アークナイツTRPG by daaaper"
|
11
11
|
|
12
12
|
# ゲームシステム名の読みがな
|
13
|
-
SORT_KEY = "ああくないつTRPG
|
13
|
+
SORT_KEY = "ああくないつTRPGはいてえはあ"
|
14
14
|
|
15
15
|
HELP_MESSAGE = <<~TEXT
|
16
|
-
■
|
17
|
-
nDm
|
16
|
+
■ 能力値判定 (nADm<=x)
|
17
|
+
nDmのダイスロールをして、出目が x 以下であれば成功。
|
18
18
|
出目が91以上でエラー。
|
19
19
|
出目が10以下でクリティカル。
|
20
20
|
|
21
|
-
■
|
21
|
+
■ 攻撃/防御判定 (nABm<=x)
|
22
22
|
nBmのダイスロールをして、
|
23
|
-
x 以下であれば成功数+1。
|
24
|
-
出目が91
|
25
|
-
出目が10
|
23
|
+
出目が x 以下であれば成功数+1。
|
24
|
+
出目が91以上でエラー。成功数-1。
|
25
|
+
出目が10以下でクリティカル。成功数+1。
|
26
26
|
上記による成功数をカウント。
|
27
27
|
|
28
|
-
■
|
28
|
+
■ 役職効果付き攻撃判定 (nABm<=x--役職名h)
|
29
|
+
h: 健康状態(0: 健康、1: 中等症、2: 重症)
|
29
30
|
nBmのダイスロールをして、
|
30
31
|
出目が x 以下であれば成功数+1。
|
31
|
-
出目が91
|
32
|
-
出目が10
|
33
|
-
|
34
|
-
狙撃(
|
32
|
+
出目が91以上でエラー。成功数-1。
|
33
|
+
出目が10以下でクリティカル。成功数+1。
|
34
|
+
上記による成功数をカウントした上で、以下の役職名による成功数増加効果を適応。
|
35
|
+
狙撃(SNI): 健康(h=0)かつ成功数1以上のとき、成功数+1。
|
36
|
+
健康状態hを省略した場合、健康(h=0)として扱われる。
|
37
|
+
|
38
|
+
■ 鉱石病判定 (ORPx@y+Dd+Tt)
|
39
|
+
x: 生理的耐性、y: 上昇後侵食度、d: ダイス補正、t: 判定値補正
|
40
|
+
生理的耐性xのOPが侵食度yに上昇した際の鉱石病判定を、ダイス数補正d、判定値補正tで行う。
|
41
|
+
ダイス数補正と判定値補正は省略可能。例えば ORP60@25 は ORP60@25+D0+T0 と同義。
|
42
|
+
また、ダイス数補正と判定値補正は逆順でも可。例えば ORP60@25+T10+D2 も可。
|
43
|
+
|
44
|
+
■ 増悪判定(--WORSENING)
|
45
|
+
症状を「末梢神経障害」「内臓機能不全」「精神症状」からランダムに選択。
|
46
|
+
継続ラウンド数を1d6+1で判定。
|
47
|
+
|
48
|
+
■ 中毒判定(--ADDICTION)
|
49
|
+
症状を「脳神経障害」「多臓器不全」「急性精神反応」からランダムに選択。
|
50
|
+
|
51
|
+
■ 判定の省略表記
|
52
|
+
nADm、nABm、nABmにおいて、
|
53
|
+
n(ダイス個数)を省略した場合、1として扱われる。
|
54
|
+
m(ダイス種類)を省略した場合、100として扱われる。
|
55
|
+
例えば、AD<=90は1AD100<=90として解釈される。
|
35
56
|
TEXT
|
36
57
|
|
37
|
-
register_prefix('
|
58
|
+
register_prefix('[-+*/\d]*AD\d*', '[-+*/\d]*AB\d*', 'ORP', '--WORSENING', '--ADDICTION')
|
59
|
+
|
60
|
+
def initialize(command)
|
61
|
+
super(command)
|
62
|
+
@sort_add_dice = true # 加算ダイスでダイス目をソートする
|
63
|
+
@sort_barabara_dice = true # バラバラダイスでダイス目をソートする
|
64
|
+
@sides_implicit_d = 100 # 1D のようにダイスの面数が指定されていない場合に100面ダイスにする
|
65
|
+
end
|
38
66
|
|
39
67
|
def eval_game_system_specific_command(command)
|
40
|
-
|
68
|
+
eval_ad(command) || eval_ab(command) || eval_orp(command) || eval_worsening(command) || eval_addiction(command)
|
41
69
|
end
|
42
70
|
|
43
71
|
private
|
44
72
|
|
45
|
-
|
46
|
-
|
73
|
+
module Status
|
74
|
+
CRITICAL = 1
|
75
|
+
SUCCESS = 2
|
76
|
+
FAILURE = 3
|
77
|
+
ERROR = 4
|
78
|
+
end
|
79
|
+
|
80
|
+
STATUS_NAME = {
|
81
|
+
Status::CRITICAL => 'クリティカル!',
|
82
|
+
Status::SUCCESS => '成功',
|
83
|
+
Status::FAILURE => '失敗',
|
84
|
+
Status::ERROR => 'エラー'
|
85
|
+
}.freeze
|
86
|
+
|
87
|
+
# クリティカル、エラー、成功失敗周りの閾値や優先関係が複雑かつルールが変動する可能性があるため、明示的にルール管理するための関数。
|
88
|
+
def check_roll(roll_result, target)
|
89
|
+
success = roll_result <= target
|
90
|
+
|
91
|
+
crierror =
|
92
|
+
if roll_result <= 10
|
93
|
+
"Critical"
|
94
|
+
elsif roll_result >= 91
|
95
|
+
"Error"
|
96
|
+
else
|
97
|
+
"Neutral"
|
98
|
+
end
|
99
|
+
|
100
|
+
result =
|
101
|
+
if success && (crierror == "Critical")
|
102
|
+
Status::CRITICAL
|
103
|
+
elsif success && (crierror == "Neutral")
|
104
|
+
Status::SUCCESS
|
105
|
+
elsif success && (crierror == "Error")
|
106
|
+
Status::SUCCESS
|
107
|
+
elsif !success && (crierror == "Critical")
|
108
|
+
Status::FAILURE
|
109
|
+
elsif !success && (crierror == "Neutral")
|
110
|
+
Status::FAILURE
|
111
|
+
elsif !success && (crierror == "Error")
|
112
|
+
Status::ERROR
|
113
|
+
end
|
114
|
+
|
115
|
+
return result
|
116
|
+
end
|
117
|
+
|
118
|
+
def eval_ad(command)
|
119
|
+
# -は文字クラスの先頭または最後に置く。
|
120
|
+
# そうしないと範囲指定子として解釈される。
|
121
|
+
m = %r{^([-+*/\d]*)AD(\d*)<=([-+*/\d]+)$}.match(command)
|
47
122
|
return nil unless m
|
48
123
|
|
49
124
|
times = m[1]
|
50
125
|
sides = m[2]
|
51
|
-
target = m[3]
|
52
|
-
times = !times.empty? ?
|
126
|
+
target = Arithmetic.eval(m[3], @round_type)
|
127
|
+
times = !times.empty? ? Arithmetic.eval(m[1], @round_type) : 1
|
53
128
|
sides = !sides.empty? ? sides.to_i : 100
|
54
|
-
|
129
|
+
|
130
|
+
roll_ad(command, times, sides, target)
|
55
131
|
end
|
56
132
|
|
57
|
-
def
|
58
|
-
m =
|
133
|
+
def eval_ab(command)
|
134
|
+
m = %r{^([-+*/\d]*)AB(\d*)<=([-+*/\d]+)(?:--([^\d\s]+)([0-2])?)?$}.match(command)
|
59
135
|
return nil unless m
|
60
136
|
|
61
137
|
times = m[1]
|
62
138
|
sides = m[2]
|
63
|
-
target = m[3]
|
139
|
+
target = Arithmetic.eval(m[3], @round_type)
|
64
140
|
type = m[4]
|
65
|
-
|
66
|
-
times = !times.empty? ?
|
141
|
+
type_status = m[5]
|
142
|
+
times = !times.empty? ? Arithmetic.eval(m[1], @round_type) : 1
|
67
143
|
sides = !sides.empty? ? sides.to_i : 100
|
144
|
+
if !type_status.nil?
|
145
|
+
type_status = type_status.to_i
|
146
|
+
elsif type == "SNIPER" # スプレッドシート版キャラシの後方互換性のために必要
|
147
|
+
type_status = 1
|
148
|
+
else
|
149
|
+
type_status = 0
|
150
|
+
end
|
68
151
|
|
69
|
-
if
|
70
|
-
|
152
|
+
if type.nil?
|
153
|
+
roll_ab(command, times, sides, target)
|
71
154
|
else
|
72
|
-
|
155
|
+
roll_ab_withtype(command, times, sides, target, type, type_status)
|
73
156
|
end
|
74
157
|
end
|
75
158
|
|
76
|
-
def
|
159
|
+
def eval_orp(command)
|
160
|
+
m = %r{^ORP(?'END'[-+*/\d]+)@(?'ORP'[-+*/\d]+)(?:\+D(?'DICE'[-+*/\d]+))?(?:\+T(?'TGT'[-+*/\d]+))?$}.match(command)
|
161
|
+
# D補正とT補正が逆順でも対応する
|
162
|
+
m ||= %r{^ORP(?'END'[-+*/\d]+)@(?'ORP'[-+*/\d]+)(?:\+T(?'TGT'[-+*/\d]+))?(?:\+D(?'DICE'[-+*/\d]+))?$}.match(command)
|
163
|
+
return nil unless m
|
164
|
+
|
165
|
+
endurance = Arithmetic.eval(m[:END], @round_type)
|
166
|
+
oripathy = Arithmetic.eval(m[:ORP], @round_type)
|
167
|
+
times_mod = !m[3].nil? ? Arithmetic.eval(m[:DICE], @round_type) : 0
|
168
|
+
target_mod = !m[4].nil? ? Arithmetic.eval(m[:TGT], @round_type) : 0
|
169
|
+
|
170
|
+
roll_orp(command, endurance, oripathy, times_mod, target_mod)
|
171
|
+
end
|
172
|
+
|
173
|
+
def roll_ad(command, times, sides, target)
|
77
174
|
dice_list = @randomizer.roll_barabara(times, sides).sort
|
78
175
|
total = dice_list.sum
|
79
|
-
success = total <= target
|
80
176
|
|
81
|
-
|
82
|
-
if total <= 10
|
83
|
-
"Critical"
|
84
|
-
elsif total >= 91
|
85
|
-
"Error"
|
86
|
-
else
|
87
|
-
"Neutral"
|
88
|
-
end
|
89
|
-
|
90
|
-
result =
|
91
|
-
if success && (crierror == "Critical")
|
92
|
-
"クリティカル!"
|
93
|
-
elsif success && (crierror == "Neutral")
|
94
|
-
"成功"
|
95
|
-
elsif success && (crierror == "Error")
|
96
|
-
"成功"
|
97
|
-
elsif !success && (crierror == "Critical")
|
98
|
-
"失敗"
|
99
|
-
elsif !success && (crierror == "Neutral")
|
100
|
-
"失敗"
|
101
|
-
elsif !success && (crierror == "Error")
|
102
|
-
"エラー"
|
103
|
-
end
|
177
|
+
result = check_roll(total, target)
|
104
178
|
|
105
179
|
if times == 1
|
106
|
-
|
180
|
+
result_text = "(#{command}) > #{dice_list.join(',')} > #{STATUS_NAME[result]}"
|
107
181
|
else
|
108
|
-
|
182
|
+
result_text = "(#{command}) > #{total}[#{dice_list.join(',')}] > #{STATUS_NAME[result]}"
|
183
|
+
end
|
184
|
+
case result
|
185
|
+
when Status::CRITICAL
|
186
|
+
Result.critical(result_text)
|
187
|
+
when Status::SUCCESS
|
188
|
+
Result.success(result_text)
|
189
|
+
when Status::FAILURE
|
190
|
+
Result.failure(result_text)
|
191
|
+
when Status::ERROR
|
192
|
+
Result.fumble(result_text)
|
109
193
|
end
|
110
194
|
end
|
111
195
|
|
112
|
-
def
|
113
|
-
dice_list
|
196
|
+
def roll_ab(command, times, sides, target)
|
197
|
+
dice_list = @randomizer.roll_barabara(times, sides).sort
|
198
|
+
|
199
|
+
success_count, critical_count, error_count = process_ab(dice_list, target)
|
114
200
|
result_count = success_count + critical_count - error_count
|
115
201
|
|
116
|
-
|
202
|
+
result_text = "(#{command}) > [#{dice_list.join(',')}] > #{success_count}+#{critical_count}C-#{error_count}E > 成功数#{result_count}"
|
203
|
+
Result.new.tap do |r|
|
204
|
+
r.text = result_text
|
205
|
+
r.condition = result_count > 0
|
206
|
+
r.critical = critical_count > 0
|
207
|
+
r.fumble = error_count > 0
|
208
|
+
end
|
117
209
|
end
|
118
210
|
|
119
|
-
def
|
120
|
-
dice_list
|
211
|
+
def roll_ab_withtype(command, times, sides, target, type, type_status)
|
212
|
+
dice_list = @randomizer.roll_barabara(times, sides).sort
|
213
|
+
|
214
|
+
success_count, critical_count, error_count = process_ab(dice_list, target)
|
121
215
|
result_count = success_count + critical_count - error_count
|
122
216
|
|
123
|
-
|
124
|
-
|
125
|
-
|
217
|
+
case type
|
218
|
+
when "SNI"
|
219
|
+
if (type_status == 0) && (result_count > 0)
|
220
|
+
result_mod = 1
|
221
|
+
else
|
222
|
+
result_mod = 0
|
223
|
+
end
|
224
|
+
when "SNIPER" # スプレッドシート版キャラシの後方互換性のため残している
|
225
|
+
if (type_status != 0) && (result_count > 0)
|
226
|
+
result_mod = 1
|
126
227
|
else
|
127
|
-
0
|
228
|
+
result_mod = 0
|
128
229
|
end
|
129
|
-
|
230
|
+
end
|
130
231
|
|
131
|
-
|
232
|
+
if !result_mod.nil?
|
233
|
+
result_count += result_mod
|
234
|
+
result_text = "(#{command}) > [#{dice_list.join(',')}] > #{success_count}+#{critical_count}C-#{error_count}E+#{result_mod}(#{type}) > 成功数#{result_count}"
|
235
|
+
else
|
236
|
+
result_text = "(#{command}) > [#{dice_list.join(',')}] > #{success_count}+#{critical_count}C-#{error_count}E > 成功数#{result_count}"
|
237
|
+
end
|
238
|
+
Result.new.tap do |r|
|
239
|
+
r.text = result_text
|
240
|
+
r.condition = result_count > 0
|
241
|
+
r.critical = critical_count > 0
|
242
|
+
r.fumble = error_count > 0
|
243
|
+
end
|
132
244
|
end
|
133
245
|
|
134
|
-
|
246
|
+
ENDURANCE_LEVEL_TABLE = [20, 40, 70, 90, Float::INFINITY].freeze # 生理的耐性の実数値から能力評価への変換テーブル
|
247
|
+
ORP_TIMES_TABLE = [1, 2, 2, 3, 4].freeze # 生理的耐性の能力評価ごとのダイス数基本値
|
248
|
+
def roll_orp(command, endurance, oripathy, times_mod, target_mod)
|
249
|
+
sides = 100
|
250
|
+
|
251
|
+
endurance_level = ENDURANCE_LEVEL_TABLE.find_index { |n| endurance <= n }
|
252
|
+
original_times = ORP_TIMES_TABLE[endurance_level]
|
253
|
+
times = original_times + times_mod
|
254
|
+
|
255
|
+
if oripathy <= 20
|
256
|
+
return Result.new("(#{command}) > 鉱石病判定が発生しない侵食度です。侵食度は21以上を指定してください。")
|
257
|
+
end
|
258
|
+
|
259
|
+
oripathy_stage = (oripathy / 20.0).ceil - 1
|
260
|
+
original_target = (80 - oripathy_stage * 20) - (oripathy - oripathy_stage * 20) * 5
|
261
|
+
target = original_target + target_mod
|
262
|
+
dice_and_target_text = "ダイス数#{original_times}" +
|
263
|
+
(times_mod > 0 ? "+#{times_mod}" : "") +
|
264
|
+
"、判定値#{original_target}" +
|
265
|
+
(target_mod > 0 ? "+#{target_mod}" : "")
|
266
|
+
result_texts = ["(#{command})", dice_and_target_text, "#{times}B100<=#{target}"]
|
267
|
+
|
268
|
+
if target <= 0
|
269
|
+
result_texts += ["自動失敗!"]
|
270
|
+
return Result.failure(result_texts.join(" > "))
|
271
|
+
end
|
272
|
+
|
273
|
+
# 複数振ったダイスのうち1つでも判定値を下回れば成功なので、最も出目の小さいダイスのみを確認すればよい。
|
274
|
+
# dice_listをソートした上で、dice_list[0]が最小の出目。
|
135
275
|
dice_list = @randomizer.roll_barabara(times, sides).sort
|
276
|
+
success_count = dice_list.count { |n| n <= target }
|
277
|
+
if success_count > 0
|
278
|
+
result_texts += ["[#{dice_list.join(',')}]", "成功数#{success_count}", "成功"]
|
279
|
+
Result.success(result_texts.join(" > "))
|
280
|
+
else
|
281
|
+
result_texts += ["[#{dice_list.join(',')}]", "成功数#{success_count}", "失敗"]
|
282
|
+
Result.failure(result_texts.join(" > "))
|
283
|
+
end
|
284
|
+
end
|
136
285
|
|
286
|
+
def process_ab(dice_list, target)
|
137
287
|
success_count = 0
|
138
288
|
critical_count = 0
|
139
289
|
error_count = 0
|
140
290
|
|
141
291
|
dice_list.each do |value|
|
142
|
-
|
143
|
-
|
144
|
-
|
292
|
+
case check_roll(value, target)
|
293
|
+
when Status::CRITICAL
|
294
|
+
critical_count += 1
|
295
|
+
success_count += 1
|
296
|
+
when Status::SUCCESS
|
297
|
+
success_count += 1
|
298
|
+
when Status::FAILURE
|
299
|
+
# Nothing to do
|
300
|
+
when Status::ERROR
|
301
|
+
error_count += 1
|
302
|
+
end
|
145
303
|
end
|
146
304
|
|
147
|
-
return [
|
148
|
-
end
|
149
|
-
|
150
|
-
ADDICTION_TABLE = [
|
151
|
-
"中枢神経障害",
|
152
|
-
"多臓器不全",
|
153
|
-
"急性ストレス反応",
|
154
|
-
].freeze
|
155
|
-
|
156
|
-
def roll_addiction(command)
|
157
|
-
return nil if command != "--ADDICTION"
|
158
|
-
|
159
|
-
value = @randomizer.roll_once(3)
|
160
|
-
chosen = ADDICTION_TABLE[value - 1]
|
161
|
-
|
162
|
-
return "--ADDICTION > #{chosen}"
|
305
|
+
return [success_count, critical_count, error_count]
|
163
306
|
end
|
164
307
|
|
165
308
|
WORSENING_TABLE = [
|
@@ -168,7 +311,7 @@ module BCDice
|
|
168
311
|
"精神症状",
|
169
312
|
].freeze
|
170
313
|
|
171
|
-
def
|
314
|
+
def eval_worsening(command)
|
172
315
|
return nil if command != "--WORSENING"
|
173
316
|
|
174
317
|
value = @randomizer.roll_once(3)
|
@@ -177,6 +320,21 @@ module BCDice
|
|
177
320
|
|
178
321
|
return "--WORSENING > #{chosen}: #{elapse} rounds"
|
179
322
|
end
|
323
|
+
|
324
|
+
ADDICTION_TABLE = [
|
325
|
+
"脳神経障害",
|
326
|
+
"多臓器不全",
|
327
|
+
"急性精神症状",
|
328
|
+
].freeze
|
329
|
+
|
330
|
+
def eval_addiction(command)
|
331
|
+
return nil if command != "--ADDICTION"
|
332
|
+
|
333
|
+
value = @randomizer.roll_once(3)
|
334
|
+
chosen = ADDICTION_TABLE[value - 1]
|
335
|
+
|
336
|
+
return "--ADDICTION > #{chosen}"
|
337
|
+
end
|
180
338
|
end
|
181
339
|
end
|
182
340
|
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bcdice/dice_table/range_table'
|
4
|
+
|
5
|
+
module BCDice
|
6
|
+
module GameSystem
|
7
|
+
class BlackJacket < Base
|
8
|
+
# ゲームシステムの識別子
|
9
|
+
ID = 'BlackJacket'
|
10
|
+
|
11
|
+
# ゲームシステム名
|
12
|
+
NAME = 'ブラックジャケットRPG'
|
13
|
+
|
14
|
+
# ゲームシステム名の読みがな
|
15
|
+
SORT_KEY = 'ふらつくしあけつとRPG'
|
16
|
+
|
17
|
+
# ダイスボットの使い方
|
18
|
+
HELP_MESSAGE = <<~INFO_MESSAGE_TEXT
|
19
|
+
・行為判定(BJx)
|
20
|
+
x:成功率
|
21
|
+
例)BJ80
|
22
|
+
クリティカル、ファンブルの自動的判定を行います。
|
23
|
+
「BJ50+20-30」のように加減算記述も可能。
|
24
|
+
成功率は上限100%、下限0%
|
25
|
+
・デスチャート(DCxY)
|
26
|
+
x:チャートの種類。肉体:DCL、精神:DCS、環境:DCC
|
27
|
+
Y=マイナス値
|
28
|
+
例)DCL5:ライフが -5 の判定
|
29
|
+
DCS3:サニティーが -3 の判定
|
30
|
+
DCC0:クレジット 0 の判定
|
31
|
+
・チャレンジ・ペナルティ・チャート(CPC)
|
32
|
+
・サイドトラック・チャート(STC)
|
33
|
+
INFO_MESSAGE_TEXT
|
34
|
+
|
35
|
+
def eval_game_system_specific_command(command)
|
36
|
+
resolute_action(command) || roll_death_chart(command) || roll_tables(command, TABLES)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def resolute_action(command)
|
42
|
+
m = /^BJ(\d+([+-]\d+)*)$/.match(command)
|
43
|
+
unless m
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
|
47
|
+
success_rate = ArithmeticEvaluator.eval(m[1])
|
48
|
+
|
49
|
+
roll_result, dice10, dice01 = roll_d100
|
50
|
+
roll_result_text = format('%02d', roll_result)
|
51
|
+
|
52
|
+
result = action_result(roll_result, dice10, dice01, success_rate)
|
53
|
+
|
54
|
+
sequence = [
|
55
|
+
"行為判定(成功率:#{success_rate}%)",
|
56
|
+
"1D100[#{dice10},#{dice01}]=#{roll_result_text}",
|
57
|
+
roll_result_text.to_s,
|
58
|
+
result.text
|
59
|
+
]
|
60
|
+
|
61
|
+
result.text = sequence.join(" > ")
|
62
|
+
result
|
63
|
+
end
|
64
|
+
|
65
|
+
SUCCESS_STR = "成功"
|
66
|
+
FAILURE_STR = "失敗"
|
67
|
+
CRITICAL_STR = (SUCCESS_STR + " > クリティカル! パワーの代償1/2").freeze
|
68
|
+
FUMBLE_STR = (FAILURE_STR + " > ファンブル! パワーの代償2倍&振り直し不可").freeze
|
69
|
+
MISERY_STR = (FAILURE_STR + " > ミザリー! パワーの代償2倍&振り直し不可").freeze
|
70
|
+
|
71
|
+
def action_result(total, tens, ones, success_rate)
|
72
|
+
if total == 100
|
73
|
+
Result.fumble(MISERY_STR)
|
74
|
+
elsif success_rate <= 0
|
75
|
+
Result.fumble(FUMBLE_STR)
|
76
|
+
elsif total <= success_rate - 100
|
77
|
+
Result.critical(CRITICAL_STR)
|
78
|
+
elsif tens == ones
|
79
|
+
if total <= success_rate
|
80
|
+
Result.critical(CRITICAL_STR)
|
81
|
+
else
|
82
|
+
Result.fumble(FUMBLE_STR)
|
83
|
+
end
|
84
|
+
elsif total <= success_rate
|
85
|
+
Result.success(SUCCESS_STR)
|
86
|
+
else
|
87
|
+
Result.failure(FAILURE_STR)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def roll_d100
|
92
|
+
dice10 = @randomizer.roll_once(10)
|
93
|
+
dice10 = 0 if dice10 == 10
|
94
|
+
dice01 = @randomizer.roll_once(10)
|
95
|
+
dice01 = 0 if dice01 == 10
|
96
|
+
|
97
|
+
roll_result = dice10 * 10 + dice01
|
98
|
+
roll_result = 100 if roll_result == 0
|
99
|
+
|
100
|
+
return roll_result, dice10, dice01
|
101
|
+
end
|
102
|
+
|
103
|
+
class DeathChart
|
104
|
+
def initialize(name, chart)
|
105
|
+
@name = name
|
106
|
+
@chart = chart.freeze
|
107
|
+
|
108
|
+
if @chart.size != 11
|
109
|
+
raise ArgumentError, "unexpected chart size #{name.inspect} (given #{@chart.size}, expected 11)"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# @param randomizer [Randomizer]
|
114
|
+
# @param minus_score [Integer]
|
115
|
+
# @return [String]
|
116
|
+
def roll(randomizer, minus_score)
|
117
|
+
dice = randomizer.roll_once(10)
|
118
|
+
key_number = dice + minus_score
|
119
|
+
|
120
|
+
key_text, chosen = at(key_number)
|
121
|
+
|
122
|
+
return "デスチャート(#{@name})[マイナス値:#{minus_score} + 1D10(->#{dice}) = #{key_number}] > #{key_text} : #{chosen}"
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
# key_numberの10から20がindexの0から10に対応する
|
128
|
+
def at(key_number)
|
129
|
+
if key_number < 10
|
130
|
+
["10以下", @chart.first]
|
131
|
+
elsif key_number > 20
|
132
|
+
["20以上", @chart.last]
|
133
|
+
else
|
134
|
+
[key_number.to_s, @chart[key_number - 10]]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def roll_death_chart(command)
|
140
|
+
m = /^DC([LSC])(\d+)$/i.match(command)
|
141
|
+
unless m
|
142
|
+
return m
|
143
|
+
end
|
144
|
+
|
145
|
+
chart = DEATH_CHARTS[m[1]]
|
146
|
+
minus_score = m[2].to_i
|
147
|
+
|
148
|
+
return chart.roll(@randomizer, minus_score)
|
149
|
+
end
|
150
|
+
|
151
|
+
DEATH_CHARTS = {
|
152
|
+
'L' => DeathChart.new(
|
153
|
+
'肉体',
|
154
|
+
[
|
155
|
+
"何も無し。キミは奇跡的に一命を取り留めた。闘いは続く。",
|
156
|
+
"激痛が走る。以後、イベント終了時まで、全ての判定の成功率-10%。",
|
157
|
+
"もう、体が動かない……。キミは[硬直2]を受ける。",
|
158
|
+
"渾身の一撃!! キミは〈生存〉判定を行なう。失敗した場合、[死亡]する。",
|
159
|
+
"突然、目の前が真っ暗になった。キミは[気絶2]を受ける。",
|
160
|
+
"以後、イベント終了時まで、全ての判定の成功率-20%。",
|
161
|
+
"記録的一撃!! キミは〈生存〉-20%の判定を行なう。失敗した場合、[死亡]する。",
|
162
|
+
"生きているのか死んでいるのか。キミは[瀕死2]を受ける。",
|
163
|
+
"叙事詩的一撃!! キミは〈生存〉-30%の判定を行なう。失敗した場合、[死亡]する。",
|
164
|
+
"以後、イベント終了時まで、全ての判定の成功率-30%。",
|
165
|
+
"神話的一撃!! キミは宙を舞って三回転ほどした後、地面に叩きつけられる。見るも無惨な姿。肉体は原型を留めていない(キミは[死亡]した)。",
|
166
|
+
]
|
167
|
+
),
|
168
|
+
'S' => DeathChart.new(
|
169
|
+
'精神',
|
170
|
+
[
|
171
|
+
"何も無し。キミは歯を食いしばってストレスに耐えた。",
|
172
|
+
"以後、イベント終了時まで、全ての判定の成功率-10%。",
|
173
|
+
"云い知れぬ恐怖がキミを襲う。キミは[恐怖2]を受ける。",
|
174
|
+
"とても傷ついた。キミは〈意思〉判定を行なう。失敗した場合、[絶望]してNPCとなる。",
|
175
|
+
"キミは意識を失った。キミは[気絶2]を受ける。",
|
176
|
+
"以後、イベント終了時まで、全ての判定の成功率-20%。",
|
177
|
+
"信じる者にだまされたような痛み。キミは〈意思〉-20%の判定を行なう。失敗した場合、[絶望]してNPCとなる。",
|
178
|
+
"仲間に裏切られたのかも知れない。キミは[混乱2]を受ける。",
|
179
|
+
"あまりに残酷な現実。キミは〈意思〉-30%の判定を行なう。失敗した場合、[絶望]してNPCとなる。",
|
180
|
+
"以後、イベント終了時まで、全ての判定の成功率-30%。",
|
181
|
+
"宇宙開闢の理に触れるも、それは人類の認識限界を超える何かであった。キミは[絶望]し、以後NPCとなる。",
|
182
|
+
]
|
183
|
+
),
|
184
|
+
'C' => DeathChart.new(
|
185
|
+
'環境',
|
186
|
+
[
|
187
|
+
"何も無し。キミは黒い噂を握りつぶした。",
|
188
|
+
"以後、イベント終了時まで、全ての判定の成功率-10%。",
|
189
|
+
"ピンチ! 以後、ラウンド終了時まで、キミはカルマを使用できない。",
|
190
|
+
"悪い噂が流れる。キミは〈交渉〉判定を行なう。失敗した場合、キミは仲間からの信頼を失って[無縁]され、NPCとなる。",
|
191
|
+
"以後、イベント終了時まで、代償にクレジットを消費するパワーを使用できない。",
|
192
|
+
"キミの悪評が世間に知れ渡る。協力者からの支援が打ち切られる。以後、シナリオ終了時まで、全ての判定の成功率-20%。",
|
193
|
+
"裏切り!! キミは〈経済〉-20%の判定を行なう。失敗した場合、キミは周囲からの信頼を失い、[無縁]され、NPCとなる。",
|
194
|
+
"以後、シナリオ終了時まで、【環境】系の技能のレベルがすべて0となる。",
|
195
|
+
"捏造報道? 身に覚えのない背信行為がスクープとして報道される。キミは〈心理〉-30%の判定を行なう。失敗した場合、キミは人としての尊厳を失い、[無縁]を受ける。",
|
196
|
+
"以後、イベント終了時まで、全ての判定の成功率-30%。",
|
197
|
+
"キミの名は史上最悪の汚点として歴史に刻まれる。もはらキミを信じる仲間はなく、キミを助ける社会もない。キミは[無縁]され、以後NPCとなる。",
|
198
|
+
]
|
199
|
+
)
|
200
|
+
}.freeze
|
201
|
+
|
202
|
+
TABLES = {
|
203
|
+
"CPC" => DiceTable::Table.new(
|
204
|
+
"チャレンジ・ペナルティ・チャート",
|
205
|
+
"1D10",
|
206
|
+
[
|
207
|
+
"逝去\n助けるべきNPC(ヒロインなど)が死亡する。",
|
208
|
+
"黒星\n敵が目的を成就し、事件はPCの敗北で終了する。そのまま余韻フェイズへ。",
|
209
|
+
"活性\n敵のボスのライフを2倍にしたうえで決戦フェイズを開始する。",
|
210
|
+
"攻勢\n敵ボスの与ダメージに+2D6の修正を与えたうえで決戦フェイズを開始する。",
|
211
|
+
"大挙\n敵の数(ボス以外)を2倍にしたうえで決戦フェイズを開始する。",
|
212
|
+
"暗黒\nすべてのエリアを[暗闇]にしたうえで決戦フェイズを開始する。",
|
213
|
+
"猛火\n2つの戦場エリアを[ダメージゾーン2]にして、決戦フェイズを開始する。",
|
214
|
+
"伏兵\n敵の半分をエリア1とエリア2に移動させた状態で決戦フェイズを開始する。",
|
215
|
+
"満腹\nボス以外の敵のライフをすべて2倍にしたうえで決戦フェイズを開始する。",
|
216
|
+
"封印\n決戦フェイズの間、PCはカルマを使用できない。決戦フェイズを開始する。"
|
217
|
+
]
|
218
|
+
),
|
219
|
+
"STC" => DiceTable::Table.new(
|
220
|
+
"サイドトラック・チャート",
|
221
|
+
"1D10",
|
222
|
+
[
|
223
|
+
"邂逅\n偶然、NPCと出会う。どのNPCが現れるかはGMが決定すること。",
|
224
|
+
"事故\n交通事故に出くわす。周囲ではパニックが起きているかも知れない。",
|
225
|
+
"午睡\n強烈な睡魔に襲われる。まさか、新手のヴィランの能力か?",
|
226
|
+
"告白\nNPCのひとりから、今まで秘めていた思いを吐露される。",
|
227
|
+
"設定\n新たな設定が明かされる。実はNPCの父だったとか、生来目が見えん、とか。",
|
228
|
+
"刺客\n何者かから攻撃を受ける。第3勢力か?",
|
229
|
+
"会敵\n偶然、仇敵のひとりと出くわす。追うべきか? 無視すべきか?",
|
230
|
+
"不審\n怪しい人物を見かける。追うべきか? 無視すべきか?",
|
231
|
+
"遭遇\nシナリオと関係のないヴィラン組織と遭遇する。",
|
232
|
+
"平和\n特に何も起きなかった。",
|
233
|
+
]
|
234
|
+
),
|
235
|
+
}.freeze
|
236
|
+
|
237
|
+
register_prefix(
|
238
|
+
'BJ',
|
239
|
+
'DC[LSC]',
|
240
|
+
TABLES.keys
|
241
|
+
)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|