bcdice/BCDice

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

Summary

Maintainability
C
7 hrs
Test Coverage
A
98%
# frozen_string_literal: true

require 'bcdice/dice_table/table'
require 'bcdice/dice_table/range_table'

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

      # ゲームシステム名
      NAME = '獸ノ森'

      # ゲームシステム名の読みがな
      SORT_KEY = 'けもののもり'

      # ダイスボットの使い方
      HELP_MESSAGE = <<~MESSAGETEXT
        ・行為判定(成功度自動算出)(P119): KAx[±y]
        ・継続判定(成功度+1固定): KCx[±y]
           x=目標値
           y=目標値への修正(任意) x+y-z のように複数指定可能
             例1)KA7+3 → 目標値7にプラス3の修正を加えた行為判定
             例2)KC6 → 目標値6の継続判定
        ・罠動作チェック+獲物表(P163): CTR
           罠ごとに1D12を振り、12が出た場合には生き物が罠を動作させ、その影響を受けている。
        ・各種表(基本ルールブック)
          ・大失敗表(P120): FT
          ・能力値ランダム決定表(P121): RST
          ・ランダム所要時間表(P122): RTT
          ・ランダム消耗表(P122): RET
          ・ランダム天気表(P128): RWT
          ・ランダム天気持続表(P128): RWDT
          ・ランダム遮蔽物表(屋外)(P140): ROMT
          ・ランダム遮蔽物表(屋内)(P140): RIMT
          ・逃走体験表(P144): EET
          ・食材採集表(P157): GFT
          ・水採集表(P157): GWT
          ・白の魔石効果表(P186): WST
        ・部位ダメージ関連の表(参照先ページはリプレイ&データブック「嚙神ノ宴」のもの)
          ・人間部位表(P216): HPT
          ・部位ダメージ段階表(P217): PDT
          ・四足動物部位表(P225): QPT
          ・無足動物部位表(P225): APT
          ・二足動物部位表(P226): TPT
          ・鳥部位表(P226): BPT
          ・頭足動物部位表(P227): CPT
          ・昆虫部位表(P227): IPT
          ・蜘蛛部位表(P228): SPT
      MESSAGETEXT

      def eval_game_system_specific_command(command)
        case command
        when /KA\d[-+\d]*/ then check_1D12(command, true)
        when /KC\d[-+\d]*/ then check_1D12(command, false)
        when 'CTR' then get_trap_result()
        when 'EET' then get_escape_experience_table_result(command)
        else roll_tables(command, TABLES)
        end
      end

      def check_1D12(command, is_action_judge)
        debug('獸ノ森の1d12判定')
        m = /K[AC](\d[-+\d]*)/.match(command)
        return nil unless m

        # 修正込みの目標値を計算
        target_total = ArithmeticEvaluator.eval(m[1])
        debug('target_total', target_total)

        # 行為判定の成功度は [目標値の10の位の数+1]
        # 継続判定の成功度は固定で+1
        success_degree = is_action_judge ? target_total / 10 + 1 : 1

        dice_total = @randomizer.roll_once(12)
        debug('dice_total, target_total, success_degree = ', dice_total, target_total, success_degree)

        if dice_total == 12
          Result.fumble("(1D12<=#{target_total}) > #{dice_total} > 大失敗")
        elsif dice_total == 11
          Result.critical("(1D12<=#{target_total}) > #{dice_total} > 大成功(成功度+#{success_degree}, 次の継続判定の目標値を10に変更)")
        elsif dice_total <= target_total
          Result.success("(1D12<=#{target_total}) > #{dice_total} > 成功(成功度+#{success_degree})")
        else
          Result.failure("(1D12<=#{target_total}) > #{dice_total} > 失敗")
        end
      end

      def get_trap_result()
        tra_check_num = @randomizer.roll_once(12)
        unless tra_check_num == 12
          return Result.new("罠動作チェック(1D12) > #{tra_check_num} > 罠は動作していなかった")
        end

        chase_num = @randomizer.roll_once(12)
        chase = case chase_num
                when 1, 2, 3, 4 then '小型動物'
                when 5, 6, 7, 8 then '大型動物'
                when 9, 10, 11, 12 then '人間の放浪者'
                end
        Result.new("罠動作チェック(1D12) > #{tra_check_num} > 罠が動作していた! > 獲物表(#{chase_num}) > #{chase}が罠にかかっていた")
      end

      def get_escape_experience_table_result(command)
        escape_experience = roll_tables(command, TABLES)
        escape_duration = @randomizer.roll_once(12)
        Result.new("#{escape_experience} (再登場: #{escape_duration}時間後)")
      end

      TABLES = {
        'FT' => DiceTable::RangeTable.new(
          '大失敗表',
          '1D12',
          [
            [1..3, '【余裕】が3点減少する(最低0まで)'],
            [4..5, 'ランダムな荷物1個が落ちて行方不明になる(大失敗したエリアのアイテム調査で見つけることが可能)'],
            [6..7, 'ランダムな荷物1個が破壊される'],
            [8..9, 'ランダム天気表(RWT)を使用し、結果をターンの終了まで適用する'],
            [10,   'ランダムな準備している小道具1個が破壊される'],
            [11,   '着装している防具が破壊される'],
            [12,   '準備している武器が破壊される'],
          ]
        ),
        'RST' => DiceTable::RangeTable.new(
          '能力値ランダム決定表',
          '1D12',
          [
            [1..2,   '【移動】'],
            [3..4,   '【格闘】'],
            [5..6,   '【射撃】'],
            [7..8,   '【製作】'],
            [9..10,  '【察知】'],
            [11..12, '【自制】'],
          ]
        ),
        'RTT' => DiceTable::RangeTable.new(
          'ランダム所要時間表',
          '1D12',
          [
            [1..3,   '2'],
            [4..6,   '3'],
            [7..9,   '4'],
            [10..12, '5'],
          ]
        ),
        'RET' => DiceTable::RangeTable.new(
          'ランダム消耗表',
          '1D12',
          [
            [1..3,   '0'],
            [4..6,   '1'],
            [7..9,   '2'],
            [10..12, '4'],
          ]
        ),
        'RWT' => DiceTable::RangeTable.new(
          'ランダム天気表',
          '1D12',
          [
            [1..2,   '濃霧'],
            [3..4,   '大雨'],
            [5..6,   '雷雨'],
            [7..8,   '強風'],
            [9..10,  '酷暑'],
            [11..12, '極寒'],
          ]
        ),
        'RWDT' => DiceTable::RangeTable.new(
          'ランダム天気持続表',
          '1D12',
          [
            [1..2,   '1ターン'],
            [3..4,   '3ターン'],
            [5..6,   '6ターン'],
            [7..8,   '24ターン'],
            [9..10,  '72ターン'],
            [11..12, '156ターン'],
          ]
        ),
        'ROMT' => DiceTable::RangeTable.new(
          'ランダム遮蔽物表(屋外)',
          '1D12',
          [
            [1..2,   '【藪】耐久度3,軽減値1,特殊効果:コンタクト内のキャラクターに対する射撃攻撃判定に-1の修正を付加'],
            [3..5,   '【木】耐久度5,軽減値2,特殊効果:コンタクト内のキャラクターに対する射撃攻撃判定に-1の修正を付加'],
            [6..8,   '【大木】耐久度7,軽減値3,特殊効果:コンタクト内のキャラクターに対する射撃攻撃判定に-2の修正を付加'],
            [9..10,  '【岩】耐久度6,軽減値4,特殊効果:コンタクト内のキャラクターに対する射撃攻撃判定に-1の修正を付加/コンタクト内で行われる格闘攻撃のダメージ+1'],
            [11..12, '【岩壁】耐久度8,軽減値4,特殊効果:コンタクト内のキャラクターに対する射撃攻撃判定に-2の修正を付加/コンタクト内で行われる格闘攻撃のダメージ+2'],
          ]
        ),
        'RIMT' => DiceTable::RangeTable.new(
          'ランダム遮蔽物表(屋内)',
          '1D12',
          [
            [1..4,  '【木材の壁】耐久度4,軽減値2,特殊効果:コンタクト内のキャラクターに対する射撃攻撃判定に-1の修正を付加'],
            [5..8,  '【木材の扉】耐久度4,軽減値2,特殊効果:コンタクト内のキャラクターに対する射撃攻撃判定に-1、接触判定と突撃判定に-2の修正を付加'],
            [9..12, '【木製家具】耐久度3,軽減値2,特殊効果:コンタクト内で行われる格闘攻撃のダメージ+1'],
          ]
        ),
        'EET' => DiceTable::RangeTable.new(
          '逃走体験表',
          '1D12',
          [
            [1..3,   '【余裕】が0になる'],
            [4..6,   '任意の【絆】を合計2点減少する'],
            [7..9,   '全ての荷物を失う(逃走したエリアに配置され、調査で発見可能)'],
            [10..12, '全ての武器と防具と小道具と荷物を失う(逃走したエリアに配置され、調査で発見可能)'],
          ]
        ),
        'GFT' => DiceTable::RangeTable.new(
          '食材採集表',
          '1D12',
          [
            [1..2,  '食べられる根(栄養価:2)'],
            [3..5,  '食べられる草(栄養価:3)'],
            [6..8,  '食べられる実(栄養価:5)'],
            [9..10, '小型動物(栄養価:10)'],
            [11,    '大型動物(栄養価:40)'],
            [12,    '気持ち悪い虫(栄養価:1)'],
          ]
        ),
        'GWT' => DiceTable::RangeTable.new(
          '水採集表',
          '1D12',
          [
            [1..6,  '汚水'],
            [7..11, '飲料水'],
            [12,    '毒水'],
          ]
        ),
        'WST' => DiceTable::Table.new(
          '白の魔石効果表',
          '1D12',
          [
            '役に立たないものの色を変える',
            '役に立たないものを大きくする',
            '役に立たないものを小さくする',
            '役に立たないものを保存する',
            '役に立たないものを復元する',
            '役に立たないものを召喚する',
            '役に立たないものを動かす',
            '役に立たないものを増やす',
            '役に立たないものを貼り付ける',
            '役に立たないものを作り出す',
            '小型動物を召喚する',
            '大型動物を召喚する',
          ]
        ),
        'HPT' => DiceTable::RangeTable.new(
          '人間部位表',
          '1D12',
          [
            [1..2,  '右腕部'],
            [3..4,  '左腕部'],
            [5..6,  '右脚部'],
            [7..8,  '左脚部'],
            [9..11, '胴部'],
            [12,    '頭部'],
          ]
        ),
        'PDT' => DiceTable::RangeTable.new(
          '部位ダメージ段階表',
          '1D12',
          [
            [1..6,  '軽傷'],
            [7..10, '重傷'],
            [11,    '破壊'],
            [12,    '喪失'],
          ]
        ),
        'QPT' => DiceTable::RangeTable.new(
          '四足動物部位表',
          '1D12',
          [
            [1..2,    '異形'],
            [3,       '武器'],
            [4,       '右前脚部'],
            [5,       '左前脚部'],
            [6,       '右後脚部'],
            [7,       '左後脚部'],
            [8..10,   '胴部'],
            [11..12,  '頭部'],
          ]
        ),
        'APT' => DiceTable::RangeTable.new(
          '無足動物部位表',
          '1D12',
          [
            [1..3,    '異形'],
            [4..6,    '武器'],
            [7..10,   '胴部'],
            [11..12,  '頭部'],
          ]
        ),
        'TPT' => DiceTable::RangeTable.new(
          '二足動物部位表',
          '1D12',
          [
            [1,     '異形'],
            [2,     '武器'],
            [3,     '右腕部'],
            [4,     '左腕部'],
            [5..6,  '右脚部'],
            [7..8,  '左脚部'],
            [9..11, '胴部'],
            [12,    '頭部'],
          ]
        ),
        'BPT' => DiceTable::RangeTable.new(
          '鳥部位表',
          '1D12',
          [
            [1,     '異形'],
            [2,     '武器'],
            [3..4,  '右翼(右腕部)'],
            [5..6,  '左翼(左腕部)'],
            [7,     '右脚部'],
            [8,     '左脚部'],
            [9..11, '胴部'],
            [12,    '頭部'],
          ]
        ),
        'CPT' => DiceTable::RangeTable.new(
          '頭足動物部位表',
          '1D12',
          [
            [1,     '異形'],
            [2,     '武器'],
            [3,     '右腕部'],
            [4,     '左腕部'],
            [5..7,  '右脚部'],
            [8..10, '左脚部'],
            [11,    '胴部'],
            [12,    '頭部'],
          ]
        ),
        'IPT' => DiceTable::RangeTable.new(
          '昆虫部位表',
          '1D12',
          [
            [1..2,    '異形'],
            [3,       '武器'],
            [4,       '右前脚部'],
            [5,       '左前脚部'],
            [6,       '右中脚部'],
            [7,       '左中脚部'],
            [8,       '右後脚部'],
            [9,       '左後脚部'],
            [10..11,  '胴部'],
            [12,      '頭部'],
          ]
        ),
        'SPT' => DiceTable::RangeTable.new(
          '蜘蛛部位表',
          '1D12',
          [
            [1,   '異形'],
            [2,   '武器'],
            [3,   '右第一脚部'],
            [4,   '左第一脚部'],
            [5,   '右第二脚部'],
            [6,   '左第二脚部'],
            [7,   '右第三脚部'],
            [8,   '左第三脚部'],
            [9,   '右第四脚部'],
            [10,  '左第四脚部'],
            [11,  '胴部'],
            [12,  '頭部'],
          ]
        ),
      }.freeze

      register_prefix('K[AC]', 'CTR', TABLES.keys)
    end
  end
end