bcdice 3.1.2 → 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.
Files changed (161) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +154 -75
  3. data/README.md +3 -2
  4. data/i18n/Cthulhu/zh_hans.yml +11 -0
  5. data/i18n/MagicaLogia/zh_hans.yml +564 -0
  6. data/i18n/StellarKnights/ja_jp.yml +498 -0
  7. data/i18n/StellarKnights/ko_kr.yml +498 -0
  8. data/i18n/zh_hans.yml +7 -0
  9. data/lib/bcdice/command/parsed.rb +11 -3
  10. data/lib/bcdice/command/parser.rb +179 -100
  11. data/lib/bcdice/common_command/add_dice.rb +1 -1
  12. data/lib/bcdice/common_command/barabara_dice/node.rb +2 -2
  13. data/lib/bcdice/common_command/barabara_dice/result.rb +6 -0
  14. data/lib/bcdice/common_command/calc.rb +1 -1
  15. data/lib/bcdice/common_command/choice.rb +78 -7
  16. data/lib/bcdice/dice_table.rb +3 -0
  17. data/lib/bcdice/dice_table/d66_half_grid_table.rb +33 -0
  18. data/lib/bcdice/dice_table/d66_left_range_table.rb +29 -0
  19. data/lib/bcdice/dice_table/d66_one_third_table.rb +34 -0
  20. data/lib/bcdice/game_system.rb +10 -0
  21. data/lib/bcdice/game_system/AceKillerGene.rb +1 -1
  22. data/lib/bcdice/game_system/Ainecadette.rb +68 -0
  23. data/lib/bcdice/game_system/Airgetlamh.rb +1 -1
  24. data/lib/bcdice/game_system/Alsetto.rb +1 -1
  25. data/lib/bcdice/game_system/AlterRaise.rb +2 -2
  26. data/lib/bcdice/game_system/Amadeus.rb +110 -93
  27. data/lib/bcdice/game_system/AngelGear.rb +10 -5
  28. data/lib/bcdice/game_system/AnimaAnimus.rb +16 -9
  29. data/lib/bcdice/game_system/Arianrhod.rb +6 -8
  30. data/lib/bcdice/game_system/Avandner.rb +1 -1
  31. data/lib/bcdice/game_system/BarnaKronika.rb +1 -1
  32. data/lib/bcdice/game_system/BattleTech.rb +109 -28
  33. data/lib/bcdice/game_system/BeastBindTrinity.rb +28 -12
  34. data/lib/bcdice/game_system/BeginningIdol.rb +5 -5
  35. data/lib/bcdice/game_system/BeginningIdol_Korean.rb +5 -5
  36. data/lib/bcdice/game_system/BladeOfArcana.rb +1 -1
  37. data/lib/bcdice/game_system/BlindMythos.rb +39 -38
  38. data/lib/bcdice/game_system/BloodCrusade.rb +6 -7
  39. data/lib/bcdice/game_system/BloodMoon.rb +9 -8
  40. data/lib/bcdice/game_system/CardRanker.rb +8 -7
  41. data/lib/bcdice/game_system/ChaosFlare.rb +21 -15
  42. data/lib/bcdice/game_system/Chill.rb +10 -10
  43. data/lib/bcdice/game_system/Chill3.rb +9 -9
  44. data/lib/bcdice/game_system/CodeLayerd.rb +39 -39
  45. data/lib/bcdice/game_system/ColossalHunter.rb +34 -47
  46. data/lib/bcdice/game_system/CrashWorld.rb +1 -1
  47. data/lib/bcdice/game_system/Cthulhu.rb +1 -1
  48. data/lib/bcdice/game_system/Cthulhu7th.rb +50 -12
  49. data/lib/bcdice/game_system/Cthulhu7th_ChineseTraditional.rb +1 -1
  50. data/lib/bcdice/game_system/Cthulhu7th_Korean.rb +1 -1
  51. data/lib/bcdice/game_system/Cthulhu_SimplifiedChinese.rb +60 -0
  52. data/lib/bcdice/game_system/DarkBlaze.rb +0 -11
  53. data/lib/bcdice/game_system/DarkDaysDrive.rb +8 -7
  54. data/lib/bcdice/game_system/DeadlineHeroes.rb +11 -10
  55. data/lib/bcdice/game_system/DemonParasite.rb +5 -13
  56. data/lib/bcdice/game_system/DesperateRun.rb +1 -1
  57. data/lib/bcdice/game_system/DoubleCross.rb +3 -1
  58. data/lib/bcdice/game_system/Dracurouge.rb +3 -2
  59. data/lib/bcdice/game_system/EclipsePhase.rb +14 -14
  60. data/lib/bcdice/game_system/Elric.rb +18 -14
  61. data/lib/bcdice/game_system/Elysion.rb +1 -1
  62. data/lib/bcdice/game_system/EmbryoMachine.rb +9 -8
  63. data/lib/bcdice/game_system/Emoklore.rb +25 -20
  64. data/lib/bcdice/game_system/Fiasco.rb +1 -1
  65. data/lib/bcdice/game_system/FilledWith.rb +4 -0
  66. data/lib/bcdice/game_system/FutariSousa.rb +12 -10
  67. data/lib/bcdice/game_system/GURPS.rb +17 -28
  68. data/lib/bcdice/game_system/Garako.rb +1 -1
  69. data/lib/bcdice/game_system/GardenOrder.rb +12 -7
  70. data/lib/bcdice/game_system/GeishaGirlwithKatana.rb +1 -1
  71. data/lib/bcdice/game_system/GoblinSlayer.rb +1 -1
  72. data/lib/bcdice/game_system/Gorilla.rb +2 -8
  73. data/lib/bcdice/game_system/GranCrest.rb +189 -417
  74. data/lib/bcdice/game_system/Gundog.rb +9 -8
  75. data/lib/bcdice/game_system/GundogRevised.rb +10 -9
  76. data/lib/bcdice/game_system/GundogZero.rb +1 -1
  77. data/lib/bcdice/game_system/GurpsFW.rb +16 -16
  78. data/lib/bcdice/game_system/HarnMaster.rb +19 -26
  79. data/lib/bcdice/game_system/HatsuneMiku.rb +1 -1
  80. data/lib/bcdice/game_system/Hieizan.rb +12 -21
  81. data/lib/bcdice/game_system/HouraiGakuen.rb +1 -1
  82. data/lib/bcdice/game_system/HuntersMoon.rb +5 -10
  83. data/lib/bcdice/game_system/InfiniteFantasia.rb +19 -17
  84. data/lib/bcdice/game_system/Insane.rb +13 -20
  85. data/lib/bcdice/game_system/Irisbane.rb +154 -0
  86. data/lib/bcdice/game_system/IthaWenUa.rb +8 -15
  87. data/lib/bcdice/game_system/JamesBond.rb +10 -10
  88. data/lib/bcdice/game_system/JekyllAndHyde.rb +1 -1
  89. data/lib/bcdice/game_system/Kamigakari.rb +1 -1
  90. data/lib/bcdice/game_system/KanColle.rb +4 -9
  91. data/lib/bcdice/game_system/KemonoNoMori.rb +26 -39
  92. data/lib/bcdice/game_system/KillDeathBusiness.rb +7 -15
  93. data/lib/bcdice/game_system/KurayamiCrying.rb +141 -2
  94. data/lib/bcdice/game_system/LiveraDoll.rb +254 -304
  95. data/lib/bcdice/game_system/LogHorizon.rb +13 -10
  96. data/lib/bcdice/game_system/MagicaLogia_SimplifiedChinese.rb +66 -0
  97. data/lib/bcdice/game_system/MeikyuDays.rb +10 -8
  98. data/lib/bcdice/game_system/MeikyuKingdom.rb +26 -26
  99. data/lib/bcdice/game_system/MeikyuKingdomBasic.rb +4 -3
  100. data/lib/bcdice/game_system/MetalHead.rb +12 -22
  101. data/lib/bcdice/game_system/MetalHeadExtream.rb +5 -5
  102. data/lib/bcdice/game_system/MonotoneMuseum.rb +8 -6
  103. data/lib/bcdice/game_system/Nechronica.rb +63 -90
  104. data/lib/bcdice/game_system/NinjaSlayer.rb +6 -7
  105. data/lib/bcdice/game_system/NjslyrBattle.rb +8 -11
  106. data/lib/bcdice/game_system/OneWayHeroics.rb +1 -1
  107. data/lib/bcdice/game_system/Paradiso.rb +3 -3
  108. data/lib/bcdice/game_system/ParasiteBlood.rb +1 -1
  109. data/lib/bcdice/game_system/Peekaboo.rb +4 -9
  110. data/lib/bcdice/game_system/Pendragon.rb +7 -7
  111. data/lib/bcdice/game_system/PersonaO.rb +86 -0
  112. data/lib/bcdice/game_system/PhantasmAdventure.rb +8 -8
  113. data/lib/bcdice/game_system/Postman.rb +1 -1
  114. data/lib/bcdice/game_system/PulpCthulhu.rb +1 -1
  115. data/lib/bcdice/game_system/Raisondetre.rb +2 -2
  116. data/lib/bcdice/game_system/Revulture.rb +123 -0
  117. data/lib/bcdice/game_system/RuneQuest.rb +8 -8
  118. data/lib/bcdice/game_system/Ryutama.rb +1 -1
  119. data/lib/bcdice/game_system/SRS.rb +17 -16
  120. data/lib/bcdice/game_system/SamsaraBallad.rb +38 -11
  121. data/lib/bcdice/game_system/Satasupe.rb +32 -32
  122. data/lib/bcdice/game_system/ScreamHighSchool.rb +5 -5
  123. data/lib/bcdice/game_system/SharedFantasia.rb +19 -23
  124. data/lib/bcdice/game_system/ShinobiGami.rb +14 -6
  125. data/lib/bcdice/game_system/Skynauts.rb +103 -142
  126. data/lib/bcdice/game_system/SkynautsBouken.rb +228 -0
  127. data/lib/bcdice/game_system/StarryDolls.rb +318 -0
  128. data/lib/bcdice/game_system/SteamPunkers.rb +25 -7
  129. data/lib/bcdice/game_system/StellarKnights.rb +85 -706
  130. data/lib/bcdice/game_system/StellarKnights_Korean.rb +79 -0
  131. data/lib/bcdice/game_system/SterileLife.rb +2 -3
  132. data/lib/bcdice/game_system/StratoShout.rb +8 -16
  133. data/lib/bcdice/game_system/Strave.rb +1 -1
  134. data/lib/bcdice/game_system/SwordWorld.rb +16 -7
  135. data/lib/bcdice/game_system/SwordWorld2_0.rb +2 -2
  136. data/lib/bcdice/game_system/SwordWorld2_5.rb +2 -2
  137. data/lib/bcdice/game_system/TherapieSein.rb +1 -1
  138. data/lib/bcdice/game_system/TokumeiTenkousei.rb +4 -3
  139. data/lib/bcdice/game_system/TokyoGhostResearch.rb +1 -1
  140. data/lib/bcdice/game_system/TorgEternity.rb +2 -2
  141. data/lib/bcdice/game_system/ToshiakiHolyGrailWar.rb +92 -0
  142. data/lib/bcdice/game_system/TrinitySeven.rb +279 -280
  143. data/lib/bcdice/game_system/TunnelsAndTrolls.rb +1 -1
  144. data/lib/bcdice/game_system/Utakaze.rb +53 -48
  145. data/lib/bcdice/game_system/Villaciel.rb +1 -1
  146. data/lib/bcdice/game_system/WARPS.rb +11 -9
  147. data/lib/bcdice/game_system/WaresBlade.rb +4 -9
  148. data/lib/bcdice/game_system/Warhammer.rb +15 -6
  149. data/lib/bcdice/game_system/Warhammer4.rb +15 -11
  150. data/lib/bcdice/game_system/WitchQuest.rb +1 -1
  151. data/lib/bcdice/game_system/YankeeYogSothoth.rb +4 -9
  152. data/lib/bcdice/game_system/Yggdrasill.rb +1 -1
  153. data/lib/bcdice/game_system/ZettaiReido.rb +20 -25
  154. data/lib/bcdice/game_system/meikyu_kingdom_basic/kingdom_table.rb +1 -1
  155. data/lib/bcdice/game_system/sword_world/rating_parsed.rb +4 -4
  156. data/lib/bcdice/game_system/sword_world/rating_parser.rb +1 -1
  157. data/lib/bcdice/game_system/sword_world/transcendent_test.rb +26 -8
  158. data/lib/bcdice/repl.rb +1 -1
  159. data/lib/bcdice/result.rb +26 -0
  160. data/lib/bcdice/version.rb +1 -1
  161. metadata +22 -5
@@ -42,7 +42,7 @@ module BCDice
42
42
  MESSAGETEXT
43
43
 
44
44
  register_prefix(
45
- '(\d+)?A(A|L)(\d+)?((x|\*)(\d+)(\+(\d+))?)?(C(\d+))?'
45
+ '\d*A[AL]'
46
46
  )
47
47
 
48
48
  def initialize(command)
@@ -37,7 +37,7 @@ module BCDice
37
37
  ・8ALC4x5 → 8d6で目標値4、攻撃力5、トライアンフ無しの命中判定。
38
38
  MESSAGETEXT
39
39
 
40
- register_prefix('\d+AL(C|G)?(\d+)?(x|\*)\d+', '\d+ALC?(\d+)?')
40
+ register_prefix('\d+AL[CG]?')
41
41
 
42
42
  def initialize(command)
43
43
  super(command)
@@ -35,8 +35,8 @@ module BCDice
35
35
  MESSAGETEXT
36
36
 
37
37
  register_prefix(
38
- 'EMA(\d+)?', 'PER(\d+)?', 'LOC(\d+)?', 'QUI(\d+)?', 'DEL(\d+)?',
39
- 'TRA(\d+)?', 'SCE(\d+)?', 'STA(\d+)?', 'EMO(\d+)?'
38
+ 'EMA', 'PER', 'LOC', 'QUI', 'DEL',
39
+ 'TRA', 'SCE', 'STA', 'EMO'
40
40
  )
41
41
 
42
42
  def eval_game_system_specific_command(command)
@@ -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)
@@ -48,17 +48,22 @@ module BCDice
48
48
  dice_text = dice.join(",")
49
49
  modify_n = 0
50
50
  success = 0
51
-
52
51
  if skill_value
53
52
  success = dice.count { |val| val <= skill_value }
54
53
  modify_n = Arithmetic.eval(modify, RoundType::FLOOR) unless modify.empty?
55
54
  end
56
55
 
57
- output = "(#{command}) > #{success}[#{dice_text}]#{format('%+d', modify_n)} > 成功数: #{success + modify_n}"
56
+ gospel = '(福音発生)' if success + modify_n >= 100
57
+
58
+ output = "(#{command}) > #{success}[#{dice_text}]#{format('%+d', modify_n)} > 成功数: #{success + modify_n}#{gospel}"
59
+
58
60
  if success + modify_n >= 100
59
- output += "(福音発生)"
61
+ Result.critical(output)
62
+ elsif 0 < success + modify_n
63
+ Result.success(output)
64
+ else
65
+ Result.failure(output)
60
66
  end
61
- return output
62
67
  end
63
68
 
64
69
  TABLES = {
@@ -117,7 +122,7 @@ module BCDice
117
122
  )
118
123
  }.freeze
119
124
 
120
- register_prefix('(\d+)AG(\d+)?(([\+\-]\d+)*)', TABLES.keys)
125
+ register_prefix('\d+AG', TABLES.keys)
121
126
  end
122
127
  end
123
128
  end
@@ -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 = {
@@ -86,7 +93,7 @@ module BCDice
86
93
  }.freeze
87
94
 
88
95
  # ダイスボットで使用するコマンドを配列で列挙する
89
- register_prefix('\d+AN<=\d([\+\-]\d+)*', TABLES.keys)
96
+ register_prefix('\d+AN<=', TABLES.keys)
90
97
  end
91
98
  end
92
99
  end
@@ -25,25 +25,23 @@ module BCDice
25
25
  @d66_sort_type = D66SortType::NO_SORT
26
26
  end
27
27
 
28
- def check_nD6(total, _dice_total, dice_list, cmp_op, target)
28
+ def result_nd6(total, _dice_total, dice_list, cmp_op, target)
29
29
  n_max = dice_list.count(6)
30
30
 
31
31
  if dice_list.count(1) == dice_list.size
32
32
  # 全部1の目ならファンブル
33
- "ファンブル"
33
+ Result.fumble("ファンブル")
34
34
  elsif n_max >= 2
35
35
  # 2個以上6の目があったらクリティカル
36
- "クリティカル(+#{n_max}D6)" if n_max >= 2
36
+ Result.critical("クリティカル(+#{n_max}D6)")
37
37
  elsif cmp_op != :>= || target == '?'
38
- ''
38
+ nil
39
39
  elsif total >= target
40
- "成功"
40
+ Result.success("成功")
41
41
  else
42
- "失敗"
42
+ Result.failure("失敗")
43
43
  end
44
44
  end
45
-
46
- alias check_2D6 check_nD6
47
45
  end
48
46
  end
49
47
  end
@@ -34,7 +34,7 @@ module BCDice
34
34
  ・8av4*7+10 → 8d10で目標値4、攻撃力7、クリティカルトリガー10の命中判定。
35
35
  MESSAGETEXT
36
36
 
37
- register_prefix('\d+AV\d+((x|\*)\d+(\+\d+)?)?(c\d+)?')
37
+ register_prefix('\d+AV')
38
38
 
39
39
  def initialize(command)
40
40
  super(command)
@@ -26,7 +26,7 @@ module BCDice
26
26
   セット数が1以上の時はセット数も表示し、攻撃判定の場合は命中部位も表示します。
27
27
  INFO_MESSAGE_TEXT
28
28
 
29
- register_prefix('\d+BK', '\d+BA', '\d+BKC\d+', '\d+BAC\d+', '\d+R6')
29
+ register_prefix('\d+BK', '\d+BA', '\d+BKC', '\d+BAC', '\d+R6')
30
30
 
31
31
  def initialize(command)
32
32
  super(command)
@@ -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+.+', '\d*LRM\d+.+', '\d*BT.+', 'PPC', 'CT', 'DW', 'CD\d+')
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
  # 表の集合