bcdice/BCDice

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

Summary

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

require 'bcdice/arithmetic_evaluator'
require 'bcdice/dice_table/range_table'

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

      # ゲームシステム名
      NAME = 'メタルヘッド'

      # ゲームシステム名の読みがな
      SORT_KEY = 'めたるへつと'

      # ダイスボットの使い方
      HELP_MESSAGE = <<~MESSAGETEXT
        ・アビリティロール  AR>=目標値
        ・スキルロール      SR<=目標値(%)
        ・命中判定ロール    HR<=目標値(%)

          例)AR>=5
          例)SR<=(40+25)
          例)HR<=(50-10)

          これらのロールで成否、絶対成功/絶対失敗、クリティカル/アクシデントを自動判定します。

        ・クリティカルチャート  CC
        ・アクシデントチャート  射撃・投擲用:ACL  格闘用:ACS
        ・戦闘結果チャート      CRCsn   s=耐久レベル(SUV) n=数値

          例)CRCA61 SUV=Aを対象とした数値61(62に変換される)の戦闘結果を参照する。
          例)CRCM98 対物で数値98の戦闘結果を参照する。
      MESSAGETEXT

      register_prefix('AR', 'SR', 'HR<=', 'CC', 'ACT', 'ACL', 'ACS', 'CRC[A-Z]')

      def eval_game_system_specific_command(command)
        result = roll_tables(command, TABLES)
        return result if result

        case command
        when /\ACRC(\w)(\d+)\z/
          suv = Regexp.last_match(1)
          num = Regexp.last_match(2)
          return mh_crc_table(suv, num)
        when /\AHR<=(.+)/
          target = ArithmeticEvaluator.eval(
            Regexp.last_match(1), round_type: @round_type
          )
          return rollHit(target)
        end

        return nil
      end

      def change_text(string)
        string = string.gsub(/^(S)?AR/i) { "#{Regexp.last_match(1)}2D6" }
        string = string.gsub(/^(S)?SR/i) { "#{Regexp.last_match(1)}1D100" }
        return string
      end

      def result_2d6(_total, dice_total, _dice_list, cmp_op, _target)
        return nil if cmp_op != :>=

        if dice_total >= 12
          Result.critical("絶対成功")
        elsif dice_total <= 2
          Result.fumble("絶対失敗")
        end
      end

      def rollHit(target)
        total = @randomizer.roll_once(100)
        resultText = getHitResult(total, total, target)

        text = "(1D100<=#{target}) > #{total}#{resultText}"

        return text
      end

      def result_1d100(_total, dice_total, cmp_op, _target)
        return nil unless cmp_op == :<=

        if dice_total <= 5
          Result.critical("絶対成功")
        elsif dice_total >= 96
          Result.fumble("絶対失敗")
        end
      end

      def getHitResult(total_n, _dice_n, diff)
        diceValue = total_n % 100
        dice1 = diceValue % 10 # 1の位を代入

        debug("total_n", total_n)

        return ' > 失敗' if total_n > diff

        return ' > 成功(クリティカル)' if  dice1 == 1
        return ' > 失敗(アクシデント)' if  dice1 == 0

        return ' > 成功'
      end

      # 戦闘結果チャートを振る
      # @param [String] suv 耐久レベル
      # @param [String] num 数値
      # @return [String] 振った結果
      def mh_crc_table(suv, num)
        header_parts = ['戦闘結果チャート', num]
        separator = ' > '

        suv = suv.to_s.upcase
        numbuf = num.to_i
        if numbuf < 1
          return (header_parts + ['数値が不正です']).join(separator)
        end

        num_d1 = numbuf % 10
        debug("num_d1[#{num_d1}]")
        if num_d1 == 1
          numbuf += 1
        end
        if num_d1 == 0
          numbuf -= 1
        end
        num_d1 = numbuf % 10
        debug("num_d1[#{num_d1}]")

        table_point = [
          nil, # 0
          nil, # 1
          "腕部", # 2
          "腕部", # 3
          "脚部", # 4
          "脚部", # 5
          "胴部", # 6
          "胴部", # 7
          "胴部", # 8
          "頭部", # 9
        ]

        table_damage = {
          'S' => [{'N' => 2}, {'LW' => 16}, {'MD' => 46}, {'MW' => 56}, {'HD' => 76}, {'HW' => 96}, {'MO' => 106}, {'K' => 116}],
          'A' => [{'LW' => 2}, {'MW' => 46}, {'HW' => 76}, {'MO' => 96}, {'K' => 116}],
          'B' => [{'LW' => 2}, {'MW' => 36}, {'HW' => 66}, {'MO' => 96}, {'K' => 106}],
          'C' => [{'LW' => 2}, {'MW' => 26}, {'HW' => 66}, {'MO' => 86}, {'K' => 106}],
          'D' => [{'LW' => 2}, {'MW' => 26}, {'HW' => 46}, {'MO' => 76}, {'K' => 96}],
          'E' => [{'LW' => 2}, {'MW' => 26}, {'HW' => 39}, {'MO' => 54}, {'K' => 76}],
          'F' => [{'LW' => 2}, {'MW' => 16}, {'HW' => 39}, {'MO' => 54}, {'K' => 66}],
          'G' => [{'LW' => 2}, {'MW' => 6}, {'HW' => 16}, {'MO' => 26}, {'K' => 39}],
          'M' => [{'0'  => 2}, {'1' => 22}, {'2' => 42}, {'3' => 62}, {'4' => 82}, {'5' => 92}, {'6' => 102}, {'8' => 112}],
        }

        if table_damage[suv].nil?
          return (header_parts + [
            "耐久レベル(SUV)[#{suv}]",
            "耐久レベル(SUV)の値が不正です",
          ]).join(separator)
        end

        damage_level = ''
        table_damage[suv].each do |v|
          v.each do |d, n|
            debug("suv[#{suv}] #{v} #{d} #{n}")
            if n <= numbuf
              damage_level = d
            end
          end
        end

        result_parts = []

        if numbuf != num.to_i
          result_parts.push(numbuf.to_s)
        end

        if suv == 'M'
          result_parts.push('耐物', "HP[#{damage_level}]")
        else
          result_parts.push(
            "耐久レベル(SUV)[#{suv}]",
            "部位[#{table_point[num_d1]}] : 損傷種別[#{damage_level}]"
          )
        end

        return (header_parts + result_parts).join(separator)
      end

      # 表を振った結果の整形処理
      TABLE_ROLL_RESULT_FORMATTER = lambda do |table, result|
        [table.name, result.sum, result.content].join(' > ')
      end

      # 表の集合
      TABLES = {
        'CC' => DiceTable::RangeTable.new(
          'クリティカルチャート',
          '1D10',
          [
            [1,     '相手は知覚系に多大なダメージを受けた。PERを1にして頭部にHWのダメージ、および心理チェック。'],
            [2,     '相手の運動神経を断ち切った。DEXを1にして腕部にHWのダメージ、および心理チェック。さらに腕に持っていた武器などは落としてしまう。'],
            [3,     '相手の移動手段は完全に奪われた。REFを1にして脚部にHWダメージと心理チェック。また、次回からのこちらの攻撃は必ず命中する。'],
            [4..5,  '相手の急所に命中。激痛のため気絶した上、胴にHWダメージ。'],
            [6,     '効果的な一撃。胴にHWダメージ。心理チェック。'],
            [7,     '効果的な一撃。胴にMOダメージ。心理チェック。'],
            [8..10, '君の一撃は相手の中枢を完全に破壊した。即死である。'],
          ],
          &TABLE_ROLL_RESULT_FORMATTER
        ),
        'ACL' => DiceTable::RangeTable.new(
          'アクシデントチャート(射撃・投擲)',
          '1D10',
          [
            [1..7, 'ささいなミス。特にペナルティーはない。'],
            [8,    '不発、またはジャム。弾を取り出さねばならない物は次のターンは射撃できない。'],
            [9,    'ささいな故障。可能なら次のターンから個別武器のスキルロールで修理を行える。'],
            [10,   '武器の暴発、または爆発。頭部HWの心理効果ロール。さらに、その武器は破壊されPERとDEXのどちらか、または両方に計2ポイントのマイナスを与える。(遠隔操作の場合、射手への被害は無し)'],
          ],
          &TABLE_ROLL_RESULT_FORMATTER
        ),
        'ACS' => DiceTable::RangeTable.new(
          'アクシデントチャート(格闘)',
          '1D10',
          [
            [1..3, '足を滑らせて転倒し、起き上がるまで相手に+20の命中修正を与える。'],
            [4..6, '手を滑らせて、武器を落とす。素手の時は関係ない。'],
            [7..9, '使用武器の破壊。素手戦闘のときはMWのダメージを受ける。'],
            [10,   '手を滑らせ、不幸にも武器は飛んでいき、5m以内に人がいれば誰かに刺さるか、または打撃を与えるかもしれない。ランダムに決定し、普通どおり判定を続ける。素手のときは関係ない。'],
          ],
          &TABLE_ROLL_RESULT_FORMATTER
        ),
      }.freeze
    end
  end
end