class BCDice::GameSystem::BattleTech

Constants

HELP_MESSAGE

ダイスボットの使い方

HitPart

命中部位を表す構造体 @!attribute [rw] name

@return [String] 部位名

@!attribute [rw] critical_hit_may_occur

@return [Boolean]  致命的命中が発生し得るか
ID

ゲームシステムの識別子

LRM_LIMIT
NAME

ゲームシステム名

NO_CRITICAL_HIT_LIMIT

致命的命中が発生しない上限値

SORT_KEY

ゲームシステム名の読みがな

TABLES

表の集合

XRM_DAMAGE_TABLES

ミサイルダメージ表

Public Class Methods

ppc_parser() click to toggle source

@return [Command::Parser] PPCコマンドの構文解析器

# File lib/bcdice/game_system/BattleTech.rb, line 48
def self.ppc_parser
  return @ppc_parser if @ppc_parser

  @ppc_parser = Command::Parser.new(/PPC(?:[LCR][LU]?)?/, round_type: RoundType::FLOOR)
  @ppc_parser.enable_prefix_number
  @ppc_parser.restrict_cmp_op_to(:>=)

  @ppc_parser
end

Public Instance Methods

consciousness_roll(damage) click to toggle source

メック戦士意識維持ロールを行う

damageが6の場合は死亡。 damageが5以下の場合は、2d6の結果が意識維持表の値以上かの成功判定。

@param damage [Integer] メック戦士へのダメージ(1〜6) @return [Result] @see 「BattleTech: A Game of Armored Combat」ルールブックp. 44

# File lib/bcdice/game_system/BattleTech.rb, line 315
def consciousness_roll(damage)
  unless (1..6).include?(damage)
    return nil
  end

  command = "CD#{damage}"

  if damage == 6
    return Result.fumble("#{command} > 死亡")
  end

  consciousness_table = {
    1 => 3,
    2 => 5,
    3 => 7,
    4 => 10,
    5 => 11,
  }

  target = consciousness_table[damage]
  expr = "(2D6>=#{target})"

  values = @randomizer.roll_barabara(2, 6)
  sum = values.sum
  values_str = values.join(",")
  sum_and_values = "#{sum}[#{values_str}]"

  success = sum >= target
  result = success ? "成功" : "失敗"

  parts = [
    command,
    expr,
    sum_and_values,
    sum,
    result,
  ]
  text = parts.join(" > ")

  return success ? Result.success(text) : Result.failure(text)
end
eval_game_system_specific_command(command) click to toggle source
# File lib/bcdice/game_system/BattleTech.rb, line 63
def eval_game_system_specific_command(command)
  result = roll_tables(command, TABLES)
  return result if result

  if (ppc_parse_result = ppc_parser.parse(command))
    return execute_ppc(ppc_parse_result)
  end

  count = 1
  if command =~ /^(\d+)(.+)/
    count = Regexp.last_match(1).to_i
    command = Regexp.last_match(2)
  end

  debug('executeCommandCatched count', count)
  debug('executeCommandCatched command', command)

  case command
  when /\ACD([1-6])\z/
    damage = Regexp.last_match(1).to_i
    return consciousness_roll(damage)
  when /^((S|L)RM\d+)(.+)/
    tail = Regexp.last_match(3)
    type = Regexp.last_match(1)
    damageFunc = lambda { getXrmDamage(type) }
    return getHitResult(count, damageFunc, tail)
  when /^BT(\d+)(.+)/
    debug('BT pattern')
    tail = Regexp.last_match(2)
    damageValue = Regexp.last_match(1).to_i
    damageFunc = lambda { damageValue }
    return getHitResult(count, damageFunc, tail)
  end

  return nil
rescue UnknownXRMError
  return nil
end
execute_ppc(parse_result) click to toggle source

PPCコマンドを実行する @param parse_result [Command::Parsed] PPCコマンドの構文解析結果 @return [Result, nil]

# File lib/bcdice/game_system/BattleTech.rb, line 360
def execute_ppc(parse_result)
  count = parse_result.prefix_number || 1

  # getHitResult() の引数tailの形に合わせる
  # TODO: 攻撃を表すクラスに変える

  # "PPC" 以降の部位指定
  side = parse_result.command[3..-1]

  modifier = Format.modifier(parse_result.modify_number)
  target = parse_result.target_number

  tail = "#{side}#{modifier}>=#{target}"

  # ダメージ10固定で命中判定を行う
  return getHitResult(count, lambda { 10 }, tail)
end
getBaseValue(baseString) click to toggle source
# File lib/bcdice/game_system/BattleTech.rb, line 163
def getBaseValue(baseString)
  base = 0
  return base if baseString.nil?

  base = ArithmeticEvaluator.eval(baseString)
  return base
end
getDamageInfo(dice, damage, isLrm, index) click to toggle source
# File lib/bcdice/game_system/BattleTech.rb, line 223
def getDamageInfo(dice, damage, isLrm, index)
  return damage, damage.to_s if dice.nil?
  return damage, "[#{dice}] #{damage}" unless isLrm

  currentDamage = damage - (LRM_LIMIT * index)
  if currentDamage > LRM_LIMIT
    currentDamage = LRM_LIMIT
  end

  return currentDamage, currentDamage.to_s
end
getDamages(damageFunc, partTable, damages) click to toggle source

@param [Proc] damageFunc ダメージを返す手続き @param [RangeTable] partTable 命中部位表 @param [Hash] damages 蓄積したダメージの情報

# File lib/bcdice/game_system/BattleTech.rb, line 192
def getDamages(damageFunc, partTable, damages)
  resultText = ''
  damage, dice, isLrm = damageFunc.call()

  damagePartCount = 1
  if isLrm
    damagePartCount = (1.0 * damage / LRM_LIMIT).ceil
    resultText += "[#{dice}] #{damage}点"
  end

  damagePartCount.times do |damageIndex|
    currentDamage, damageText = getDamageInfo(dice, damage, isLrm, damageIndex)

    text, part, criticalText = getHitResultOne(damageText, partTable)
    resultText += " " if isLrm
    resultText += text

    if damages[part].nil?
      damages[part] = {
        partDamages: [],
        criticals: [],
      }
    end

    damages[part][:partDamages] << currentDamage
    damages[part][:criticals] << criticalText unless criticalText.empty?
  end

  return damages, resultText
end
getHitResult(count, damageFunc, tail) click to toggle source
# File lib/bcdice/game_system/BattleTech.rb, line 119
def getHitResult(count, damageFunc, tail)
  m = /\A([LCR][LU]?)?(\+\d+)?>=(\d+)/.match(tail)
  return nil unless m

  side = m[1] || 'C'
  baseString = m[2]
  target = m[3].to_i
  base = getBaseValue(baseString)
  debug("side, base, target", side, base, target)

  partTable = HitPart::TABLES[side]

  resultLines = []
  damages = {}
  hitCount = 0

  count.times do
    isHit, hitResult = getHitText(base, target)
    if isHit
      hitCount += 1

      damages, damageText = getDamages(damageFunc, partTable, damages)
      hitResult += damageText
    end
    resultLines << hitResult
  end

  # 命中したか?
  hit = hitCount > 0

  hitCountText = " > #{hitCount}回命中"
  hitDetails =
    if hit
      "#{hitCountText} 命中箇所:#{getTotalDamage(damages)}"
    else
      hitCountText
    end
  resultLines.push(hitDetails)

  resultText = resultLines.join("\n")

  return hit ? Result.success(resultText) : Result.failure(resultText)
end
getHitResultOne(damage_text, hit_part_table) click to toggle source

攻撃を1回行い、その結果を返す @param [String] damage_text ダメージを表す文字列 @param [RangeTable] hit_part_table 命中部位表

# File lib/bcdice/game_system/BattleTech.rb, line 276
def getHitResultOne(damage_text, hit_part_table)
  hit_part_roll_result = hit_part_table.roll(@randomizer)
  hit_part = hit_part_roll_result.content

  critical_hit_may_occur_str =
    hit_part.critical_hit_may_occur ? '(致命的命中)' : ''

  result_parts = [
    [
      "[#{hit_part_roll_result.sum}]",
      "#{hit_part.name}#{critical_hit_may_occur_str}",
      "#{damage_text}点",
    ].join(' ')
  ]

  criticalText = ''
  if hit_part.critical_hit_may_occur
    ct_roll_result = TABLES['CT'].roll(@randomizer)

    # 致命的命中が発生したか
    if ct_roll_result.sum > NO_CRITICAL_HIT_LIMIT
      criticalText = ct_roll_result.content
    end

    result_parts.push("[#{ct_roll_result.sum}] #{ct_roll_result.content}")
  end

  # TODO: 構造体で表現する
  return result_parts.join(' > '), hit_part.name, criticalText
end
getHitText(base, target) click to toggle source
# File lib/bcdice/game_system/BattleTech.rb, line 171
def getHitText(base, target)
  dice1 = @randomizer.roll_once(6)
  dice2 = @randomizer.roll_once(6)
  total = dice1 + dice2 + base
  isHit = (total >= target)
  baseString = (base > 0 ? "+#{base}" : "")

  result = "#{total}[#{dice1},#{dice2}#{baseString}]>=#{target} > "

  if isHit
    result += "命中 > "
  else
    result += "外れ"
  end

  return isHit, result
end
getTotalDamage(damages) click to toggle source
# File lib/bcdice/game_system/BattleTech.rb, line 235
def getTotalDamage(damages)
  parts = ['頭',
           '胴中央',
           '右胴',
           '左胴',
           '右脚',
           '左脚',
           '右腕',
           '左腕',]

  allDamage = 0
  damageTexts = []
  parts.each do |part|
    damageInfo = damages.delete(part)
    next if  damageInfo.nil?

    damage = damageInfo[:partDamages].inject(0) { |sum, i| sum + i }
    allDamage += damage
    damageCount = damageInfo[:partDamages].size
    criticals = damageInfo[:criticals]

    text = ""
    text += "#{part}(#{damageCount}回) #{damage}点"
    text += " #{criticals.join(' ')}" unless criticals.empty?

    damageTexts << text
  end

  unless damages.empty?
    raise "damages rest!! #{damages.inspect()}"
  end

  result = damageTexts.join(" / ")
  result += " > 合計ダメージ #{allDamage}点"

  return result
end
getXrmDamage(type) click to toggle source
# File lib/bcdice/game_system/BattleTech.rb, line 104
def getXrmDamage(type)
  raise UnknownXRMError, "unknown XRM: #{type}" unless XRM_DAMAGE_TABLES.key?(type)

  table = XRM_DAMAGE_TABLES[type]
  roll_result = table.roll(@randomizer)

  lrm = type.start_with?('L')
  damage = roll_result.content
  modified_damage = lrm ? damage : (2 * damage)

  return modified_damage, roll_result.sum, lrm
end
ppc_parser() click to toggle source

@return [Command::Parser] PPCコマンドの構文解析器

# File lib/bcdice/game_system/BattleTech.rb, line 59
def ppc_parser
  self.class.ppc_parser
end