bcdice/BCDice

View on GitHub
lib/bcdice/game_system/MagicaLogia.rb

Summary

Maintainability
B
4 hrs
Test Coverage
D
61%
# frozen_string_literal: true

require "bcdice/base"

module BCDice
  module GameSystem
    class MagicaLogia < Base
      # ゲームシステムの識別子
      ID = 'MagicaLogia'

      # ゲームシステム名
      NAME = 'マギカロギア'

      # ゲームシステム名の読みがな
      SORT_KEY = 'まきかろきあ'

      # ダイスボットの使い方
      HELP_MESSAGE = <<~INFO_MESSAGE_TEXT
        ・判定
        スペシャル/ファンブル/成功/失敗を判定
        ・各種表
        経歴表 BGT/初期アンカー表 DAT/運命属性表 FAT
        願い表 WIT/プライズ表 PT
        時の流れ表 TPT/大判時の流れ表 TPTB
        事件表 AT
        ファンブル表 FT/変調表 WT
        運命変転表 FCT
         典型的災厄 TCT/物理的災厄 PCT/精神的災厄 MCT/狂気的災厄 ICT
         社会的災厄 SCT/超常的災厄 XCT/不思議系災厄 WCT/コミカル系災厄 CCT
         魔法使いの災厄 MGCT
        シーン表 ST/大判シーン表 STB
         極限環境 XEST/内面世界 IWST/魔法都市 MCST
         死後世界 WDST/迷宮世界 LWST
         魔法書架 MBST/魔法学院 MAST/クレドの塔 TCST
         並行世界 PWST/終末   PAST/異世界酒場 GBST
         ほしかげ SLST/旧図書館 OLST
        世界法則追加表 WLAT/さまよう怪物表 WMT
        ランダム分野表 RCT
        ランダム特技表 RTT
         星分野ランダム特技表  RTS, RTT1
         獣分野ランダム特技表  RTB, RTT2
         力分野ランダム特技表  RTF, RTT3
         歌分野ランダム特技表  RTP, RTT4
         夢分野ランダム特技表  RTD, RTT5
         闇分野ランダム特技表  RTN, RTT6
        ブランク秘密表 BST/
         宿敵表 MIT/謀略表 MOT/因縁表 MAT
         奇人表 MUT/力場表 MFT/同盟表 MLT
        落花表 FFT
        その後表 FLT
        ・D66ダイスあり
      INFO_MESSAGE_TEXT

      def initialize(command)
        super(command)

        @sort_add_dice = true
        @sort_barabara_dice = true
        @d66_sort_type = D66SortType::ASC
      end

      # ゲーム別成功度判定(2D6)
      def result_2d6(total, dice_total, dice_list, cmp_op, target)
        return nil if target == '?'
        return nil unless cmp_op == :>=

        result =
          if dice_total <= 2
            Result.fumble(translate("fumble"))
          elsif dice_total >= 12
            Result.critical(translate("MagicaLogia.special"))
          elsif total >= target
            Result.success(translate("success"))
          else
            Result.failure(translate("failure"))
          end

        result.text += gain_magic_element(dice_list[0], dice_list[1])
        return result
      end

      def eval_game_system_specific_command(command)
        self.class::SKILL_TABLE.roll_command(@randomizer, command) ||
          roll_tables(command, self.class::TABLES)
      end

      private

      # 魔素獲得チェック
      def gain_magic_element(dice1, dice2)
        return "" unless dice1 == dice2

        element = translate("MagicaLogia.elements.items")[dice1 - 1]
        return " > " + format(translate("MagicaLogia.elements.format"), text: element)
      end

      class SkillExpandTable
        def self.from_i18n(key, locale, skill_table)
          table = I18n.t(key, locale: locale, raise: false)
          new(table[:name], table[:type], table[:items], skill_table)
        end

        def initialize(name, type, items, skill_table)
          @name = name
          @items = items.freeze
          @skill_table = skill_table

          m = /(\d+)D(\d+)/i.match(type)
          unless m
            raise ArgumentError, "Unexpected table type: #{type}"
          end

          @times = m[1].to_i
          @sides = m[2].to_i
        end

        def roll(randomizer)
          value = randomizer.roll_sum(@times, @sides)
          text = expand(@items[value - @times], randomizer)

          return DiceTable::RollResult.new(@name, value, text)
        end

        private

        def expand(chosen, randomizer)
          chosen.gsub(/%{([a-z]+)}/) do
            m = Regexp.last_match
            type = m[1].to_sym

            roll_skill(type, randomizer)
          end
        end

        CATEGORIES = [:star, :beast, :force, :poem, :dream, :night].freeze

        def roll_skill(type, randomizer)
          if type == :skill
            return @skill_table.roll_skill(randomizer)
          end

          if type == :element
            return @skill_table.roll_category(randomizer)
          end

          index = CATEGORIES.index(type)
          raise ArgumentError unless index

          @skill_table.categories[index].roll(randomizer).name
        end
      end

      class FallenAfterTable
        def self.from_i18n(key, locale)
          table = I18n.t(key, locale: locale, raise: true)
          new(table[:name], table[:items_lower], table[:items_higher])
        end

        def initialize(name, items_lower, items_higher)
          @name = name
          @lower = items_lower
          @higher = items_higher
        end

        def roll(randomizer)
          val1, val2 = randomizer.roll_barabara(2, 6)

          table = val1 <= 3 ? @lower : @higher

          "#{@name}(#{val1},#{val2}) > #{table[val2 - 1]}"
        end
      end

      class << self
        private

        def translate_skill_table(locale)
          DiceTable::SaiFicSkillTable.from_i18n(
            "MagicaLogia.skill_table",
            locale,
            rttn: ["RTS", "RTB", "RTF", "RTP", "RTD", "RTN"]
          )
        end

        def translate_tables(locale, skill_table)
          inveterate_enemy_table = SkillExpandTable.from_i18n("MagicaLogia.inveterate_enemy_table", locale, skill_table)
          conspiracy_table = DiceTable::Table.from_i18n("MagicaLogia.conspiracy_table", locale)
          fate_table = DiceTable::Table.from_i18n("MagicaLogia.fate_table", locale)
          cueball_table = DiceTable::Table.from_i18n("MagicaLogia.cueball_table", locale)
          force_field_table = DiceTable::Table.from_i18n("MagicaLogia.force_field_table", locale)
          alliance_table = SkillExpandTable.from_i18n("MagicaLogia.alliance_table", locale, skill_table)

          {
            "TPT" => SkillExpandTable.from_i18n("MagicaLogia.tables.TPT", locale, skill_table),
            "ST" => SkillExpandTable.from_i18n("MagicaLogia.tables.ST", locale, skill_table),
            "FT" => DiceTable::Table.from_i18n("MagicaLogia.tables.FT", locale),
            "WT" => SkillExpandTable.from_i18n("MagicaLogia.tables.WT", locale, skill_table),
            "FCT" => DiceTable::Table.from_i18n("MagicaLogia.tables.FCT", locale),
            "AT" => SkillExpandTable.from_i18n("MagicaLogia.tables.AT", locale, skill_table),
            "BGT" => DiceTable::Table.from_i18n("MagicaLogia.tables.BGT", locale),
            "DAT" => DiceTable::Table.from_i18n("MagicaLogia.tables.DAT", locale),
            "FAT" => DiceTable::Table.from_i18n("MagicaLogia.tables.FAT", locale),
            "WIT" => DiceTable::Table.from_i18n("MagicaLogia.tables.WIT", locale),
            "TCT" => DiceTable::Table.from_i18n("MagicaLogia.tables.TCT", locale),
            "PCT" => DiceTable::Table.from_i18n("MagicaLogia.tables.PCT", locale),
            "MCT" => DiceTable::Table.from_i18n("MagicaLogia.tables.MCT", locale),
            "ICT" => DiceTable::Table.from_i18n("MagicaLogia.tables.ICT", locale),
            "SCT" => DiceTable::Table.from_i18n("MagicaLogia.tables.SCT", locale),
            "XCT" => DiceTable::Table.from_i18n("MagicaLogia.tables.XCT", locale),
            "WCT" => DiceTable::Table.from_i18n("MagicaLogia.tables.WCT", locale),
            "CCT" => DiceTable::Table.from_i18n("MagicaLogia.tables.CCT", locale),
            "MIT" => inveterate_enemy_table,
            "MOT" => conspiracy_table,
            "MAT" => fate_table,
            "MUT" => cueball_table,
            "MFT" => force_field_table,
            "MLT" => alliance_table,
            "BST" => DiceTable::ChainTable.new(
              I18n.translate("MagicaLogia.tables.BST.name", raise: true, locale: locale),
              "1D6",
              [
                inveterate_enemy_table,
                conspiracy_table,
                fate_table,
                cueball_table,
                force_field_table,
                alliance_table,
              ]
            ),
            "PT" => DiceTable::Table.from_i18n("MagicaLogia.tables.PT", locale),
            "XEST" => SkillExpandTable.from_i18n("MagicaLogia.tables.XEST", locale, skill_table),
            "IWST" => SkillExpandTable.from_i18n("MagicaLogia.tables.IWST", locale, skill_table),
            "MCST" => SkillExpandTable.from_i18n("MagicaLogia.tables.MCST", locale, skill_table),
            "WDST" => SkillExpandTable.from_i18n("MagicaLogia.tables.WDST", locale, skill_table),
            "LWST" => SkillExpandTable.from_i18n("MagicaLogia.tables.LWST", locale, skill_table),
            "STB" => SkillExpandTable.from_i18n("MagicaLogia.tables.STB", locale, skill_table),
            "MGCT" => DiceTable::Table.from_i18n("MagicaLogia.tables.MGCT", locale),
            "MBST" => SkillExpandTable.from_i18n("MagicaLogia.tables.MBST", locale, skill_table),
            "MAST" => SkillExpandTable.from_i18n("MagicaLogia.tables.MAST", locale, skill_table),
            "TCST" => SkillExpandTable.from_i18n("MagicaLogia.tables.TCST", locale, skill_table),
            "PWST" => SkillExpandTable.from_i18n("MagicaLogia.tables.PWST", locale, skill_table),
            "PAST" => SkillExpandTable.from_i18n("MagicaLogia.tables.PAST", locale, skill_table),
            "GBST" => SkillExpandTable.from_i18n("MagicaLogia.tables.GBST", locale, skill_table),
            "SLST" => SkillExpandTable.from_i18n("MagicaLogia.tables.SLST", locale, skill_table),
            "WLAT" => DiceTable::Table.from_i18n("MagicaLogia.tables.WLAT", locale),
            "WMT" => SkillExpandTable.from_i18n("MagicaLogia.tables.WMT", locale, skill_table),
            "FFT" => DiceTable::Table.from_i18n("MagicaLogia.tables.FFT", locale),
            "OLST" => SkillExpandTable.from_i18n("MagicaLogia.tables.OLST", locale, skill_table),
            "TPTB" => SkillExpandTable.from_i18n("MagicaLogia.tables.TPTB", locale, skill_table),
            "FLT" => FallenAfterTable.from_i18n("MagicaLogia.tables.FLT", locale),
          }
        end
      end

      SKILL_TABLE = translate_skill_table(:ja_jp)
      TABLES = translate_tables(:ja_jp, SKILL_TABLE)

      register_prefix(SKILL_TABLE.prefixes)
      register_prefix(TABLES.keys)
    end
  end
end