bcdice/BCDice

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

Summary

Maintainability
B
5 hrs
Test Coverage
A
99%
# frozen_string_literal: true

require 'bcdice/game_system/beginning_idol/table'

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

      # ゲームシステム名
      NAME = 'ビギニングアイドル'

      # ゲームシステム名の読みがな
      SORT_KEY = 'ひきにんくあいとる'

      # ダイスボットの使い方
      HELP_MESSAGE = <<~INFO_MESSAGE_TEXT
        これは、2015年に新書サイズで発売された『駆け出しアイドルRPG ビギニングアイドル チャレンジガールズ』およびそのサプリメントに対応したコマンドです。

        ・パフォーマンス [r]PDn[+m/-m](r:場に残った出目 n:振る数 m:修正値)
        ・ワールドセッティング仕事表 BWT:大手芸能プロ LWT:弱小芸能プロ
         TWT:ライブシアター CWT:アイドル部 LO[n]:地方アイドル(n:チャンス)
         SU:情熱の夏 WI:ぬくもりの冬 NA:大自然 GA:女学園 BA:アカデミー
        ・仕事表 WT VA:バラエティ MU:音楽関係 DR:ドラマ関係
         VI:ビジュアル関係 SP:スポーツ CHR:クリスマス PAR:パートナー関係
         SW:お菓子 AN:動物 MOV:映画 FA:ファンタジー
        ・ランダムイベント RE
        ・ハプニング表 HA
        ・特技リスト AT[n](n:分野No.)
        ・アイドルスキル修得表 SGT:チャレンジガールズ RS:ロードトゥプリンス
        ・変調 BT[n](n:発生数)
        ・アイテム IT[n](n:獲得数)
        ・アクセサリー ACT:種別決定 ACB:ブランド決定 ACE:効果表
        ・衣装 DT:チャレンジガールズ RC:ロードトゥプリンス FC:フォーチュンスターズ
        ・無茶ぶり表 LUR:地方アイドル SUR:情熱の夏 WUR:ぬくもりの冬
         NUR:大自然 GUR:女学園 BUR:アカデミー
        ・センタールール HW:向かい風シーン表 FL:駆け出しシーン表 LN:孤独表
         マイスキル【MS:名前決定 MSE:効果表】 演出表【ST FST:ファンタジー】
        ・合宿ルール 散策表【SH:ショッピングモール MO:山 SEA:海 SPA:温泉街】
         TN:夜語りシチュエーション表 成長表【CG:コモン GG:ゴールド】
        ・サビ表 CHO SCH:情熱の夏 WCH:ぬくもりの冬 NCH:大自然
         GCH:女性向け PCH:力強い
        ・キャラ空白表 CBT:チャレンジガールズ RCB:ロードトゥプリンス
        ・趣味空白表 HBT:チャレンジガールズ RHB:ロードトゥプリンス
        ・マスコット暴走表 RU
        ・アイドル熱湯風呂 nC:バーストタイム(n:温度) BU:バースト表
        ・攻撃 n[S]A[r][+m/-m](n:振る数 S:失敗しない r:取り除く出目 m:修正値)
        ・かんたんパーソン表 SIP
        ・会場表
         BVT:大手芸能プロ LVT:弱小芸能プロ TVT:ライブシアター CVT:アイドル部
        ・場所表
         BST:大手芸能プロ LST:弱小芸能プロ TST:ライブシアター CST:アイドル部
        ・プレッシャー種別決定表
         BPT:大手芸能プロ LPT:弱小芸能プロ TPT:ライブシアター CPT:アイドル部
        ・道具表
         BIT:大手芸能プロ LIT:弱小芸能プロ TIT:ライブシアター CIT:アイドル部
        []内は省略可 D66入れ替えあり
      INFO_MESSAGE_TEXT

      register_prefix(
        SKILL_TABLE.prefixes,
        'IT',
        'BT',
        '\d{2}C',
        '\d+S?A',
        '[1-7]*PD'
      )

      def initialize(command)
        super(command)

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

      def result_nd6(total, dice_total, _value_list, cmp_op, target)
        return nil if target == '?'
        return nil unless cmp_op == :>=

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

      alias check_2D6 check_nD6

      def eval_game_system_specific_command(command)
        roll_attack(command) ||
          roll_burst(command) ||
          roll_performance(command) ||
          self.class::SKILL_TABLE.roll_command(@randomizer, command) ||
          self.class::ITEM_TABLE.roll_command(@randomizer, command) ||
          self.class::BAD_STATUS_TABLE.roll_command(@randomizer, command) ||
          self.class::LOCAL_WORK_TABLE.roll_command(@randomizer, command) ||
          roll_tables(command, self.class::TABLES)
      end

      private

      def roll_burst(command)
        m = /^(\d{2})C$/.match(command)
        unless m
          return nil
        end

        degrees = m[1].to_i
        if (degrees < 45) || (degrees > 55)
          return nil
        end

        counts =
          if degrees <= 49
            3
          elsif degrees <= 52
            4
          elsif degrees <= 54
            5
          else
            6
          end

        dice_list = @randomizer.roll_barabara(counts, 6).sort
        total = dice_list.sum() + degrees

        result =
          if total >= 80
            translate("BeginningIdol.burst.burst")
          elsif total >= 75
            translate("BeginningIdol.burst.critical_success")
          elsif total >= 65
            translate("BeginningIdol.burst.success")
          else
            translate("failure")
          end

        name = translate("BeginningIdol.burst.name")
        return "#{name} > #{degrees}+[#{dice_list.join(',')}] > #{total} > #{result}"
      end

      def roll_attack(command)
        m = /^(\d+)(S?)A([1-6]*)([+-]\d+)?$/.match(command)
        unless m
          return nil
        end

        counts = m[1].to_i
        return nil if counts <= 0

        sure = !m[2].empty?
        remove = m[3].each_char.map(&:to_i)
        adjust = m[4].to_i
        adjust_str = Format.modifier(adjust)

        dice = @randomizer.roll_barabara(counts, 6).sort
        dice_str = dice.join(",")

        dice -= remove

        text = "#{translate('BeginningIdol.attack.name')} > [#{dice_str}]#{adjust_str} > "

        unless (dice.count == counts) || dice.empty?
          text += "[#{dice.join(',')}]#{adjust_str} > "
        end

        if sure || (dice.count == dice.uniq.count)
          total = [dice.sum() + adjust.to_i, 0].max
          text += format(translate('BeginningIdol.attack.damage'), total: total)
        else
          text += translate('failure')
        end
        return text
      end

      def roll_performance(command)
        m = /^([1-7]*)PD(\d+)([+-]\d+)?$/.match(command)
        unless m
          return nil
        end

        counts = m[2].to_i
        return nil if counts <= 0

        carry = m[1].chars.map(&:to_i).sort
        modifier = m[3].to_i

        dice_list = @randomizer.roll_barabara(counts, 6).sort
        all_dice = (dice_list + carry).sort
        filtered = select_uniqs(all_dice)

        title = carry.empty? ? translate("BeginningIdol.PD.paformance") : translate("BeginningIdol.PD.symphony")

        result =
          if carry.empty?
            result_performance(filtered, modifier, all_dice)
          else
            result_symphony(filtered, modifier)
          end

        sequence = [
          title,
          format_dice_list(dice_list, carry, modifier),
          result,
        ]

        return sequence.join(" > ")
      end

      def select_uniqs(dice_list)
        dice_list.group_by(&:itself).to_a
                 .select { |_, arr| arr.size == 1 }
                 .map { |dice, _| dice }
                 .sort
      end

      def format_dice_list(dice_list, carry, modifier)
        if carry.empty?
          "[#{dice_list.join(',')}]#{Format.modifier(modifier)}"
        else
          "[#{dice_list.join(',')}],[#{carry.join(',')}]#{Format.modifier(modifier)}"
        end
      end

      def result_performance(list, modifier, all_list)
        if list.empty?
          format(translate("BeginningIdol.PD.miracle"), value: modifier + 10)
        elsif list == [1, 2, 3, 4, 5, 6]
          format(translate("BeginningIdol.PD.perfect_miracle"), value: modifier + 30)
        elsif list.size != all_list.size
          "[#{list.join(',')}]#{Format.modifier(modifier)} > #{list.sum() + modifier}"
        else
          (list.sum() + modifier).to_s
        end
      end

      def result_symphony(list, modifier)
        if list.empty?
          format(translate("BeginningIdol.PD.miracle_synchro"), value: modifier + 15)
        elsif list == [1, 2, 3, 4, 5, 6]
          perfect_miracle = format(translate("BeginningIdol.PD.perfect_miracle"), value: modifier + 30)
          "[#{list.join(',')}]#{Format.modifier(modifier)} > #{perfect_miracle}"
        else
          "[#{list.join(',')}]#{Format.modifier(modifier)} > #{list.sum() + modifier}"
        end
      end
    end
  end
end