bcdice/BCDice

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

Summary

Maintainability
C
1 day
Test Coverage
A
96%
# frozen_string_literal: true

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

      # ゲームシステム名
      NAME = 'ウォーハンマー'

      # ゲームシステム名の読みがな
      SORT_KEY = 'うおおはんまあ'

      # ダイスボットの使い方
      HELP_MESSAGE = <<~INFO_MESSAGE_TEXT
        ・クリティカル表(whHxx/whAxx/whBxx/whLxx)
         "WH部位 クリティカル値"の形で指定します。部位は「H(頭部)」「A(腕)」「B(胴体)」「L(足)」の4カ所です。
         例)whH10 whA5 WHL4
        ・命中判定(WHx@t)
         "WH(命中値)@(種別)"の形で指定します。
         種別は脚の数を数字、翼が付いているものは「W」、手が付いているものは「H」で書きます。
         「2H(二足)」「2W(有翼二足)」「4(四足)」「4H(半人四足)」「4W(有翼四足)」「W(鳥類)」となります。
         命中判定を行って、当たれば部位も表示します。
         なお、種別指定を省略すると「二足」、「@」だけにすると全種別の命中部位を表示します。(コマンドを忘れた時の対応です)
         例)wh60  wh43@4W  WH65@
      INFO_MESSAGE_TEXT

      register_prefix('WH')

      def initialize(command)
        super(command)

        @round_type = RoundType::CEIL # 端数切り上げに設定
      end

      def eval_game_system_specific_command(command)
        output_msg = nil

        case command.upcase

        when /^(WH\d+(@[\dWH]*)?)/i
          attackCommand = Regexp.last_match(1)
          output_msg = getAttackResult(attackCommand)

        when /^(WH[HABTLW]\d+)/i
          criticalCommand = Regexp.last_match(1)
          output_msg = getCriticalResult(criticalCommand)
        end

        return output_msg
      end

      def result_1d100(total, _dice_total, cmp_op, target)
        return Result.nothing if target == '?'
        return nil unless cmp_op == :<=

        if total <= target
          Result.success("成功(成功度#{(target - total) / 10})")
        else
          Result.failure("失敗(失敗度#{(total - target) / 10})")
        end
      end

      def result_1d100_text(total, dice_total, cmp_op, target)
        result = result_1d100(total, dice_total, cmp_op, target)&.text
        if result.nil?
          ""
        else
          " > #{result}"
        end
      end

      ####################            WHFRP関連          ########################
      def getCriticalResult(string)
        # クリティカル効果データ
        whh = [
          '01:打撃で状況が把握出来なくなる。次ターンは1回の半アクションしか行なえない。',
          '02:耳を強打された為、耳鳴りが酷く目眩がする。1Rに渡って一切のアクションを行なえない。',
          '03:打撃が頭皮を酷く傷つけた。【武器技術度】に-10%。治療を受けるまで継続。',
          '04:鎧が損傷し当該部位のAP-1。修理するには(職能:鎧鍛冶)テスト。鎧を着けていないなら1Rの間アクションを行なえない。',
          '05:転んで倒れ、頭がくらくらする。1Rに渡ってあらゆるテストに-30で、立ち上がるには起立アクションが必要。',
          '06:1d10R気絶。',
          '07:1d10分気絶。以後CTはサドンデス。',
          '08:顔がずたずたになって倒れ、以後無防備状態。治療を受けるまで毎Rの被害者のターン開始時に20%で死亡。以後CTはサドンデスを適用。【頑強】テストに失敗すると片方の視力を失う。',
          '09:凄まじい打撃により頭蓋骨が粉砕される。死は瞬時に訪れる。',
          '10:死亡する。いかに盛大に出血し、どのような死に様を見せたのかを説明してもよい。',
        ]
        wha = [
          '01:手に握っていたものを落とす。盾はくくりつけられている為、影響なし。',
          '02:打撃で腕が痺れ、1Rの間使えなくなる。',
          '03:手の機能が失われ、治療を受けるまで回復できない。手で握っていたもの(盾を除く)は落ちる。',
          '04:鎧が損傷する。当該部位のAP-1。修理するには(職能:鎧鍛冶)テスト。鎧を着けていないなら腕が痺れ、1Rの間使えなくなる。',
          '05:腕の機能が失われ、治療を受けるまで回復できない。手で握っていたもの(盾を除く)は落ちる。',
          '06:腕が砕かれる。手で握っていたもの(盾を除く)は落ちる。出血がひどく、治療を受けるまで毎Rの被害者のターン開始時に20%で死亡。以後CTはサドンデスを適用。',
          '07:手首から先が血まみれの残骸と化す。手で握っていたもの(盾を除く)は落ちる。出血がひどく、治療を受けるまで毎Rの被害者のターン開始時に20%で死亡。以後CTはサドンデスを適用。【頑健】テストに失敗すると手の機能を失う。',
          '08:腕は血まみれの肉塊がぶら下がっている状態になる。手で握っていたもの(盾を除く)は落ちる。治療を受けるまで毎Rの被害者のターン開始時に20%で死亡。以後CTはサドンデスを適用。【頑健】テストに失敗すると肘から先の機能を失う。',
          '09:大動脈に傷が及んだ。コンマ数秒の内に損傷した肩から血を噴出して倒れる。ショックと失血により、ほぼ即死する。',
          '10:死亡する。いかに盛大に出血し、どのような死に様を見せたのかを説明してもよい。',
        ]
        whb = [
          '01:打撃で息が詰まる。1Rの間、キャラクターの全てのテストや攻撃に-20%。',
          '02:股間への一撃。苦痛のあまり、1Rに渡って一切のアクションを行なえない。',
          '03:打撃で肋骨がぐちゃぐちゃになる。以後治療を受けるまでの間、【武器技術度】に-10%。',
          '04:鎧が損傷する。当該部位のAP-1。修理するには(職能:鎧鍛冶)テスト。鎧を着けていないなら股間への一撃、1Rに渡って一切のアクションを行なえない。',
          '05:転んで倒れ、息が詰まって悶絶する。1Rに渡ってあらゆるテストに-30の修正、立ち上がるには起立アクションが必要。',
          '06:1d10R気絶。',
          '07:ひどい内出血が起こり、無防備状態。出血がひどく、治療を受けるまで毎Rの被害者のターン開始時に20%で死亡。',
          '08:脊髄が粉砕されて倒れ、以後治療を受けるまで無防備状態。以後CTはサドンデスを適用。【頑強】テストに失敗すると腰から下が不随になる。',
          '09:凄まじい打撃により複数の臓器が破裂し、死は数秒のうちに訪れる。',
          '10:死亡する。いかに盛大に出血し、どのような死に様を見せたのかを説明してもよい。',
        ]
        whl = [
          '01:よろめく。次のターン、1回の半アクションしか行なえない。',
          '02:脚が痺れる。1Rに渡って【移動】は半減し、脚に関連する【敏捷】テストに-20%。回避が出来なくなる。',
          '03:脚の機能が失われ、治療を受けるまで回復しない。【移動】は半減し、脚に関連する【敏捷】テストに-20%。回避が出来なくなる。',
          '04:鎧が損傷する。当該部位のAP-1。修理するには(職能:鎧鍛冶)テスト。鎧を着けていないなら脚が痺れる、1Rに渡って【移動】は半減し、脚に関連する【敏捷】テストに-20%、回避不可になる。',
          '05:転んで倒れ、頭がくらくらする。1Rに渡ってあらゆるテストに-30の修正、立ち上がるには起立アクションが必要。',
          '06:脚が砕かれ、無防備状態。出血がひどく、治療を受けるまで毎Rの被害者のターン開始時に20%で死亡。以後CTはサドンデスを適用。',
          '07:脚は血まみれの残骸と化し、無防備状態になる。治療を受けるまで毎Rの被害者のターン開始時に20%で死亡。以後CTはサドンデスを適用。【頑強】テストに失敗すると足首から先を失う。',
          '08:脚は血まみれの肉塊がぶらさがっている状態。以後無防備状態。治療を受けるまで毎Rの被害者のターン開始時に20%で死亡。以後CTはサドンデスを適用。【頑強】テストに失敗すると膝から下を失う。',
          '09:大動脈に傷が及ぶ。コンマ数秒の内に脚の残骸から血を噴出して倒れ、ショックと出血で死は瞬時に訪れる。',
          '10:死亡する。いかに盛大に出血し、どのような死に様を見せたのかを説明してもよい。',
        ]
        whw = [
          '01:軽打。1ラウンドに渡って、あらゆるテストに-10%。',
          '02:かすり傷。+10%の【敏捷】テストを行い、失敗なら直ちに高度を1段階失う。地上にいるクリーチャーは、次のターンには飛び立てない。',
          '03:損傷する。【飛行移動力】が2点低下する。-10%の【敏捷】テストを行い、失敗なら直ちに高度を1段階失う。地上にいるクリーチャーは、次のターンには飛び立てない。',
          '04:酷く損傷する。【飛行移動力】が4点低下する。-30%の【敏捷】テストを行い、失敗なら直ちに高度を1段階失う。地上にいるクリーチャーは、1d10ターンが経過するまで飛び立てない。',
          '05:翼が使えなくなる。【飛行移動力】が0に低下する。飛行中のものは落下し、高度に応じたダメージを受ける。地上にいるクリーチャーは、怪我が癒えるまで飛び立てない。',
          '06:翼の付け根に傷が開く。【飛行移動力】が0に低下する。飛行中のものは落下し、高度に応じたダメージを受ける。地上にいるクリーチャーは、怪我が癒えるまで飛び立てない。治療を受けるまで毎R被害者のターン開始時に20%の確率で死亡。以後CTはサドンデスを適用。',
          '07:翼は血まみれの残骸と化し、無防備状態になる。【飛行移動力】が0に低下する。飛行中のものは落下し、高度に応じたダメージを受ける。地上にいるクリーチャーは、怪我が癒えるまで飛び立てない。治療を受けるまで毎R被害者のターン開始時に20%の確率で死亡。以後CTはサドンデスを適用。【頑強】テストに失敗すると飛行能力を失う。',
          '08:翼が千切れてバラバラになり、無防備状態になる。【飛行移動力】が0に低下する。飛行中のものは落下し、高度に応じたダメージを受ける。地上にいるクリーチャーは、怪我が癒えるまで飛び立てない。治療を受けるまで毎R被害者のターン開始時に20%の確率で死亡。以後CTはサドンデスを適用。飛行能力を失う。',
          '09:大動脈が切断された。コンマ数秒の内に血を噴き上げてくずおれる、ショックと出血で死は瞬時に訪れる。',
          '10:死亡する。いかに盛大に出血し、どのような死に様を見せたのかを説明してもよい。',
        ]

        criticalTable = [
          5, 7, 9, 10, 10, 10, 10, 10, 10, 10, # 01-10
          5, 6, 8, 9, 10, 10, 10, 10, 10, 10, # 11-20
          4, 6, 8, 9, 9, 10, 10, 10, 10, 10, # 21-30
          4, 5, 7, 8, 9, 9, 10, 10, 10, 10, # 31-40
          3, 5, 7, 8, 8, 9, 9, 10, 10, 10, # 41-50
          3, 4, 6, 7, 8, 8, 9, 9, 10, 10, # 51-60
          2, 4, 6, 7, 7, 8, 8, 9, 9, 10, # 61-70
          2, 3, 5, 6, 7, 7, 8, 8, 9, 9,  # 71-80
          1, 3, 5, 6, 6, 7, 7, 8, 8, 9,  # 81-90
          1, 2, 4, 5, 6, 6, 7, 7, 8, 8,  # 91-00
        ]

        unless /WH([HABTLW])(\d+)/ =~ string
          return '1'
        end

        partsWord = Regexp.last_match(1) # 部位
        criticalValue = Regexp.last_match(2).to_i # クリティカル値
        criticalValue = 10 if criticalValue > 10
        criticalValue = 1 if criticalValue < 1

        whpp = ''
        whppp = ''

        case partsWord
        when /H/i
          whpp = '頭部'
          whppp = whh
        when /A/i
          whpp = '腕部'
          whppp = wha
        when /[TB]/i
          whpp = '胴体'
          whppp = whb
        when /L/i
          whpp = '脚部'
          whppp = whl
        when /W/i
          whpp = '翼部'
          whppp = whw
        end

        dice_now = @randomizer.roll_once(100)

        crit_no = ((dice_now - 1) / 10).to_i * 10
        crit_num = criticalTable[crit_no + criticalValue - 1]

        resultText = whppp[crit_num - 1]
        if crit_num >= 5
          resultText += 'サドンデス×'
        else
          resultText += 'サドンデス○'
        end

        output = "#{whpp}CT表(#{dice_now}+#{criticalValue}) > #{resultText}"

        return output
      end

      def wh_atpos(pos_num, pos_type) # WHFRP2命中部位表
        debug("wh_atpos begin pos_type", pos_type)
        pos_2l = [
          '二足',
          15, '頭部',
          35, '右腕',
          55, '左腕',
          80, '胴体',
          90, '右脚',
          100, '左脚',
        ]
        pos_2lw = [
          '有翼二足',
          15, '頭部',
          25, '右腕',
          35, '左腕',
          45, '右翼',
          55, '左翼',
          80, '胴体',
          90, '右脚',
          100, '左脚',
        ]
        pos_4l = [
          '四足',
          15, '頭部',
          60, '胴体',
          70, '右前脚',
          80, '左前脚',
          90, '右後脚',
          100, '左後脚',
        ]
        pos_4la = [
          '半人四足',
          10, '頭部',
          20, '右腕',
          30, '左腕',
          60, '胴体',
          70, '右前脚',
          80, '左前脚',
          90, '右後脚',
          100, '左後脚',
        ]
        pos_4lw = [
          '有翼四足',
          10, '頭部',
          20, '右翼',
          30, '左翼',
          60, '胴体',
          70, '右前脚',
          80, '左前脚',
          90, '右後脚',
          100, '左後脚',
        ]
        pos_b = [
          '鳥',
          15, '頭部',
          35, '右翼',
          55, '左翼',
          80, '胴体',
          90, '右脚',
          100, '左脚',
        ]

        wh_pos = [pos_2l, pos_2lw, pos_4l, pos_4la, pos_4lw, pos_b]

        pos_t = 0
        debug("pos_type", pos_type)
        if pos_type != ""
          case pos_type
          when /@(2W|W2)/i
            pos_t = 1
          when /@(4W|W4)/i
            pos_t = 4
          when /@(4H|H4)/i
            pos_t = 3
          when /@4/i
            pos_t = 2
          when /@W/i
            pos_t = 5
          else
            unless  /@(2H|H2|2)/i =~ pos_type
              pos_t = -1
            end
          end
        end

        output = ""

        debug("pos_t", pos_t)
        if pos_t < 0
          wh_pos.each do |pos_i|
            output += get_wh_atpos_message(pos_i, pos_num)
          end
        else
          pos_i = wh_pos[pos_t]
          output += get_wh_atpos_message(pos_i, pos_num)
        end

        return output
      end

      def get_wh_atpos_message(pos_i, pos_num)
        output = ""

        output += ' ' + pos_i[0] + ":"

        1.step(pos_i.length + 1, 2) do |i|
          if pos_num <= pos_i[i]
            output += pos_i[i + 1]
            break
          end
        end

        return output
      end

      def getAttackResult(string)
        debug("getAttackResult begin string", string)

        pos_type = ""

        if /(.+)(@.*)/ =~ string
          string = Regexp.last_match(1)
          pos_type = Regexp.last_match(2)
          debug("pos_type", pos_type)
        end

        unless /WH(\d+)/i =~ string
          return '1'
        end

        diff = Regexp.last_match(1).to_i

        total_n = @randomizer.roll_once(100)

        output = "(#{string}) > #{total_n}"
        output += result_1d100_text(total_n, total_n, :<=, diff)

        pos_num = (total_n % 10) * 10 + (total_n / 10).to_i
        pos_num = 100 if total_n >= 100

        output += wh_atpos(pos_num, pos_type) if total_n <= diff

        return output
      end
    end
  end
end