bcdice 3.3.0 → 3.4.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.
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class Irisbane < Base
6
+ # ゲームシステムの識別子
7
+ ID = 'Irisbane'
8
+
9
+ # ゲームシステム名
10
+ NAME = '瞳逸らさぬイリスベイン'
11
+
12
+ # ゲームシステム名の読みがな
13
+ SORT_KEY = 'ひとみそらさぬいりすへいん'
14
+
15
+ # ダイスボットの使い方
16
+ HELP_MESSAGE = <<~HELP
17
+ ■攻撃判定( ATTACKx@y<=z )
18
+ x: 攻撃力
19
+ y: 判定数
20
+ z: 目標値
21
+ (※ ATTACK は ATK または AT と簡略化可能)
22
+ 例) ATTACK2@3<=5
23
+ 例) ATK10@2<=4
24
+ 例) AT8@3<=2
25
+
26
+ 上記 x y z にはそれぞれ四則演算を指定可能。
27
+ 例) ATTACK2+7@3*2<=5-1
28
+
29
+ □攻撃判定のダメージ増減( ATTACKx@y<=z[+a] ATTACKx@y<=z[-a])
30
+ 末尾に [+a] または [-a] と指定すると、最終的なダメージを増減できる。
31
+ a: 増減量
32
+ 例) ATTACK2@3<=5[+10]
33
+ 例) ATK10@2<=4[-8]
34
+ 例) AT8@3<=2[-8+5]
35
+
36
+ ■シチュエーション(p115)
37
+ SceneSituation, SSi
38
+ HELP
39
+
40
+ ATTACK_ROLL_REG = %r{^AT(TACK|K)?([+\-*/()\d]+)@([+\-*/()\d]+)<=([+\-*/()\d]+)(\[([+\-])([+\-*/()\d]+)\])?}i.freeze
41
+ register_prefix('AT(TACK|K)?')
42
+
43
+ def initialize(command)
44
+ super(command)
45
+
46
+ @sort_barabara_dice = true
47
+ @round_type = RoundType::CEIL
48
+ end
49
+
50
+ def eval_game_system_specific_command(command)
51
+ command = ALIAS[command] || command
52
+
53
+ if (m = ATTACK_ROLL_REG.match(command))
54
+ roll_attack(m[2], m[3], m[4], m[6], m[7])
55
+ else
56
+ roll_tables(command, TABLES)
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def roll_attack(power_expression, dice_count_expression, border_expression, modification_operator, modification_expression)
63
+ power = Arithmetic.eval(power_expression, RoundType::CEIL)
64
+ dice_count = Arithmetic.eval(dice_count_expression, RoundType::CEIL)
65
+ border = Arithmetic.eval(border_expression, RoundType::CEIL)
66
+ modification_value = modification_expression.nil? ? nil : Arithmetic.eval(modification_expression, RoundType::CEIL)
67
+ return if power.nil? || dice_count.nil? || border.nil?
68
+ return if modification_operator && modification_value.nil?
69
+
70
+ power = 0 if power < 0
71
+ border = border.clamp(1, 6)
72
+
73
+ command = make_command_text(power, dice_count, border, modification_operator, modification_value)
74
+
75
+ if dice_count <= 0
76
+ return "#{command} > 判定数が 0 です"
77
+ end
78
+
79
+ dices = @randomizer.roll_barabara(dice_count, 6).sort
80
+
81
+ success_dice_count = dices.count { |dice| dice <= border }
82
+ damage = success_dice_count * power
83
+
84
+ message_elements = []
85
+ message_elements << command
86
+ message_elements << dices.join(',')
87
+ message_elements << "成功ダイス数 #{success_dice_count}"
88
+ message_elements << "× 攻撃力 #{power}" if success_dice_count > 0
89
+
90
+ if success_dice_count > 0
91
+ if modification_operator && modification_value
92
+ message_elements << "ダメージ #{damage}#{modification_operator}#{modification_value}"
93
+ damage = parse_operator(modification_operator).call(damage, modification_value)
94
+ damage = 0 if damage < 0
95
+ message_elements << damage.to_s
96
+ else
97
+ message_elements << "ダメージ #{damage}"
98
+ end
99
+ end
100
+
101
+ Result.new(message_elements.join(' > ')).tap do |r|
102
+ r.condition = success_dice_count > 0
103
+ end
104
+ end
105
+
106
+ def make_command_text(power, dice_count, border, modification_operator, modification_value)
107
+ text = "(ATTACK#{power}@#{dice_count}<=#{border}"
108
+ text += "[#{modification_operator}#{modification_value}]" if modification_operator
109
+ text += ")"
110
+ text
111
+ end
112
+
113
+ def parse_operator(operator)
114
+ case operator
115
+ when '+'
116
+ lambda { |x, y| x + y }
117
+ when '-'
118
+ lambda { |x, y| x - y }
119
+ end
120
+ end
121
+
122
+ TABLES = {
123
+ "SceneSituation" => DiceTable::D66LeftRangeTable.new(
124
+ "シチュエーション",
125
+ BCDice::D66SortType::NO_SORT,
126
+ [
127
+ [1..3, [
128
+ "【日常】何一つ変わることの無い日々の一幕。移ろい易い世界では、それはとても大切である。",
129
+ "【準備】何かを為すための用意をする一幕。情報収集、買物遠征、やるべきことは一杯だ。",
130
+ "【趣味】自分の時間を、有効活用している一幕。必要に追われていない分、心は軽く晴れやかだ。",
131
+ "【喫茶】一息入れ、嗜好品を嗜む時の一幕。穏やかな空気は、だが、往々にして変わりやすい。",
132
+ "【鍛錬】体を鍛え、心を養う修練の一幕。己さえ良ければ、その方法も何だって良い。",
133
+ "【職務】役割の元、仕事に精を出す時の一幕。目的が何であれ、為すべきことに変わりはない。",
134
+ ]],
135
+ [4..6, [
136
+ "【移動】何処かから何処かへと向かう一幕。進んでいるなら、手段も目的地も関係あるまい。",
137
+ "【墓前】故人が眠る場所へと赴く一幕。共に眠ることだけは無いように。",
138
+ "【操作】何かを操り、望みを果たしている一幕。運転にせよ何にせよ、脇見には注意が必要だ。",
139
+ "【食事】何かを糧とし、己の力を蓄える一幕。行動すれば消耗する。腹が減っては何とやらだ。",
140
+ "【休息】日々の合間の、憩いの一幕。“何もしない”というのも、立派な行いである。",
141
+ "【夢幻】現実に存在しない何かへと耽る一幕。時間帯に関わらず、何時かは必ず覚めるだろう。",
142
+ ]],
143
+ ]
144
+ ),
145
+ }.transform_keys(&:upcase).freeze
146
+
147
+ ALIAS = {
148
+ "SSi" => "SceneSituation",
149
+ }.transform_keys(&:upcase).transform_values(&:upcase).freeze
150
+
151
+ register_prefix(TABLES.keys, ALIAS.keys)
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class PersonaO < Base
6
+ # ゲームシステムのの識別子
7
+ ID = 'PersonaO'
8
+
9
+ # ゲームシステム名
10
+ NAME = 'ペルソナTRPG-O'
11
+
12
+ # ゲームシステム名の読みがな
13
+ SORT_KEY = 'へるそなTRPGO'
14
+
15
+ # ダイスボットの使い方
16
+ HELP_MESSAGE = <<~INFO_MESSAGE_TEXT
17
+ ・基本判定
18
+  PTx@y x:目標値、y:クリティカル値(省略時は5)
19
+  例)PT60 PT90@10
20
+
21
+ ・ダメージ計算
22
+  nPD+(x+y*2)%(z-a)-b n:ダイス個数、x:スキル固定値、y:ボーナス、z:バフ倍率、a:耐性、b:敵防御力
23
+  nPD+(x+y*2)までがスキルによる素のダメージ、zおよびaは計算式を入れてよい。
24
+  
25
+  例)ソニックパンチ、力B2点、
26
+    タルカジャがかかっており、打撃耐性あり、
27
+    目標の物理防御力は2点
28
+    
29
+    2PD+(20+2*2)%(100+50-50)-2
30
+ INFO_MESSAGE_TEXT
31
+
32
+ register_prefix(
33
+ 'PT',
34
+ '\d+PD'
35
+ )
36
+
37
+ def eval_game_system_specific_command(command)
38
+ roll_attack(command) || roll_damage(command)
39
+ end
40
+
41
+ private
42
+
43
+ def roll_attack(command)
44
+ m = /^PT(-?\d+)?(@(-?\d+))?$/i.match(command)
45
+ unless m
46
+ return nil
47
+ end
48
+
49
+ success_rate = m[1].to_i
50
+ critical_border = m[3]&.to_i || 5
51
+
52
+ dice_value = @randomizer.roll_once(100)
53
+ result =
54
+ if dice_value <= critical_border
55
+ Result.critical("クリティカル")
56
+ elsif dice_value <= success_rate
57
+ Result.success("成功")
58
+ else
59
+ Result.failure("失敗")
60
+ end
61
+
62
+ result.text = "D100<=#{success_rate}@#{critical_border} > #{dice_value} > #{result.text}"
63
+ return result
64
+ end
65
+
66
+ def roll_damage(command)
67
+ m = /^(\d+)PD\+(-?\d+)%(-?\d+)-(\d+)$/i.match(command)
68
+ unless m
69
+ return nil
70
+ end
71
+
72
+ dice = m[1].to_i
73
+ kotei = m[2].to_i
74
+ hosei = m[3].to_i
75
+ bougyo = m[4].to_i
76
+
77
+ dice_list = @randomizer.roll_barabara(dice, 10)
78
+ dice_sum = dice_list.sum
79
+
80
+ dmg = dice_sum + (hosei * kotei / 100.0).to_i - bougyo
81
+
82
+ return "#{dice}D10+#{kotei}*#{hosei}%-#{bougyo} > [#{dice_list.join(',')}]+#{kotei}*#{hosei}%-#{bougyo} > #{dmg} ダメージ!"
83
+ end
84
+ end
85
+ end
86
+ end
@@ -50,8 +50,8 @@ module BCDice
50
50
  private
51
51
 
52
52
  def roll_attack(dice_count_expression, border_expression, additional_damage_rules)
53
- dice_count = Arithmetic.eval(dice_count_expression, round_type: RoundType::FLOOR)
54
- border = Arithmetic.eval(border_expression, round_type: RoundType::FLOOR).clamp(1, 6) if border_expression
53
+ dice_count = Arithmetic.eval(dice_count_expression, RoundType::FLOOR)
54
+ border = Arithmetic.eval(border_expression, RoundType::FLOOR).clamp(1, 6) if border_expression
55
55
 
56
56
  command = make_command_text(dice_count, border, additional_damage_rules)
57
57
 
@@ -21,7 +21,7 @@ module BCDice
21
21
  ・表
22
22
  ・プロフ(Profile):年齢表 PAT, 性別表 PST, 国籍表 PCT
23
23
  ・名前表(Name):イギリス NIT, アメリカ NAT, フランス NFT, ドイツ NGT, ソビエト NST, 日本表 NJT
24
- ・過去表(Past):トゥルース PTT, ガーディアン PGT, ノーブル PNT, デヴォート PDT, エセティック PET, チャレンジ PCT
24
+ ・過去表(Past):トゥルース PTT, ガーディアン PGT, ノーブル PNT, デヴォート PDT, エセティック PET, チャレンジ PACT
25
25
  ・経緯表(Background):ギガントアームズ BGT, アーマードリム BAT, エッジモービル BET, クロノウェポン BCT, スパイテック BST, スチームウェア BWT
26
26
  ・特徴表(Feature):特徴表S FST, 特徴表P FPT
27
27
  ・関係性表(Relationship):関係性表S RST, 関係性表P RPT
@@ -276,6 +276,19 @@ module BCDice
276
276
  "【報復】 あなたの恩師は過去に蒸気事件で犠牲となっている。あなたはいつか、その犯人に報復するつもりだ。"
277
277
  ]
278
278
  ),
279
+ "PACT" => SPTable.new(
280
+ "過去表『チャレンジ』",
281
+ [
282
+ "【誓い】 とある陰謀により、あなたは大切な何かを失った。いつか陰謀を暴く……それがあなたの誓いだ。",
283
+ "【研究】 あなたは蒸気犯罪で用いられるスチームギアを撲滅するために、常に研究を続けている。",
284
+ "【反抗】 あなたは夢や努力を嘲笑されて育った。そのせいか、人々を虐げる蒸気事件が我慢ならない。",
285
+ "【克服】 あなたは恐ろしい蒸気事件に遭遇した過去がある。その恐怖を克服するため、蒸気事件に挑むのだ。",
286
+ "【修練】 あなたは修練により、己を高める術を知っている。そして修練のため、蒸気事件に挑むのだ。",
287
+ "【挑戦】 あなたは幼少時から困難に立ち向かい続けてきた。ゆえに、蒸気事件であろうと立ち向かうのだ。",
288
+ "【決着】 あなたは怨敵である蒸気事件の犯人を追っている。いつの日か、そいつとは決着をつけるつもりだ。",
289
+ "【異質】 あなたは困難に立ち向かうことでしか高揚できない。そして、蒸気事件以上の困難など存在しないのだ。"
290
+ ]
291
+ ),
279
292
  "BGT" => SPTable.new(
280
293
  "経緯表『ギガントアームズ』",
281
294
  [
@@ -79,7 +79,7 @@ module BCDice
79
79
  if (table = self.class::TABLES[command])
80
80
  table.roll(@randomizer)
81
81
  elsif (m = %r{([()+/\d]+)SK(\d)?((,\d>\d)+)?}.match(command))
82
- num_dices = Arithmetic.eval(m[1], round_type: RoundType::FLOOR)
82
+ num_dices = Arithmetic.eval(m[1], RoundType::FLOOR)
83
83
 
84
84
  unless num_dices.nil?
85
85
  resolute_action(num_dices, m[2] && m[2].to_i, m[3])
@@ -6,9 +6,9 @@ module BCDice
6
6
  # ゲームシステムの識別子
7
7
  ID = 'TorgEternity'
8
8
  # ゲームシステム名
9
- NAME = 'TORG Eternity'
9
+ NAME = 'トーグ エタニティ'
10
10
  # ゲームシステム名の読みがな
11
- SORT_KEY = 'とおくえたあにてい'
11
+ SORT_KEY = 'とおくえたにてい'
12
12
  # ダイスボットの使い方
13
13
  HELP_MESSAGE = <<~INFO_MESSAGE_TEXT
14
14
  ・判定
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module GameSystem
5
+ class ToshiakiHolyGrailWar < Base
6
+ # ゲームシステムの識別子
7
+ ID = 'ToshiakiHolyGrailWar'
8
+ # ゲームシステム名
9
+ NAME = 'としあきの聖杯戦争TRPG'
10
+ # ゲームシステム名の読みがな
11
+ SORT_KEY = 'としあきのせいはいせんそうTRPG'
12
+ # ダイスボットの使い方
13
+ HELP_MESSAGE = <<~INFO_MESSAGE_TEXT
14
+ ■ 判定 (Fx+y-z@a>=t)
15
+ 補正値ペナルティを自動計算してダイスの面数を決定しダイスロールを実行します。
16
+ ダイス面数は2以上、10以下の範囲に制限されます。
17
+ x: ステータス
18
+ y: 補正値 (任意)
19
+ z: マイナス補正値 (任意)
20
+ a: ダイス面数の増量 (任意)
21
+ t: 目標値 (任意)
22
+ 例)
23
+ F8+11, F8+11-5, F8+11-5@1, F8+11+9-3-2@-1, F8+11-5>=50, F8
24
+ INFO_MESSAGE_TEXT
25
+
26
+ register_prefix('F')
27
+
28
+ def eval_game_system_specific_command(command)
29
+ roll_f(command)
30
+ end
31
+
32
+ private
33
+
34
+ def roll_f(command)
35
+ parser = Command::Parser.new(/F(\d+)(\+\d+)*(-\d+)*/, round_type: RoundType::CEIL)
36
+ .disable_modifier
37
+ .enable_critical
38
+ cmd = parser.parse(command)
39
+ unless cmd
40
+ return nil
41
+ end
42
+
43
+ m = cmd.command.match(/^F(\d+)((?:\+\d+)+)?((?:-\d+)+)?$/)
44
+ unless m
45
+ return nil
46
+ end
47
+
48
+ status = m[1].to_i
49
+ positive_modifier = m[2] ? Arithmetic.eval(m[2], RoundType::CEIL) : 0
50
+ negative_modifier = m[3] ? Arithmetic.eval(m[3], RoundType::CEIL) : 0
51
+ side_bonus = cmd.critical || 0
52
+
53
+ times = [status + positive_modifier + negative_modifier, 0].max
54
+ sides = (6 - positive_modifier_penalty(positive_modifier) + negative_modifier_bonus(negative_modifier) + side_bonus).clamp(2, 10)
55
+
56
+ list = @randomizer.roll_barabara(times, sides)
57
+ total = list.sum()
58
+ result =
59
+ if cmd.cmp_op.nil?
60
+ Result.new
61
+ elsif total.send(cmd.cmp_op, cmd.target_number)
62
+ Result.success("成功")
63
+ else
64
+ Result.failure("失敗")
65
+ end
66
+
67
+ sequence = [
68
+ cmd,
69
+ "(#{times}D#{sides}#{cmd.cmp_op}#{cmd.target_number})",
70
+ "#{total}[#{list.join(',')}]",
71
+ total,
72
+ result.text,
73
+ ].compact
74
+
75
+ result.text = sequence.join(" > ")
76
+ return result
77
+ end
78
+
79
+ def positive_modifier_penalty(modifier)
80
+ if modifier <= 10
81
+ 0
82
+ else
83
+ modifier / 10
84
+ end
85
+ end
86
+
87
+ def negative_modifier_bonus(modifier)
88
+ modifier <= -5 ? 1 : 0
89
+ end
90
+ end
91
+ end
92
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BCDice
4
- VERSION = "3.3.0"
4
+ VERSION = "3.4.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bcdice
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.0
4
+ version: 3.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SAKATA Sinji
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-26 00:00:00.000000000 Z
11
+ date: 2021-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -38,6 +38,7 @@ files:
38
38
  - i18n/Amadeus/ko_kr.yml
39
39
  - i18n/Cthulhu/ja_jp.yml
40
40
  - i18n/Cthulhu/ko_kr.yml
41
+ - i18n/Cthulhu/zh_hans.yml
41
42
  - i18n/Cthulhu/zh_hant.yml
42
43
  - i18n/DetatokoSaga/ja_jp.yml
43
44
  - i18n/DetatokoSaga/ko_kr.yml
@@ -109,6 +110,7 @@ files:
109
110
  - lib/bcdice/dice_table/chain_table.rb
110
111
  - lib/bcdice/dice_table/d66_grid_table.rb
111
112
  - lib/bcdice/dice_table/d66_half_grid_table.rb
113
+ - lib/bcdice/dice_table/d66_left_range_table.rb
112
114
  - lib/bcdice/dice_table/d66_one_third_table.rb
113
115
  - lib/bcdice/dice_table/d66_parity_table.rb
114
116
  - lib/bcdice/dice_table/d66_range_table.rb
@@ -163,6 +165,7 @@ files:
163
165
  - lib/bcdice/game_system/CthulhuTech.rb
164
166
  - lib/bcdice/game_system/Cthulhu_ChineseTraditional.rb
165
167
  - lib/bcdice/game_system/Cthulhu_Korean.rb
168
+ - lib/bcdice/game_system/Cthulhu_SimplifiedChinese.rb
166
169
  - lib/bcdice/game_system/DarkBlaze.rb
167
170
  - lib/bcdice/game_system/DarkDaysDrive.rb
168
171
  - lib/bcdice/game_system/DarkSouls.rb
@@ -217,6 +220,7 @@ files:
217
220
  - lib/bcdice/game_system/InfiniteFantasia.rb
218
221
  - lib/bcdice/game_system/Insane.rb
219
222
  - lib/bcdice/game_system/Insane_Korean.rb
223
+ - lib/bcdice/game_system/Irisbane.rb
220
224
  - lib/bcdice/game_system/IthaWenUa.rb
221
225
  - lib/bcdice/game_system/JamesBond.rb
222
226
  - lib/bcdice/game_system/JekyllAndHyde.rb
@@ -265,6 +269,7 @@ files:
265
269
  - lib/bcdice/game_system/Pathfinder.rb
266
270
  - lib/bcdice/game_system/Peekaboo.rb
267
271
  - lib/bcdice/game_system/Pendragon.rb
272
+ - lib/bcdice/game_system/PersonaO.rb
268
273
  - lib/bcdice/game_system/PhantasmAdventure.rb
269
274
  - lib/bcdice/game_system/Postman.rb
270
275
  - lib/bcdice/game_system/PulpCthulhu.rb
@@ -312,6 +317,7 @@ files:
312
317
  - lib/bcdice/game_system/Torg.rb
313
318
  - lib/bcdice/game_system/Torg1_5.rb
314
319
  - lib/bcdice/game_system/TorgEternity.rb
320
+ - lib/bcdice/game_system/ToshiakiHolyGrailWar.rb
315
321
  - lib/bcdice/game_system/TrinitySeven.rb
316
322
  - lib/bcdice/game_system/TunnelsAndTrolls.rb
317
323
  - lib/bcdice/game_system/TwilightGunsmoke.rb
@@ -381,14 +387,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
381
387
  requirements:
382
388
  - - ">="
383
389
  - !ruby/object:Gem::Version
384
- version: '2.5'
390
+ version: '2.6'
385
391
  required_rubygems_version: !ruby/object:Gem::Requirement
386
392
  requirements:
387
393
  - - ">="
388
394
  - !ruby/object:Gem::Version
389
395
  version: '0'
390
396
  requirements: []
391
- rubygems_version: 3.1.2
397
+ rubygems_version: 3.1.4
392
398
  signing_key:
393
399
  specification_version: 4
394
400
  summary: BCDice is a rolling dice engine for TRPG