bcdice/BCDice

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

Summary

Maintainability
B
4 hrs
Test Coverage
A
97%
# frozen_string_literal: true

require 'bcdice/base'

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

      # ゲームシステム名
      NAME = 'サンサーラ・バラッド'

      # ゲームシステム名の読みがな
      #
      # 「ゲームシステム名の読みがなの設定方法」(docs/dicebot_sort_key.md)を参考にして
      # 設定してください
      SORT_KEY = 'さんさあらはらつと'

      # ダイスボットの使い方
      HELP_MESSAGE = <<~MESSAGETEXT
        SB     通常のD100ロールを行う
        SBS     スワップロールでD100ロールを行う
        SB#x@y     F値をx、C値をyとして通常のD100ロールを行う
        SBS#x@y     F値をx、C値をyとしてスワップロールでD100ロールを行う

        例:
        SB<=85 通常の技能で成功率85%の判定
        SBS<=70 習熟を得た技能で成功率70%の判定
        SBS#3@7<=80 習熟を得た技能で、F値3、C値7で成功率80%の攻撃判定
        SB<57 通常の技能で、能動側の達成値が57の受動判定
        SBS<70 習熟を得た技能で、能動側の達成値が70の受動判定
      MESSAGETEXT

      # ダイスボットで使用するコマンドを配列で列挙する
      register_prefix('SBS?')

      def eval_game_system_specific_command(command)
        debug("eval_game_system_specific_command Begin")

        parser = Command::Parser.new('SBS', 'SB', round_type: round_type)
                                .enable_critical
                                .enable_fumble
                                .restrict_cmp_op_to(nil, :<=, :<)
        cmd = parser.parse(command)

        unless cmd
          return nil
        end

        if cmd.command == 'SB'
          places_text = nil
          total = @randomizer.roll_once(100)
        else
          a = @randomizer.roll_once(10)
          b = @randomizer.roll_once(10)
          places_text = "#{a},#{b}"
          places = [a, b].map { |n| n == 10 ? 0 : n }.sort

          total = places[0] * 10 + places[1]
          total = 100 if total == 0
        end

        result = compare(total, cmd)

        result_str =
          if result.failure?
            "失敗"
          elsif result.success?
            "成功"
          end

        additional_str =
          if result.fumble?
            "ファンブル"
          elsif result.critical?
            "クリティカル"
          end

        sequence = [
          "(D100#{cmd.cmp_op}#{cmd.target_number})",
          places_text,
          total.to_s,
          result_str,
          additional_str,
        ].compact

        result.text = sequence.join(" > ")

        return result
      end

      private

      # @return [Result]
      def compare(total, cmd)
        if [:<=, :<].include?(cmd.cmp_op)
          if !total.send(cmd.cmp_op, cmd.target_number)
            Result.failure(nil)
          elsif fumble_?(total, cmd.fumble)
            Result.new.tap do |r|
              r.success = true
              r.fumble = true
            end
          elsif critical_?(total, cmd.critical)
            Result.critical(nil)
          else
            Result.success(nil)
          end
        elsif fumble_?(total, cmd.fumble)
          # ファンブル優先
          Result.new.tap do |r|
            r.fumble = true
          end
        elsif critical_?(total, cmd.critical)
          Result.new.tap do |r|
            r.critical = true
          end
        else
          Result.new
        end
      end

      # @param total [Integer]
      # @param fumble [Integer, nil]
      # @return [Boolean]
      def fumble_?(total, fumble)
        fumble && (total % 10 <= fumble)
      end

      # @param total [Integer]
      # @param critical [Integer, nil]
      # @return [Boolean]
      def critical_?(total, critical)
        critical && (total % 10 >= critical)
      end
    end
  end
end