bcdice 3.2.0 → 3.3.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -3
  3. data/README.md +1 -1
  4. data/i18n/MagicaLogia/zh_hans.yml +564 -0
  5. data/i18n/StellarKnights/ja_jp.yml +2 -0
  6. data/i18n/StellarKnights/ko_kr.yml +498 -0
  7. data/i18n/zh_hans.yml +7 -0
  8. data/lib/bcdice/command/parsed.rb +11 -3
  9. data/lib/bcdice/command/parser.rb +179 -100
  10. data/lib/bcdice/common_command/barabara_dice/node.rb +2 -2
  11. data/lib/bcdice/common_command/barabara_dice/result.rb +6 -0
  12. data/lib/bcdice/game_system.rb +3 -0
  13. data/lib/bcdice/game_system/Amadeus.rb +110 -93
  14. data/lib/bcdice/game_system/AnimaAnimus.rb +15 -8
  15. data/lib/bcdice/game_system/BattleTech.rb +109 -28
  16. data/lib/bcdice/game_system/BeastBindTrinity.rb +28 -12
  17. data/lib/bcdice/game_system/BlindMythos.rb +38 -37
  18. data/lib/bcdice/game_system/CodeLayerd.rb +39 -39
  19. data/lib/bcdice/game_system/ColossalHunter.rb +34 -47
  20. data/lib/bcdice/game_system/DeadlineHeroes.rb +9 -8
  21. data/lib/bcdice/game_system/DoubleCross.rb +2 -0
  22. data/lib/bcdice/game_system/Emoklore.rb +22 -17
  23. data/lib/bcdice/game_system/FutariSousa.rb +12 -10
  24. data/lib/bcdice/game_system/GardenOrder.rb +11 -6
  25. data/lib/bcdice/game_system/KemonoNoMori.rb +25 -38
  26. data/lib/bcdice/game_system/KurayamiCrying.rb +86 -0
  27. data/lib/bcdice/game_system/LiveraDoll.rb +254 -304
  28. data/lib/bcdice/game_system/LogHorizon.rb +10 -7
  29. data/lib/bcdice/game_system/MagicaLogia_SimplifiedChinese.rb +66 -0
  30. data/lib/bcdice/game_system/MeikyuKingdomBasic.rb +2 -1
  31. data/lib/bcdice/game_system/Nechronica.rb +57 -91
  32. data/lib/bcdice/game_system/NinjaSlayer.rb +2 -3
  33. data/lib/bcdice/game_system/SRS.rb +17 -16
  34. data/lib/bcdice/game_system/SamsaraBallad.rb +38 -11
  35. data/lib/bcdice/game_system/Satasupe.rb +31 -31
  36. data/lib/bcdice/game_system/ScreamHighSchool.rb +3 -3
  37. data/lib/bcdice/game_system/Skynauts.rb +101 -140
  38. data/lib/bcdice/game_system/StarryDolls.rb +318 -0
  39. data/lib/bcdice/game_system/SteamPunkers.rb +11 -6
  40. data/lib/bcdice/game_system/StellarKnights.rb +28 -12
  41. data/lib/bcdice/game_system/StellarKnights_Korean.rb +79 -0
  42. data/lib/bcdice/game_system/TokumeiTenkousei.rb +4 -3
  43. data/lib/bcdice/game_system/TrinitySeven.rb +275 -276
  44. data/lib/bcdice/game_system/Utakaze.rb +52 -47
  45. data/lib/bcdice/game_system/Yggdrasill.rb +1 -1
  46. data/lib/bcdice/game_system/ZettaiReido.rb +20 -25
  47. data/lib/bcdice/version.rb +1 -1
  48. metadata +8 -2
@@ -43,8 +43,8 @@ module BCDice
43
43
  Result.new.tap do |r|
44
44
  r.secret = @secret
45
45
  r.text = sequence.join(" > ")
46
- r.rands = randomizer.rand_results
47
- r.detailed_rands = randomizer.detailed_rand_results
46
+ r.last_dice_list_list = dice_list_list
47
+ r.last_dice_list = dice_list
48
48
  r.success_num = success_num
49
49
  end
50
50
  end
@@ -7,6 +7,10 @@ module BCDice
7
7
  module BarabaraDice
8
8
  # バラバラロールの結果を表すクラス
9
9
  class Result < ::BCDice::Result
10
+ # @return [Array<Array<Integer>>] 出目のグループの配列
11
+ attr_accessor :last_dice_list_list
12
+ # @return [Array<Integer>] すべての出目が格納される配列
13
+ attr_accessor :last_dice_list
10
14
  # @return [Integer] 成功数
11
15
  attr_accessor :success_num
12
16
 
@@ -14,6 +18,8 @@ module BCDice
14
18
  def initialize(text = nil)
15
19
  super(text)
16
20
 
21
+ @last_dice_list_list = []
22
+ @last_dice_list = []
17
23
  @success_num = 0
18
24
  end
19
25
  end
@@ -113,6 +113,7 @@ require "bcdice/game_system/LostRecord"
113
113
  require "bcdice/game_system/LostRoyal"
114
114
  require "bcdice/game_system/MagicaLogia"
115
115
  require "bcdice/game_system/MagicaLogia_Korean"
116
+ require "bcdice/game_system/MagicaLogia_SimplifiedChinese"
116
117
  require "bcdice/game_system/MeikyuDays"
117
118
  require "bcdice/game_system/MeikyuKingdom"
118
119
  require "bcdice/game_system/MeikyuKingdomBasic"
@@ -170,8 +171,10 @@ require "bcdice/game_system/ShinobiGami"
170
171
  require "bcdice/game_system/ShoujoTenrankai"
171
172
  require "bcdice/game_system/Skynauts"
172
173
  require "bcdice/game_system/SkynautsBouken"
174
+ require "bcdice/game_system/StarryDolls"
173
175
  require "bcdice/game_system/SteamPunkers"
174
176
  require "bcdice/game_system/StellarKnights"
177
+ require "bcdice/game_system/StellarKnights_Korean"
175
178
  require "bcdice/game_system/SterileLife"
176
179
  require "bcdice/game_system/StrangerOfSwordCity"
177
180
  require "bcdice/game_system/StratoShout"
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "bcdice/arithmetic_evaluator"
4
+ require "bcdice/format"
5
+ require "bcdice/normalize"
6
+
3
7
  module BCDice
4
8
  module GameSystem
5
9
  class Amadeus < Base
@@ -46,111 +50,124 @@ module BCDice
46
50
  end
47
51
 
48
52
  def eval_game_system_specific_command(command)
49
- text = amadeusDice(command)
50
- return text unless text.nil?
51
-
52
- return roll_tables(command, self.class::TABLES)
53
+ roll_amadeus(command) ||
54
+ roll_tables(command, self.class::TABLES)
53
55
  end
54
56
 
55
- def amadeusDice(command)
56
- return nil unless /^(R([A-DS])([+\-\d]*))(@(\d))?((>(=)?)([+\-\d]*))?(@(\d))?$/i =~ command
57
-
58
- commandText = Regexp.last_match(1)
59
- skillRank = Regexp.last_match(2)
60
- modifyText = Regexp.last_match(3)
61
- signOfInequality = (Regexp.last_match(7).nil? ? ">=" : Regexp.last_match(7))
62
- targetText = (Regexp.last_match(9).nil? ? "4" : Regexp.last_match(9))
63
- if nil | Regexp.last_match(5)
64
- specialNum = Regexp.last_match(5).to_i
65
- elsif nil | Regexp.last_match(11)
66
- specialNum = Regexp.last_match(11).to_i
67
- else
68
- specialNum = 6
57
+ private
58
+
59
+ def roll_amadeus(command)
60
+ m = /^R([A-DS])([+\-\d]*)(@(\d))?((>=?)([+\-\d]*))?(@(\d))?$/i.match(command)
61
+ unless m
62
+ return nil
69
63
  end
70
64
 
71
- diceCount = CHECK_DICE_COUNT[skillRank]
72
- modify = ArithmeticEvaluator.eval(modifyText)
73
- target = ArithmeticEvaluator.eval(targetText)
74
-
75
- diceList = @randomizer.roll_barabara(diceCount, 6)
76
- diceText = diceList.join(",")
77
- specialText = (specialNum == 6 ? "" : "@#{specialNum}")
78
-
79
- message = "(#{commandText}#{specialText}#{signOfInequality}#{targetText}) > [#{diceText}]#{modifyText} > "
80
- diceList = [diceList.min] if skillRank == "D"
81
- is_loop = false
82
- diceList.each do |dice|
83
- if is_loop
84
- message += " / "
85
- elsif diceList.length > 1
86
- is_loop = true
65
+ rank = m[1]
66
+ modifier = ArithmeticEvaluator.eval(m[2])
67
+ cmp_op = m[6] ? Normalize.comparison_operator(m[6]) : :>=
68
+ target = m[7] ? ArithmeticEvaluator.eval(m[7]) : 4
69
+ special = (m[4] || m[9] || 6).to_i
70
+
71
+ dice_count = CHECK_DICE_COUNT[rank]
72
+
73
+ dice_list = @randomizer.roll_barabara(dice_count, 6)
74
+ dice_text = dice_list.join(",")
75
+ special_text = (special == 6 ? "" : "@#{special}")
76
+
77
+ dice_list = [dice_list.min] if rank == "D"
78
+ available_inga = dice_list.size > 1
79
+ inga_table = translate("Amadeus.inga_table")
80
+
81
+ success = false
82
+ critical = false
83
+ fumble = false
84
+
85
+ results =
86
+ dice_list.map do |dice|
87
+ total = dice + modifier
88
+ result =
89
+ if dice == 1
90
+ fumble = true
91
+ translate("Amadeus.fumble")
92
+ elsif dice >= special
93
+ critical = true
94
+ success = true
95
+ translate("Amadeus.special")
96
+ elsif total.send(cmp_op, target)
97
+ success = true
98
+ translate("success")
99
+ else
100
+ translate("failure")
101
+ end
102
+
103
+ if available_inga
104
+ inga = inga_table[dice - 1]
105
+ "#{total}_#{result}[#{dice}#{inga}]"
106
+ else
107
+ "#{total}_#{result}[#{dice}]"
108
+ end
87
109
  end
88
- achieve = dice + modify
89
- result = check_success(achieve, dice, signOfInequality, target, specialNum)
90
- if is_loop
91
- inga_table = translate("Amadeus.inga_table")
92
- inga = inga_table[dice - 1]
93
- message += "#{achieve}_#{result}[#{dice}#{inga}]"
110
+
111
+ sequence = [
112
+ "(R#{rank}#{Format.modifier(modifier)}#{special_text}#{cmp_op}#{target})",
113
+ "[#{dice_text}]#{Format.modifier(modifier)}",
114
+ results.join(" / ")
115
+ ]
116
+
117
+ Result.new.tap do |r|
118
+ r.text = sequence.join(" > ")
119
+ if success
120
+ r.success = true
121
+ r.critical = critical
94
122
  else
95
- message += "#{achieve}_#{result}[#{dice}]"
123
+ r.failure = true
124
+ r.fumble = fumble
96
125
  end
97
126
  end
98
-
99
- return message
100
- end
101
-
102
- def check_success(total_n, dice_n, signOfInequality, diff, special_n)
103
- return translate("Amadeus.fumble") if dice_n == 1
104
- return translate("Amadeus.special") if dice_n >= special_n
105
-
106
- cmp_op = Normalize.comparison_operator(signOfInequality)
107
- target_num = diff.to_i
108
-
109
- if total_n.send(cmp_op, target_num)
110
- translate("success")
111
- else
112
- translate("failure")
113
- end
114
127
  end
115
128
 
116
129
  CHECK_DICE_COUNT = {"S" => 4, "A" => 3, "B" => 2, "C" => 1, "D" => 2}.freeze
117
130
 
118
- def self.translate_tables(locale)
119
- {
120
- "ECT" => DiceTable::Table.from_i18n("Amadeus.table.ECT", locale),
121
- "BST" => DiceTable::Table.from_i18n("Amadeus.table.BST", locale),
122
- "RT" => DiceTable::Table.from_i18n("Amadeus.table.RT", locale),
123
- "PRT" => DiceTable::Table.from_i18n("Amadeus.table.PRT", locale),
124
- "FT" => DiceTable::Table.from_i18n("Amadeus.table.FT", locale),
125
- "BT" => DiceTable::D66Table.from_i18n("Amadeus.table.BT", locale),
126
- "FWT" => DiceTable::Table.from_i18n("Amadeus.table.FWT", locale),
127
- "BRT" => DiceTable::Table.from_i18n("Amadeus.table.BRT", locale),
128
- "RIT" => DiceTable::Table.from_i18n("Amadeus.table.RIT", locale),
129
- "WT" => DiceTable::Table.from_i18n("Amadeus.table.WT", locale),
130
- "NMT" => DiceTable::Table.from_i18n("Amadeus.table.NMT", locale),
131
- "TGT" => DiceTable::Table.from_i18n("Amadeus.table.TGT", locale),
132
- "CST" => DiceTable::Table.from_i18n("Amadeus.table.CST", locale),
133
- "GCVT" => DiceTable::Table.from_i18n("Amadeus.table.GCVT", locale),
134
- "YCVT" => DiceTable::Table.from_i18n("Amadeus.table.YCVT", locale),
135
- "ECVT" => DiceTable::Table.from_i18n("Amadeus.table.ECVT", locale),
136
- "CCVT" => DiceTable::Table.from_i18n("Amadeus.table.CCVT", locale),
137
- "NCVT" => DiceTable::Table.from_i18n("Amadeus.table.NCVT", locale),
138
- "DGVT" => DiceTable::Table.from_i18n("Amadeus.table.DGVT", locale),
139
- "DAVT" => DiceTable::Table.from_i18n("Amadeus.table.DAVT", locale),
140
- "PRCT" => DiceTable::Table.from_i18n("Amadeus.table.PRCT", locale),
141
- "TCCT" => DiceTable::Table.from_i18n("Amadeus.table.TCCT", locale),
142
- "INCT" => DiceTable::Table.from_i18n("Amadeus.table.INCT", locale),
143
- "PSCT" => DiceTable::Table.from_i18n("Amadeus.table.PSCT", locale),
144
- "LVCT" => DiceTable::Table.from_i18n("Amadeus.table.LVCT", locale),
145
- "DACT" => DiceTable::Table.from_i18n("Amadeus.table.DACT", locale),
146
- "RGT" => DiceTable::Table.from_i18n("Amadeus.table.RGT", locale),
147
- "FBT" => DiceTable::Table.from_i18n("Amadeus.table.FBT", locale),
148
- "CHVT" => DiceTable::Table.from_i18n("Amadeus.table.CHVT", locale),
149
- "LCVT" => DiceTable::Table.from_i18n("Amadeus.table.LCVT", locale),
150
- "KCVT" => DiceTable::Table.from_i18n("Amadeus.table.KCVT", locale),
151
- "SAT" => DiceTable::D66Table.from_i18n("Amadeus.table.SAT", locale),
152
- "SMT" => DiceTable::D66Table.from_i18n("Amadeus.table.SMT", locale),
153
- }
131
+ class << self
132
+ private
133
+
134
+ def translate_tables(locale)
135
+ {
136
+ "ECT" => DiceTable::Table.from_i18n("Amadeus.table.ECT", locale),
137
+ "BST" => DiceTable::Table.from_i18n("Amadeus.table.BST", locale),
138
+ "RT" => DiceTable::Table.from_i18n("Amadeus.table.RT", locale),
139
+ "PRT" => DiceTable::Table.from_i18n("Amadeus.table.PRT", locale),
140
+ "FT" => DiceTable::Table.from_i18n("Amadeus.table.FT", locale),
141
+ "BT" => DiceTable::D66Table.from_i18n("Amadeus.table.BT", locale),
142
+ "FWT" => DiceTable::Table.from_i18n("Amadeus.table.FWT", locale),
143
+ "BRT" => DiceTable::Table.from_i18n("Amadeus.table.BRT", locale),
144
+ "RIT" => DiceTable::Table.from_i18n("Amadeus.table.RIT", locale),
145
+ "WT" => DiceTable::Table.from_i18n("Amadeus.table.WT", locale),
146
+ "NMT" => DiceTable::Table.from_i18n("Amadeus.table.NMT", locale),
147
+ "TGT" => DiceTable::Table.from_i18n("Amadeus.table.TGT", locale),
148
+ "CST" => DiceTable::Table.from_i18n("Amadeus.table.CST", locale),
149
+ "GCVT" => DiceTable::Table.from_i18n("Amadeus.table.GCVT", locale),
150
+ "YCVT" => DiceTable::Table.from_i18n("Amadeus.table.YCVT", locale),
151
+ "ECVT" => DiceTable::Table.from_i18n("Amadeus.table.ECVT", locale),
152
+ "CCVT" => DiceTable::Table.from_i18n("Amadeus.table.CCVT", locale),
153
+ "NCVT" => DiceTable::Table.from_i18n("Amadeus.table.NCVT", locale),
154
+ "DGVT" => DiceTable::Table.from_i18n("Amadeus.table.DGVT", locale),
155
+ "DAVT" => DiceTable::Table.from_i18n("Amadeus.table.DAVT", locale),
156
+ "PRCT" => DiceTable::Table.from_i18n("Amadeus.table.PRCT", locale),
157
+ "TCCT" => DiceTable::Table.from_i18n("Amadeus.table.TCCT", locale),
158
+ "INCT" => DiceTable::Table.from_i18n("Amadeus.table.INCT", locale),
159
+ "PSCT" => DiceTable::Table.from_i18n("Amadeus.table.PSCT", locale),
160
+ "LVCT" => DiceTable::Table.from_i18n("Amadeus.table.LVCT", locale),
161
+ "DACT" => DiceTable::Table.from_i18n("Amadeus.table.DACT", locale),
162
+ "RGT" => DiceTable::Table.from_i18n("Amadeus.table.RGT", locale),
163
+ "FBT" => DiceTable::Table.from_i18n("Amadeus.table.FBT", locale),
164
+ "CHVT" => DiceTable::Table.from_i18n("Amadeus.table.CHVT", locale),
165
+ "LCVT" => DiceTable::Table.from_i18n("Amadeus.table.LCVT", locale),
166
+ "KCVT" => DiceTable::Table.from_i18n("Amadeus.table.KCVT", locale),
167
+ "SAT" => DiceTable::D66Table.from_i18n("Amadeus.table.SAT", locale),
168
+ "SMT" => DiceTable::D66Table.from_i18n("Amadeus.table.SMT", locale),
169
+ }
170
+ end
154
171
  end
155
172
 
156
173
  TABLES = translate_tables(:ja_jp)
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'bcdice/dice_table/table'
4
4
  require 'bcdice/dice_table/range_table'
5
- require 'bcdice/arithmetic_evaluator'
5
+ require 'bcdice/arithmetic'
6
6
 
7
7
  module BCDice
8
8
  module GameSystem
@@ -32,17 +32,19 @@ module BCDice
32
32
  MESSAGETEXT
33
33
 
34
34
  def eval_game_system_specific_command(command)
35
- case command
36
- when /(\d+)AN<=(\d+([+\-]\d+)*)/i
37
- return check_action(Regexp.last_match)
38
- else
35
+ m = /(\d+)AN<=(\d+([+\-]\d+)*)/i.match(command)
36
+ if TABLES.key?(command)
39
37
  return roll_tables(command, TABLES)
38
+ elsif m
39
+ return check_action(m)
40
+ else
41
+ return nil
40
42
  end
41
43
  end
42
44
 
43
45
  def check_action(match_data)
44
- dice_cnt = ArithmeticEvaluator.eval(match_data[1])
45
- target = ArithmeticEvaluator.eval(match_data[2])
46
+ dice_cnt = Arithmetic.eval(match_data[1], RoundType::FLOOR)
47
+ target = Arithmetic.eval(match_data[2], RoundType::FLOOR)
46
48
  debug("dice_cnt", dice_cnt)
47
49
  debug("target", target)
48
50
 
@@ -52,7 +54,12 @@ module BCDice
52
54
  has_critical = dice_arr.include?(1)
53
55
  result = has_critical ? suc_cnt + 2 : suc_cnt
54
56
 
55
- return "(#{dice_cnt}B10<=#{target}) > #{dice_str} > #{result > 0 ? '成功' : '失敗'}(達成値:#{result})#{has_critical ? ' (クリティカル発生)' : ''}"
57
+ Result.new.tap do |r|
58
+ r.text = "(#{dice_cnt}B10<=#{target}) > #{dice_str} > #{result > 0 ? '成功' : '失敗'}(達成値:#{result})#{has_critical ? ' (クリティカル発生)' : ''}"
59
+ r.critical = has_critical
60
+ r.success = result > 0
61
+ r.failure = !r.success?
62
+ end
56
63
  end
57
64
 
58
65
  TABLES = {
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'bcdice/format'
4
+ require 'bcdice/command/parser'
3
5
  require 'bcdice/dice_table/table'
4
6
  require 'bcdice/dice_table/range_table'
5
7
 
@@ -26,24 +28,45 @@ module BCDice
26
28
    正面からダメージ3の攻撃を技能ベース2目標値4で1回判定
27
29
   例)2BT3RL+5>=8
28
30
    右下半身にダメージ3の攻撃を技能ベース5目標値8で2回判定
29
-  ミサイルによるダメージは BT(ダメージ)の変わりに SRM2/4/6, LRM5/10/15/20を指定
31
+  ミサイルによるダメージは BT(ダメージ) の代わりに SRM2/4/6, LRM5/10/15/20 を指定
30
32
   例)3SRM6LU+5>=8
31
33
    左上半身にSRM6連を技能ベース5目標値8で3回判定
34
+ BT(ダメージ) の代わりに PPC を指定するとダメージ10で判定
35
+ 例)2PPCR+3>=10
36
+  右側からPPC(ダメージ10)による攻撃を技能ベース3目標値10で2回判定
32
37
  ・CT:致命的命中表
33
38
  ・DW:転倒後の向き表
34
- ・CDx:メック戦士意識維持表。ダメージ値xで判定 例)CD3
39
+ ・CDx:メック戦士意識維持ロール。ダメージ値x(1〜6)で判定 例)CD3
35
40
  MESSAGETEXT
36
41
 
37
- register_prefix('\d*SRM', '\d*LRM', '\d*BT', 'PPC', 'CT', 'DW', 'CD')
42
+ register_prefix('\d*SRM', '\d*LRM', '\d*BT', '\d*PPC', 'CT', 'DW', 'CD')
38
43
 
39
44
  # 致命的命中が発生しない上限値
40
45
  NO_CRITICAL_HIT_LIMIT = 7
41
46
 
47
+ # @return [Command::Parser] PPCコマンドの構文解析器
48
+ def self.ppc_parser
49
+ return @ppc_parser if @ppc_parser
50
+
51
+ @ppc_parser = Command::Parser.new(/PPC(?:[LCR][LU]?)?/, round_type: RoundType::FLOOR)
52
+ @ppc_parser.enable_prefix_number
53
+ @ppc_parser.restrict_cmp_op_to(:>=)
54
+
55
+ @ppc_parser
56
+ end
57
+
58
+ # @return [Command::Parser] PPCコマンドの構文解析器
59
+ def ppc_parser
60
+ self.class.ppc_parser
61
+ end
62
+
42
63
  def eval_game_system_specific_command(command)
43
64
  result = roll_tables(command, TABLES)
44
65
  return result if result
45
66
 
46
- command = command.gsub("PPC", "BT10")
67
+ if (ppc_parse_result = ppc_parser.parse(command))
68
+ return execute_ppc(ppc_parse_result)
69
+ end
47
70
 
48
71
  count = 1
49
72
  if command =~ /^(\d+)(.+)/
@@ -55,9 +78,9 @@ module BCDice
55
78
  debug('executeCommandCatched command', command)
56
79
 
57
80
  case command
58
- when /^CD(\d+)$/
81
+ when /\ACD([1-6])\z/
59
82
  damage = Regexp.last_match(1).to_i
60
- return getCheckDieResult(damage)
83
+ return consciousness_roll(damage)
61
84
  when /^((S|L)RM\d+)(.+)/
62
85
  tail = Regexp.last_match(3)
63
86
  type = Regexp.last_match(1)
@@ -94,7 +117,7 @@ module BCDice
94
117
  LRM_LIMIT = 5
95
118
 
96
119
  def getHitResult(count, damageFunc, tail)
97
- m = /([LCR][LU]?)?(\+\d+)?>=(\d+)/.match(tail)
120
+ m = /\A([LCR][LU]?)?(\+\d+)?>=(\d+)/.match(tail)
98
121
  return nil unless m
99
122
 
100
123
  side = m[1] || 'C'
@@ -105,7 +128,7 @@ module BCDice
105
128
 
106
129
  partTable = HitPart::TABLES[side]
107
130
 
108
- resultTexts = []
131
+ resultLines = []
109
132
  damages = {}
110
133
  hitCount = 0
111
134
 
@@ -117,15 +140,24 @@ module BCDice
117
140
  damages, damageText = getDamages(damageFunc, partTable, damages)
118
141
  hitResult += damageText
119
142
  end
120
- resultTexts << hitResult
143
+ resultLines << hitResult
121
144
  end
122
145
 
123
- resultTexts.push(" > #{hitCount}回命中")
146
+ # 命中したか?
147
+ hit = hitCount > 0
124
148
 
125
- totalResultText = resultTexts.join("\n")
126
- totalResultText += " 命中箇所:" + getTotalDamage(damages) if hitCount > 0
149
+ hitCountText = " > #{hitCount}回命中"
150
+ hitDetails =
151
+ if hit
152
+ "#{hitCountText} 命中箇所:#{getTotalDamage(damages)}"
153
+ else
154
+ hitCountText
155
+ end
156
+ resultLines.push(hitDetails)
127
157
 
128
- return totalResultText
158
+ resultText = resultLines.join("\n")
159
+
160
+ return hit ? Result.success(resultText) : Result.failure(resultText)
129
161
  end
130
162
 
131
163
  def getBaseValue(baseString)
@@ -272,26 +304,75 @@ module BCDice
272
304
  return result_parts.join(' > '), hit_part.name, criticalText
273
305
  end
274
306
 
275
- def getCheckDieResult(damage)
276
- if damage >= 6
277
- return "死亡"
307
+ # メック戦士意識維持ロールを行う
308
+ #
309
+ # damageが6の場合は死亡。
310
+ # damageが5以下の場合は、2d6の結果が意識維持表の値以上かの成功判定。
311
+ #
312
+ # @param damage [Integer] メック戦士へのダメージ(1〜6)
313
+ # @return [Result]
314
+ # @see 「BattleTech: A Game of Armored Combat」ルールブックp. 44
315
+ def consciousness_roll(damage)
316
+ unless (1..6).include?(damage)
317
+ return nil
318
+ end
319
+
320
+ command = "CD#{damage}"
321
+
322
+ if damage == 6
323
+ return Result.fumble("#{command} > 死亡")
278
324
  end
279
325
 
280
- table = [[1, 3],
281
- [2, 5],
282
- [3, 7],
283
- [4, 10],
284
- [5, 11]]
326
+ consciousness_table = {
327
+ 1 => 3,
328
+ 2 => 5,
329
+ 3 => 7,
330
+ 4 => 10,
331
+ 5 => 11,
332
+ }
333
+
334
+ target = consciousness_table[damage]
335
+ expr = "(2D6>=#{target})"
336
+
337
+ values = @randomizer.roll_barabara(2, 6)
338
+ sum = values.sum
339
+ values_str = values.join(",")
340
+ sum_and_values = "#{sum}[#{values_str}]"
341
+
342
+ success = sum >= target
343
+ result = success ? "成功" : "失敗"
344
+
345
+ parts = [
346
+ command,
347
+ expr,
348
+ sum_and_values,
349
+ sum,
350
+ result,
351
+ ]
352
+ text = parts.join(" > ")
285
353
 
286
- target = get_table_by_number(damage, table, nil)
354
+ return success ? Result.success(text) : Result.failure(text)
355
+ end
287
356
 
288
- dice1 = @randomizer.roll_once(6)
289
- dice2 = @randomizer.roll_once(6)
290
- total = dice1 + dice2
291
- result = total >= target ? "成功" : "失敗"
292
- text = "#{total}[#{dice1},#{dice2}]>=#{target} #{result}"
357
+ # PPCコマンドを実行する
358
+ # @param parse_result [Command::Parsed] PPCコマンドの構文解析結果
359
+ # @return [Result, nil]
360
+ def execute_ppc(parse_result)
361
+ count = parse_result.prefix_number || 1
362
+
363
+ # getHitResult() の引数tailの形に合わせる
364
+ # TODO: 攻撃を表すクラスに変える
365
+
366
+ # "PPC" 以降の部位指定
367
+ side = parse_result.command[3..-1]
368
+
369
+ modifier = Format.modifier(parse_result.modify_number)
370
+ target = parse_result.target_number
371
+
372
+ tail = "#{side}#{modifier}>=#{target}"
293
373
 
294
- return text
374
+ # ダメージ10固定で命中判定を行う
375
+ return getHitResult(count, lambda { 10 }, tail)
295
376
  end
296
377
 
297
378
  # 表の集合