bcdice 3.0.0.pre.alpha.1

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 (258) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +21 -0
  3. data/.github/.codecov.yaml +10 -0
  4. data/.github/workflows/coverage.yaml +21 -0
  5. data/.github/workflows/lint.yaml +13 -0
  6. data/.github/workflows/test.yml +20 -0
  7. data/.gitignore +14 -0
  8. data/.rubocop.yml +96 -0
  9. data/.rubocop_todo.yml +113 -0
  10. data/CHANGELOG.md +868 -0
  11. data/Gemfile +11 -0
  12. data/LICENSE +29 -0
  13. data/README.md +38 -0
  14. data/ROADMAP.md +30 -0
  15. data/Rakefile +118 -0
  16. data/bcdice.gemspec +27 -0
  17. data/bin/repl.rb +21 -0
  18. data/docs/README.txt +2028 -0
  19. data/docs/dicebot_sort_key.md +72 -0
  20. data/docs/how_to_make_dicebot.md +453 -0
  21. data/lib/bcdice.rb +8 -0
  22. data/lib/bcdice/arithmetic_evaluator.rb +167 -0
  23. data/lib/bcdice/base.rb +423 -0
  24. data/lib/bcdice/command_parser.rb +242 -0
  25. data/lib/bcdice/common_command.rb +23 -0
  26. data/lib/bcdice/common_command/add_dice.rb +54 -0
  27. data/lib/bcdice/common_command/add_dice/node.rb +488 -0
  28. data/lib/bcdice/common_command/add_dice/parser.rb +282 -0
  29. data/lib/bcdice/common_command/add_dice/randomizer.rb +48 -0
  30. data/lib/bcdice/common_command/barabara_dice.rb +57 -0
  31. data/lib/bcdice/common_command/calc.rb +39 -0
  32. data/lib/bcdice/common_command/choice.rb +35 -0
  33. data/lib/bcdice/common_command/d66_dice.rb +63 -0
  34. data/lib/bcdice/common_command/reroll_dice.rb +190 -0
  35. data/lib/bcdice/common_command/upper_dice.rb +165 -0
  36. data/lib/bcdice/common_command/version.rb +26 -0
  37. data/lib/bcdice/dice_table.rb +8 -0
  38. data/lib/bcdice/dice_table/chain_table.rb +33 -0
  39. data/lib/bcdice/dice_table/d66_grid_table.rb +26 -0
  40. data/lib/bcdice/dice_table/d66_range_table.rb +26 -0
  41. data/lib/bcdice/dice_table/d66_table.rb +34 -0
  42. data/lib/bcdice/dice_table/range_table.rb +267 -0
  43. data/lib/bcdice/dice_table/roll_result.rb +43 -0
  44. data/lib/bcdice/dice_table/sai_fic_skill_table.rb +32 -0
  45. data/lib/bcdice/dice_table/table.rb +35 -0
  46. data/lib/bcdice/enum.rb +15 -0
  47. data/lib/bcdice/format.rb +36 -0
  48. data/lib/bcdice/game_system.rb +187 -0
  49. data/lib/bcdice/game_system/AFF2e.rb +141 -0
  50. data/lib/bcdice/game_system/AceKillerGene.rb +55 -0
  51. data/lib/bcdice/game_system/Airgetlamh.rb +117 -0
  52. data/lib/bcdice/game_system/Alsetto.rb +124 -0
  53. data/lib/bcdice/game_system/Alshard.rb +27 -0
  54. data/lib/bcdice/game_system/AlterRaise.rb +282 -0
  55. data/lib/bcdice/game_system/Amadeus.rb +622 -0
  56. data/lib/bcdice/game_system/Amadeus_Korean.rb +501 -0
  57. data/lib/bcdice/game_system/AnimaAnimus.rb +96 -0
  58. data/lib/bcdice/game_system/Arianrhod.rb +50 -0
  59. data/lib/bcdice/game_system/ArsMagica.rb +149 -0
  60. data/lib/bcdice/game_system/Avandner.rb +101 -0
  61. data/lib/bcdice/game_system/BBN.rb +143 -0
  62. data/lib/bcdice/game_system/BadLife.rb +435 -0
  63. data/lib/bcdice/game_system/BarnaKronika.rb +188 -0
  64. data/lib/bcdice/game_system/BattleTech.rb +530 -0
  65. data/lib/bcdice/game_system/BeastBindTrinity.rb +348 -0
  66. data/lib/bcdice/game_system/BeginningIdol.rb +2527 -0
  67. data/lib/bcdice/game_system/BeginningIdol_Korean.rb +2378 -0
  68. data/lib/bcdice/game_system/BladeOfArcana.rb +252 -0
  69. data/lib/bcdice/game_system/BlindMythos.rb +460 -0
  70. data/lib/bcdice/game_system/BloodCrusade.rb +423 -0
  71. data/lib/bcdice/game_system/BloodMoon.rb +213 -0
  72. data/lib/bcdice/game_system/CardRanker.rb +249 -0
  73. data/lib/bcdice/game_system/ChaosFlare.rb +181 -0
  74. data/lib/bcdice/game_system/Chill.rb +163 -0
  75. data/lib/bcdice/game_system/Chill3.rb +52 -0
  76. data/lib/bcdice/game_system/CodeLayerd.rb +132 -0
  77. data/lib/bcdice/game_system/ColossalHunter.rb +569 -0
  78. data/lib/bcdice/game_system/CrashWorld.rb +81 -0
  79. data/lib/bcdice/game_system/Cthulhu.rb +252 -0
  80. data/lib/bcdice/game_system/Cthulhu7th.rb +767 -0
  81. data/lib/bcdice/game_system/Cthulhu7th_ChineseTraditional.rb +386 -0
  82. data/lib/bcdice/game_system/Cthulhu7th_Korean.rb +383 -0
  83. data/lib/bcdice/game_system/CthulhuTech.rb +263 -0
  84. data/lib/bcdice/game_system/Cthulhu_ChineseTraditional.rb +243 -0
  85. data/lib/bcdice/game_system/Cthulhu_Korean.rb +240 -0
  86. data/lib/bcdice/game_system/DarkBlaze.rb +207 -0
  87. data/lib/bcdice/game_system/DarkDaysDrive.rb +425 -0
  88. data/lib/bcdice/game_system/DarkSouls.rb +91 -0
  89. data/lib/bcdice/game_system/DeadlineHeroes.rb +583 -0
  90. data/lib/bcdice/game_system/DemonParasite.rb +524 -0
  91. data/lib/bcdice/game_system/DetatokoSaga.rb +310 -0
  92. data/lib/bcdice/game_system/DetatokoSaga_Korean.rb +310 -0
  93. data/lib/bcdice/game_system/DiceBot.rb +17 -0
  94. data/lib/bcdice/game_system/DiceOfTheDead.rb +111 -0
  95. data/lib/bcdice/game_system/DoubleCross.rb +373 -0
  96. data/lib/bcdice/game_system/DoubleCross_Korean.rb +371 -0
  97. data/lib/bcdice/game_system/Dracurouge.rb +1856 -0
  98. data/lib/bcdice/game_system/Dracurouge_Korean.rb +310 -0
  99. data/lib/bcdice/game_system/DungeonsAndDragons.rb +19 -0
  100. data/lib/bcdice/game_system/EarthDawn.rb +225 -0
  101. data/lib/bcdice/game_system/EarthDawn3.rb +275 -0
  102. data/lib/bcdice/game_system/EarthDawn4.rb +272 -0
  103. data/lib/bcdice/game_system/EclipsePhase.rb +51 -0
  104. data/lib/bcdice/game_system/Elric.rb +36 -0
  105. data/lib/bcdice/game_system/Elysion.rb +1213 -0
  106. data/lib/bcdice/game_system/EmbryoMachine.rb +227 -0
  107. data/lib/bcdice/game_system/EndBreaker.rb +132 -0
  108. data/lib/bcdice/game_system/EtrianOdysseySRS.rb +27 -0
  109. data/lib/bcdice/game_system/Fiasco.rb +123 -0
  110. data/lib/bcdice/game_system/Fiasco_Korean.rb +122 -0
  111. data/lib/bcdice/game_system/FilledWith.rb +1543 -0
  112. data/lib/bcdice/game_system/FullMetalPanic.rb +27 -0
  113. data/lib/bcdice/game_system/FutariSousa.rb +994 -0
  114. data/lib/bcdice/game_system/FutariSousa_Korean.rb +994 -0
  115. data/lib/bcdice/game_system/Garako.rb +559 -0
  116. data/lib/bcdice/game_system/GardenOrder.rb +451 -0
  117. data/lib/bcdice/game_system/GehennaAn.rb +136 -0
  118. data/lib/bcdice/game_system/GeishaGirlwithKatana.rb +120 -0
  119. data/lib/bcdice/game_system/GoblinSlayer.rb +168 -0
  120. data/lib/bcdice/game_system/GoldenSkyStories.rb +77 -0
  121. data/lib/bcdice/game_system/Gorilla.rb +44 -0
  122. data/lib/bcdice/game_system/GranCrest.rb +518 -0
  123. data/lib/bcdice/game_system/Gundog.rb +52 -0
  124. data/lib/bcdice/game_system/GundogRevised.rb +294 -0
  125. data/lib/bcdice/game_system/GundogZero.rb +261 -0
  126. data/lib/bcdice/game_system/Gurps.rb +403 -0
  127. data/lib/bcdice/game_system/GurpsFW.rb +2330 -0
  128. data/lib/bcdice/game_system/HarnMaster.rb +208 -0
  129. data/lib/bcdice/game_system/HatsuneMiku.rb +706 -0
  130. data/lib/bcdice/game_system/Hieizan.rb +48 -0
  131. data/lib/bcdice/game_system/HouraiGakuen.rb +270 -0
  132. data/lib/bcdice/game_system/HuntersMoon.rb +503 -0
  133. data/lib/bcdice/game_system/Illusio.rb +89 -0
  134. data/lib/bcdice/game_system/InfiniteFantasia.rb +51 -0
  135. data/lib/bcdice/game_system/Insane.rb +822 -0
  136. data/lib/bcdice/game_system/Insane_Korean.rb +773 -0
  137. data/lib/bcdice/game_system/IthaWenUa.rb +38 -0
  138. data/lib/bcdice/game_system/JamesBond.rb +45 -0
  139. data/lib/bcdice/game_system/Kamigakari.rb +394 -0
  140. data/lib/bcdice/game_system/Kamigakari_Korean.rb +428 -0
  141. data/lib/bcdice/game_system/KanColle.rb +938 -0
  142. data/lib/bcdice/game_system/KemonoNoMori.rb +258 -0
  143. data/lib/bcdice/game_system/KillDeathBusiness.rb +1420 -0
  144. data/lib/bcdice/game_system/KillDeathBusiness_Korean.rb +1510 -0
  145. data/lib/bcdice/game_system/KurayamiCrying.rb +49 -0
  146. data/lib/bcdice/game_system/LiveraDoll.rb +375 -0
  147. data/lib/bcdice/game_system/LogHorizon.rb +3567 -0
  148. data/lib/bcdice/game_system/LogHorizon_Korean.rb +1407 -0
  149. data/lib/bcdice/game_system/LostRecord.rb +30 -0
  150. data/lib/bcdice/game_system/LostRoyal.rb +242 -0
  151. data/lib/bcdice/game_system/MagicaLogia.rb +1148 -0
  152. data/lib/bcdice/game_system/MeikyuDays.rb +415 -0
  153. data/lib/bcdice/game_system/MeikyuKingdom.rb +466 -0
  154. data/lib/bcdice/game_system/MeikyuKingdomBasic.rb +269 -0
  155. data/lib/bcdice/game_system/MetalHead.rb +249 -0
  156. data/lib/bcdice/game_system/MetalHeadExtream.rb +917 -0
  157. data/lib/bcdice/game_system/MetallicGuardian.rb +31 -0
  158. data/lib/bcdice/game_system/MonotoneMuseum.rb +354 -0
  159. data/lib/bcdice/game_system/MonotoneMuseum_Korean.rb +304 -0
  160. data/lib/bcdice/game_system/Nechronica.rb +211 -0
  161. data/lib/bcdice/game_system/Nechronica_Korean.rb +160 -0
  162. data/lib/bcdice/game_system/NeverCloud.rb +74 -0
  163. data/lib/bcdice/game_system/NightWizard.rb +231 -0
  164. data/lib/bcdice/game_system/NightWizard3rd.rb +24 -0
  165. data/lib/bcdice/game_system/NightmareHunterDeep.rb +117 -0
  166. data/lib/bcdice/game_system/NinjaSlayer.rb +274 -0
  167. data/lib/bcdice/game_system/NjslyrBattle.rb +54 -0
  168. data/lib/bcdice/game_system/Nuekagami.rb +293 -0
  169. data/lib/bcdice/game_system/OneWayHeroics.rb +134 -0
  170. data/lib/bcdice/game_system/OracleEngine.rb +265 -0
  171. data/lib/bcdice/game_system/OrgaRain.rb +77 -0
  172. data/lib/bcdice/game_system/Oukahoushin3rd.rb +162 -0
  173. data/lib/bcdice/game_system/Paradiso.rb +323 -0
  174. data/lib/bcdice/game_system/Paranoia.rb +64 -0
  175. data/lib/bcdice/game_system/ParanoiaRebooted.rb +109 -0
  176. data/lib/bcdice/game_system/ParasiteBlood.rb +231 -0
  177. data/lib/bcdice/game_system/Pathfinder.rb +21 -0
  178. data/lib/bcdice/game_system/Peekaboo.rb +324 -0
  179. data/lib/bcdice/game_system/Pendragon.rb +39 -0
  180. data/lib/bcdice/game_system/PhantasmAdventure.rb +73 -0
  181. data/lib/bcdice/game_system/Postman.rb +185 -0
  182. data/lib/bcdice/game_system/PulpCthulhu.rb +184 -0
  183. data/lib/bcdice/game_system/Raisondetre.rb +171 -0
  184. data/lib/bcdice/game_system/RecordOfLodossWar.rb +58 -0
  185. data/lib/bcdice/game_system/RecordOfSteam.rb +149 -0
  186. data/lib/bcdice/game_system/RokumonSekai2.rb +110 -0
  187. data/lib/bcdice/game_system/RoleMaster.rb +24 -0
  188. data/lib/bcdice/game_system/RuneQuest.rb +47 -0
  189. data/lib/bcdice/game_system/RyuTuber.rb +266 -0
  190. data/lib/bcdice/game_system/Ryutama.rb +199 -0
  191. data/lib/bcdice/game_system/SRS.rb +348 -0
  192. data/lib/bcdice/game_system/SamsaraBallad.rb +111 -0
  193. data/lib/bcdice/game_system/Satasupe.rb +1173 -0
  194. data/lib/bcdice/game_system/ScreamHighSchool.rb +128 -0
  195. data/lib/bcdice/game_system/SevenFortressMobius.rb +34 -0
  196. data/lib/bcdice/game_system/ShadowRun.rb +26 -0
  197. data/lib/bcdice/game_system/ShadowRun4.rb +49 -0
  198. data/lib/bcdice/game_system/ShadowRun5.rb +80 -0
  199. data/lib/bcdice/game_system/SharedFantasia.rb +69 -0
  200. data/lib/bcdice/game_system/ShinMegamiTenseiKakuseihen.rb +85 -0
  201. data/lib/bcdice/game_system/ShinkuuGakuen.rb +506 -0
  202. data/lib/bcdice/game_system/ShinobiGami.rb +680 -0
  203. data/lib/bcdice/game_system/ShoujoTenrankai.rb +677 -0
  204. data/lib/bcdice/game_system/Skynauts.rb +324 -0
  205. data/lib/bcdice/game_system/SteamPunkers.rb +485 -0
  206. data/lib/bcdice/game_system/StellarKnights.rb +856 -0
  207. data/lib/bcdice/game_system/SterileLife.rb +452 -0
  208. data/lib/bcdice/game_system/StrangerOfSwordCity.rb +103 -0
  209. data/lib/bcdice/game_system/StratoShout.rb +316 -0
  210. data/lib/bcdice/game_system/StratoShout_Korean.rb +314 -0
  211. data/lib/bcdice/game_system/Strave.rb +223 -0
  212. data/lib/bcdice/game_system/SwordWorld.rb +491 -0
  213. data/lib/bcdice/game_system/SwordWorld2_0.rb +295 -0
  214. data/lib/bcdice/game_system/SwordWorld2_5.rb +121 -0
  215. data/lib/bcdice/game_system/TherapieSein.rb +93 -0
  216. data/lib/bcdice/game_system/TokumeiTenkousei.rb +96 -0
  217. data/lib/bcdice/game_system/TokyoGhostResearch.rb +122 -0
  218. data/lib/bcdice/game_system/TokyoNova.rb +19 -0
  219. data/lib/bcdice/game_system/Torg.rb +364 -0
  220. data/lib/bcdice/game_system/Torg1_5.rb +139 -0
  221. data/lib/bcdice/game_system/TorgEternity.rb +414 -0
  222. data/lib/bcdice/game_system/TrinitySeven.rb +364 -0
  223. data/lib/bcdice/game_system/TunnelsAndTrolls.rb +287 -0
  224. data/lib/bcdice/game_system/TwilightGunsmoke.rb +527 -0
  225. data/lib/bcdice/game_system/Utakaze.rb +153 -0
  226. data/lib/bcdice/game_system/VampireTheMasquerade5th.rb +132 -0
  227. data/lib/bcdice/game_system/Villaciel.rb +555 -0
  228. data/lib/bcdice/game_system/WARPS.rb +33 -0
  229. data/lib/bcdice/game_system/WaresBlade.rb +34 -0
  230. data/lib/bcdice/game_system/Warhammer.rb +336 -0
  231. data/lib/bcdice/game_system/WitchQuest.rb +275 -0
  232. data/lib/bcdice/game_system/WorldOfDarkness.rb +135 -0
  233. data/lib/bcdice/game_system/YankeeYogSothoth.rb +440 -0
  234. data/lib/bcdice/game_system/YearZeroEngine.rb +134 -0
  235. data/lib/bcdice/game_system/ZettaiReido.rb +142 -0
  236. data/lib/bcdice/game_system/meikyu_kingdom/item_table.rb +383 -0
  237. data/lib/bcdice/game_system/meikyu_kingdom/kingdom_name_table.rb +89 -0
  238. data/lib/bcdice/game_system/meikyu_kingdom/landscape_table.rb +196 -0
  239. data/lib/bcdice/game_system/meikyu_kingdom/name_tables.rb +223 -0
  240. data/lib/bcdice/game_system/meikyu_kingdom/placename_table.rb +214 -0
  241. data/lib/bcdice/game_system/meikyu_kingdom/tables.rb +610 -0
  242. data/lib/bcdice/game_system/meikyu_kingdom/word_table.rb +117 -0
  243. data/lib/bcdice/game_system/meikyu_kingdom_basic/item_table.rb +380 -0
  244. data/lib/bcdice/game_system/meikyu_kingdom_basic/kingdom_table.rb +184 -0
  245. data/lib/bcdice/game_system/meikyu_kingdom_basic/name_table.rb +417 -0
  246. data/lib/bcdice/game_system/meikyu_kingdom_basic/table.rb +912 -0
  247. data/lib/bcdice/game_system/meikyu_kingdom_basic/word_table.rb +121 -0
  248. data/lib/bcdice/game_system/one_way_heroics/dungeon_table.rb +67 -0
  249. data/lib/bcdice/game_system/one_way_heroics/random_event_table.rb +166 -0
  250. data/lib/bcdice/game_system/one_way_heroics/tables.rb +668 -0
  251. data/lib/bcdice/loader.rb +37 -0
  252. data/lib/bcdice/normalize.rb +38 -0
  253. data/lib/bcdice/preprocessor.rb +87 -0
  254. data/lib/bcdice/randomizer.rb +137 -0
  255. data/lib/bcdice/repl.rb +155 -0
  256. data/lib/bcdice/user_defined_dice_table.rb +153 -0
  257. data/lib/bcdice/version.rb +3 -0
  258. metadata +304 -0
@@ -0,0 +1,348 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bcdice/arithmetic_evaluator'
4
+
5
+ # スタンダードRPGシステムのダイスボット
6
+ #
7
+ # スタンダードRPGシステムにおいて共通な2D6成功判定を実装している。
8
+ # また、各ゲームシステムに合わせた2D6成功判定のエイリアスコマンドを
9
+ # 登録するクラスメソッド set_aliases_for_srs_roll を持つ。
10
+ module BCDice
11
+ module GameSystem
12
+ class SRS < Base
13
+ # ゲームシステムの識別子
14
+ ID = 'SRS'
15
+
16
+ # ゲームシステム名
17
+ NAME = 'スタンダードRPGシステム'
18
+
19
+ # ゲームシステム名の読みがな
20
+ SORT_KEY = 'すたんたあとRPGしすてむ'
21
+
22
+ HELP_MESSAGE_1 = <<~HELP_MESSAGE
23
+ ・判定
24
+  ・通常判定:2D6+m>=t[c,f]
25
+   修正値m、目標値t、クリティカル値c、ファンブル値fで判定ロールを行います。
26
+   修正値、クリティカル値、ファンブル値は省略可能です([]ごと省略可)。
27
+   クリティカル値、ファンブル値の既定値は、それぞれ12、2です。
28
+   自動成功、自動失敗、成功、失敗を自動表示します。
29
+
30
+   例) 2d6>=10     修正値0、目標値10で判定
31
+   例) 2d6+2>=10    修正値+2、目標値10で判定
32
+   例) 2d6+2>=10[11]  ↑をクリティカル値11で判定
33
+   例) 2d6+2>=10[12,4] ↑をクリティカル値12、ファンブル値4で判定
34
+ HELP_MESSAGE
35
+
36
+ HELP_MESSAGE_2 = <<~HELP_MESSAGE
37
+  ・クリティカルおよびファンブルのみの判定:2D6+m[c,f]
38
+   目標値を指定せず、修正値m、クリティカル値c、ファンブル値fで判定ロールを行います。
39
+   修正値、クリティカル値、ファンブル値は省略可能です([]は省略不可)。
40
+   自動成功、自動失敗を自動表示します。
41
+
42
+   例) 2d6[]    修正値0、クリティカル値12、ファンブル値2で判定
43
+   例) 2d6+2[11]  修正値+2、クリティカル値11、ファンブル値2で判定
44
+   例) 2d6+2[12,4] 修正値+2、クリティカル値12、ファンブル値4で判定
45
+ HELP_MESSAGE
46
+
47
+ HELP_MESSAGE_3 = <<~HELP_MESSAGE
48
+ ・D66ダイスあり(入れ替えなし)
49
+ HELP_MESSAGE
50
+
51
+ # 既定のダイスボット説明文
52
+ DEFAULT_HELP_MESSAGE = "#{HELP_MESSAGE_1}\n#{HELP_MESSAGE_2}\n#{HELP_MESSAGE_3}"
53
+
54
+ HELP_MESSAGE = DEFAULT_HELP_MESSAGE
55
+
56
+ # 成功判定のエイリアスコマンド定義用のクラスメソッドを提供するモジュール
57
+ module ClassMethods
58
+ # ダイスボットの説明文を返す
59
+ # @return [String]
60
+ attr_reader :help_message
61
+
62
+ # 目標値あり成功判定のエイリアスコマンドの正規表現を返す
63
+ # @return [Regexp, nil]
64
+ attr_reader :aliases_re_for_srs_roll_with_target_value
65
+
66
+ # 目標値なし成功判定のエイリアスコマンドの正規表現を返す
67
+ # @return [Regexp, nil]
68
+ attr_reader :aliases_re_for_srs_roll_without_target_value
69
+
70
+ # 成功判定のエイリアスコマンドを設定する
71
+ # @param [String] aliases エイリアスコマンド(可変長引数)
72
+ # @return [self]
73
+ #
74
+ # エイリアスコマンドとして指定した文字列がコマンドの先頭にあれば、
75
+ # 実行時にそれが2D6に置換されるようになる。
76
+ def set_aliases_for_srs_roll(*aliases)
77
+ aliases_upcase = aliases.map(&:upcase)
78
+ aliases_part = aliases_upcase
79
+ .map { |a| Regexp.escape(a) }
80
+ .join('|')
81
+
82
+ @aliases_re_for_srs_roll_with_target_value = Regexp.new(
83
+ '\A' \
84
+ "(?:#{aliases_part})" \
85
+ '((?:[-+][-+\d]+)?>=\d+(?:\[\d*(?:,\d+)?\])?)\z'
86
+ )
87
+
88
+ @aliases_re_for_srs_roll_without_target_value = Regexp.new(
89
+ '\A' \
90
+ "(?:#{aliases_part})" \
91
+ '([-+][-+\d]+)?(\[\d*(?:,\d+)?\])?\z'
92
+ )
93
+
94
+ prepare_help_msg_for_aliases_for_srs_roll(aliases_upcase)
95
+ @help_message = concatenate_help_messages
96
+
97
+ return self
98
+ end
99
+
100
+ # 成功判定のエイリアスコマンドを未設定にする
101
+ # @return [self]
102
+ def clear_aliases_for_srs_roll
103
+ @aliases_re_for_srs_roll_with_target_value = nil
104
+ @aliases_re_for_srs_roll_without_target_value = nil
105
+ @help_message = SRS::DEFAULT_HELP_MESSAGE
106
+
107
+ return self
108
+ end
109
+
110
+ private
111
+
112
+ # 成功判定のエイリアスコマンドの説明文を用意する
113
+ # @param [Array<String>] aliases エイリアスコマンドの配列
114
+ # @return [self]
115
+ # @todo 現在は2文字のエイリアスコマンドに幅を合わせてある。
116
+ # エイリアスコマンドの文字数が変わる場合があれば、位置を調整するコードが
117
+ # 必要。
118
+ def prepare_help_msg_for_aliases_for_srs_roll(aliases)
119
+ @help_msg_for_aliases_for_srs_roll_with_target_value =
120
+ aliases
121
+ .map { |a| "  例) #{a}+2>=10     2d6+2>=10と同じ(#{a}が2D6のショートカットコマンド)\n" }
122
+ .join()
123
+
124
+ @help_msg_for_aliases_for_srs_roll_without_target_value =
125
+ aliases
126
+ .map do |a|
127
+ "  例) #{a}      2d6[]と同じ(#{a}が2D6のショートカットコマンド)\n" \
128
+ "  例) #{a}+2[12,4]  2d6+2[12,4]と同じ(#{a}が2D6のショートカットコマンド)\n"
129
+ end
130
+ .join()
131
+
132
+ return self
133
+ end
134
+
135
+ # ダイスボットの説明文を結合する
136
+ # @return [String] 結合された説明文
137
+ def concatenate_help_messages
138
+ "#{SRS::HELP_MESSAGE_1}" \
139
+ "#{@help_msg_for_aliases_for_srs_roll_with_target_value}\n" \
140
+ "#{SRS::HELP_MESSAGE_2}" \
141
+ "#{@help_msg_for_aliases_for_srs_roll_without_target_value}\n" \
142
+ "#{SRS::HELP_MESSAGE_3}"
143
+ end
144
+ end
145
+
146
+ class << self
147
+ # クラスが継承されたときに行う処理
148
+ # @return [void]
149
+ def inherited(subclass)
150
+ subclass
151
+ .extend(ClassMethods)
152
+ .clear_aliases_for_srs_roll
153
+ end
154
+
155
+ # ダイスボットの説明文を返す
156
+ # @return [String] 既定のダイスボット説明文
157
+ def help_message
158
+ DEFAULT_HELP_MESSAGE
159
+ end
160
+
161
+ # 目標値あり成功判定のエイリアスコマンドの正規表現を返す
162
+ # @return [nil]
163
+ def aliases_re_for_srs_roll_with_target_value
164
+ nil
165
+ end
166
+
167
+ # 目標値なし成功判定のエイリアスコマンドの正規表現を返す
168
+ # @return [nil]
169
+ def aliases_re_for_srs_roll_without_target_value
170
+ nil
171
+ end
172
+ end
173
+
174
+ # 固有のコマンドの接頭辞を設定する
175
+ register_prefix(['2D6.*'])
176
+
177
+ # ダイスボットを初期化する
178
+ def initialize(command)
179
+ super(command)
180
+
181
+ # 式、出目ともに送信する
182
+
183
+ # バラバラロール(Bコマンド)でソートする
184
+ @sort_add_dice = true
185
+ # D66ダイスあり(出目をソートしない)
186
+ @enabled_d66 = true
187
+ @d66_sort_type = D66SortType::NO_SORT
188
+ end
189
+
190
+ # ダイスボットの説明文を返す
191
+ # @return [String]
192
+ def help_message
193
+ self.class.help_message
194
+ end
195
+
196
+ # 既定のクリティカル値
197
+ DEFAULT_CRITICAL_VALUE = 12
198
+ # 既定のファンブル値
199
+ DEFAULT_FUMBLE_VALUE = 2
200
+
201
+ # 目標値あり成功判定の正規表現
202
+ SRS_ROLL_WITH_TARGET_VALUE_RE =
203
+ /\A2D6([-+][-+\d]+)?>=(\d+)(?:\[(\d+)?(?:,(\d+))?\])?\z/.freeze
204
+ # 目標値なし成功判定の正規表現
205
+ SRS_ROLL_WITHOUT_TARGET_VALUE_RE =
206
+ /\A2D6([-+][-+\d]+)?\[(\d+)?(?:,(\d+))?\]\z/.freeze
207
+
208
+ # 既定の閾値指定表記
209
+ SRS_ROLL_DEFAULT_THRESHOLDS =
210
+ "[#{DEFAULT_CRITICAL_VALUE},#{DEFAULT_FUMBLE_VALUE}]"
211
+
212
+ # 成功判定コマンドのノード
213
+ SRSRollNode = Struct.new(
214
+ :modifier, :critical_value, :fumble_value, :target_value
215
+ ) do
216
+ # 成功判定の文字列表記を返す
217
+ # @return [String]
218
+ def to_s
219
+ lhs = "2D6#{Format.modifier(modifier)}"
220
+ expression = target_value ? "#{lhs}>=#{target_value}" : lhs
221
+
222
+ return "#{expression}[#{critical_value},#{fumble_value}]"
223
+ end
224
+ end
225
+
226
+ # 固有のダイスロールコマンドを実行する
227
+ # @param [String] command 入力されたコマンド
228
+ # @return [String, nil] ダイスロールコマンドの実行結果
229
+ def eval_game_system_specific_command(command)
230
+ alias_replaced_with_2d6 = replace_alias_for_srs_roll_with_2d6(command)
231
+
232
+ if (node = parse(alias_replaced_with_2d6))
233
+ return execute_srs_roll(node)
234
+ end
235
+
236
+ return nil
237
+ end
238
+
239
+ private
240
+
241
+ # 成功判定のエイリアスコマンドを2D6に置換する
242
+ # @param [String] input 入力文字列
243
+ # @return [String]
244
+ def replace_alias_for_srs_roll_with_2d6(input)
245
+ case input
246
+ when self.class.aliases_re_for_srs_roll_with_target_value
247
+ return "2D6#{Regexp.last_match(1)}"
248
+ when self.class.aliases_re_for_srs_roll_without_target_value
249
+ modifier = Regexp.last_match(1)
250
+ thresholds = Regexp.last_match(2) || SRS_ROLL_DEFAULT_THRESHOLDS
251
+
252
+ return "2D6#{modifier}#{thresholds}"
253
+ else
254
+ return input
255
+ end
256
+ end
257
+
258
+ # 構文解析する
259
+ # @param [String] command コマンド文字列
260
+ # @return [SRSRollNode, nil]
261
+ def parse(command)
262
+ case command
263
+ when SRS_ROLL_WITH_TARGET_VALUE_RE
264
+ return parse_srs_roll_with_target_value(Regexp.last_match)
265
+ when SRS_ROLL_WITHOUT_TARGET_VALUE_RE
266
+ return parse_srs_roll_without_target_value(Regexp.last_match)
267
+ else
268
+ return nil
269
+ end
270
+ end
271
+
272
+ # 修正値を評価する
273
+ # @param [String, nil] modifier_str 修正値部分の文字列
274
+ # @return [Integer] 修正値
275
+ def eval_modifier(modifier_str)
276
+ return 0 unless modifier_str
277
+
278
+ return ArithmeticEvaluator.eval(modifier_str, round_type: @round_type)
279
+ end
280
+
281
+ # 目標値あり成功判定の正規表現マッチ情報からノードを作る
282
+ # @param [MatchData] m 正規表現のマッチ情報
283
+ # @return [SRSRollNode]
284
+ def parse_srs_roll_with_target_value(m)
285
+ modifier = eval_modifier(m[1])
286
+ target_value = m[2].to_i
287
+ critical_value = m[3]&.to_i || DEFAULT_CRITICAL_VALUE
288
+ fumble_value = m[4]&.to_i || DEFAULT_FUMBLE_VALUE
289
+
290
+ return SRSRollNode.new(modifier, critical_value, fumble_value, target_value)
291
+ end
292
+
293
+ # 目標値なし成功判定の正規表現マッチ情報からノードを作る
294
+ # @param [MatchData] m 正規表現のマッチ情報
295
+ # @return [SRSRollNode]
296
+ def parse_srs_roll_without_target_value(m)
297
+ modifier = eval_modifier(m[1])
298
+ critical_value = m[2]&.to_i || DEFAULT_CRITICAL_VALUE
299
+ fumble_value = m[3]&.to_i || DEFAULT_FUMBLE_VALUE
300
+
301
+ return SRSRollNode.new(modifier, critical_value, fumble_value, nil)
302
+ end
303
+
304
+ # 成功判定を実行する
305
+ # @param [SRSRollNode] srs_roll 成功判定ノード
306
+ # @return [String] 成功判定結果
307
+ def execute_srs_roll(srs_roll)
308
+ dice_list = @randomizer.roll_barabara(2, 6)
309
+ dice_list.sort! if @sort_add_dice
310
+
311
+ sum = dice_list.sum()
312
+ dice_str = dice_list.join(",")
313
+
314
+ modified_sum = sum + srs_roll.modifier
315
+
316
+ parts = [
317
+ "(#{srs_roll})",
318
+ "#{sum}[#{dice_str}]#{Format.modifier(srs_roll.modifier)}",
319
+ modified_sum,
320
+ compare_result(srs_roll, sum, modified_sum)
321
+ ]
322
+
323
+ return parts.compact.join(' > ')
324
+ end
325
+
326
+ # ダイスロール結果を目標値、クリティカル値、ファンブル値と比較する
327
+ # @param [SRSRollNode] srs_roll 成功判定ノード
328
+ # @param [Integer] sum 出目の合計
329
+ # @param [Integer] modified_sum 修正後の値
330
+ # @return [String, nil] 比較結果
331
+ def compare_result(srs_roll, sum, modified_sum)
332
+ if sum >= srs_roll.critical_value
333
+ return '自動成功'
334
+ end
335
+
336
+ if sum <= srs_roll.fumble_value
337
+ return '自動失敗'
338
+ end
339
+
340
+ if srs_roll.target_value
341
+ return modified_sum >= srs_roll.target_value ? '成功' : '失敗'
342
+ end
343
+
344
+ return nil
345
+ end
346
+ end
347
+ end
348
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bcdice/command_parser"
4
+
5
+ module BCDice
6
+ module GameSystem
7
+ class SamsaraBallad < Base
8
+ # ゲームシステムの識別子
9
+ ID = 'SamsaraBallad'
10
+
11
+ # ゲームシステム名
12
+ NAME = 'サンサーラ・バラッド'
13
+
14
+ # ゲームシステム名の読みがな
15
+ #
16
+ # 「ゲームシステム名の読みがなの設定方法」(docs/dicebot_sort_key.md)を参考にして
17
+ # 設定してください
18
+ SORT_KEY = 'さんさあらはらつと'
19
+
20
+ # ダイスボットの使い方
21
+ HELP_MESSAGE = <<~MESSAGETEXT
22
+ SB 通常のD100ロールを行う
23
+ SBS スワップロールでD100ロールを行う
24
+ SB#x@y F値をx、C値をyとして通常のD100ロールを行う
25
+ SBS#x@y F値をx、C値をyとしてスワップロールでD100ロールを行う
26
+
27
+ 例:
28
+ SB<=85 通常の技能で成功率85%の判定
29
+ SBS<=70 習熟を得た技能で成功率70%の判定
30
+ SBS#3@7<=80 習熟を得た技能で、F値3、C値7で成功率80%の攻撃判定
31
+ SB<57 通常の技能で、能動側の達成値が57の受動判定
32
+ SBS<70 習熟を得た技能で、能動側の達成値が70の受動判定
33
+ MESSAGETEXT
34
+
35
+ # ダイスボットで使用するコマンドを配列で列挙する
36
+ register_prefix(['SBS?.*'])
37
+
38
+ def eval_game_system_specific_command(command)
39
+ debug("eval_game_system_specific_command Begin")
40
+
41
+ parser = CommandParser.new('SB', 'SBS')
42
+ cmd = parser.parse(command)
43
+
44
+ unless cmd
45
+ return nil
46
+ end
47
+
48
+ if cmd.command == 'SB'
49
+ places_text = nil
50
+ total = @randomizer.roll_once(100)
51
+ else
52
+ a = @randomizer.roll_once(10)
53
+ b = @randomizer.roll_once(10)
54
+ places_text = "#{a},#{b}"
55
+ places = [a, b].map { |n| n == 10 ? 0 : n }.sort
56
+
57
+ total = places[0] * 10 + places[1]
58
+ total = 100 if total == 0
59
+ end
60
+
61
+ cmp_result = compare(total, cmd)
62
+
63
+ sequence = [
64
+ "(D100#{cmd.cmp_op}#{cmd.target_number})",
65
+ places_text,
66
+ total.to_s,
67
+ cmp_result,
68
+ ].compact
69
+
70
+ return sequence.join(" > ")
71
+ end
72
+
73
+ private
74
+
75
+ # @return [String]
76
+ # @return [nil]
77
+ def compare(total, cmd)
78
+ if [:<=, :<].include?(cmd.cmp_op)
79
+ if !total.send(cmd.cmp_op, cmd.target_number)
80
+ "失敗"
81
+ elsif fumble_?(total, cmd.fumble)
82
+ "ファンブル"
83
+ elsif critical_?(total, cmd.critical)
84
+ "クリティカル"
85
+ else
86
+ "成功"
87
+ end
88
+ elsif fumble_?(total, cmd.fumble)
89
+ # ファンブル優先
90
+ "ファンブル"
91
+ elsif critical_?(total, cmd.critical)
92
+ "クリティカル"
93
+ end
94
+ end
95
+
96
+ # @param total [Integer]
97
+ # @param fumble [Integer, nil]
98
+ # @return [Boolean]
99
+ def fumble_?(total, fumble)
100
+ fumble && (total % 10 <= fumble)
101
+ end
102
+
103
+ # @param total [Integer]
104
+ # @param critical [Integer, nil]
105
+ # @return [Boolean]
106
+ def critical_?(total, critical)
107
+ critical && (total % 10 >= critical)
108
+ end
109
+ end
110
+ end
111
+ end