Class: BCDice::GameSystem::BattleTech

Inherits:
Base
  • Object
show all
Defined in:
lib/bcdice/game_system/BattleTech.rb

Defined Under Namespace

Classes: HitPart, UnknownXRMError

Constant Summary collapse

ID =

ゲームシステムの識別子

'BattleTech'
NAME =

ゲームシステム名

'バトルテック'
SORT_KEY =

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

'はとるてつく'
HELP_MESSAGE =

ダイスボットの使い方

<<~MESSAGETEXT
  ・判定方法
   (回数)BT(ダメージ)(部位)+(基本値)>=(目標値)
   回数は省略時 1固定。
   部位はC(正面)R(右)、L(左)。省略時はC(正面)固定
   U(上半身)、L(下半身)を組み合わせ CU/RU/LU/CL/RL/LLも指定可能
   例)BT3+2>=4
    正面からダメージ3の攻撃を技能ベース2目標値4で1回判定
   例)2BT3RL+5>=8
    右下半身にダメージ3の攻撃を技能ベース5目標値8で2回判定
   ミサイルによるダメージは BT(ダメージ) の代わりに SRM2/4/6, LRM5/10/15/20 を指定
   例)3SRM6LU+5>=8
    左上半身にSRM6連を技能ベース5目標値8で3回判定
    BT(ダメージ) の代わりに PPC を指定するとダメージ10で判定
    例)2PPCR+3>=10
     右側からPPC(ダメージ10)による攻撃を技能ベース3目標値10で2回判定
  ・CT:致命的命中表
  ・DW:転倒後の向き表
  ・CDx:メック戦士意識維持ロール。ダメージ値x(1〜6)で判定 例)CD3
MESSAGETEXT
NO_CRITICAL_HIT_LIMIT =

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

7
LRM_LIMIT =
5
TABLES =

表の集合

{
  'CT' => DiceTable::RangeTable.new(
    '致命的命中表',
    '2D6',
    [
      [2..NO_CRITICAL_HIT_LIMIT, '致命的命中はなかった'],
      [8..9,                     '1箇所の致命的命中'],
      [10..11,                   '2箇所の致命的命中'],
      [12,                       'その部位が吹き飛ぶ(腕、脚、頭)または3箇所の致命的命中(胴)'],
    ]
  ),
  'DW' => DiceTable::Table.new(
    '転倒後の向き表',
    '1D6',
    [
      '同じ(前面から転倒) 正面/背面',
      '1ヘクスサイド右(側面から転倒) 右側面',
      '2ヘクスサイド右(側面から転倒) 右側面',
      '180度逆(背面から転倒) 正面/背面',
      '2ヘクスサイド左(側面から転倒) 左側面',
      '1ヘクスサイド左(側面から転倒) 左側面',
    ]
  )
}.freeze
XRM_DAMAGE_TABLES =

ミサイルダメージ表

{
  'SRM2' => DiceTable::RangeTable.new(
    'SRM2ダメージ表',
    '2D6',
    [
      [2..7,  1],
      [8..12, 2],
    ]
  ),
  'SRM4' => DiceTable::RangeTable.new(
    'SRM4ダメージ表',
    '2D6',
    [
      [2,      1],
      [3..6,   2],
      [7..10,  3],
      [11..12, 4],
    ]
  ),
  'SRM6' => DiceTable::RangeTable.new(
    'SRM6ダメージ表',
    '2D6',
    [
      [2..3,   2],
      [4..5,   3],
      [6..8,   4],
      [9..10,  5],
      [11..12, 6],
    ]
  ),
  'LRM5' => DiceTable::RangeTable.new(
    'LRM5ダメージ表',
    '2D6',
    [
      [2,      1],
      [3..4,   2],
      [5..8,   3],
      [9..10,  4],
      [11..12, 5],
    ]
  ),
  'LRM10' => DiceTable::RangeTable.new(
    'LRM10ダメージ表',
    '2D6',
    [
      [2..3,    3],
      [4,       4],
      [5..8,    6],
      [9..10,   8],
      [11..12, 10],
    ]
  ),
  'LRM15' => DiceTable::RangeTable.new(
    'LRM15ダメージ表',
    '2D6',
    [
      [2..3,    5],
      [4,       6],
      [5..8,    9],
      [9..10,  12],
      [11..12, 15],
    ]
  ),
  'LRM20' => DiceTable::RangeTable.new(
    'LRM20ダメージ表',
    '2D6',
    [
      [2..3,    6],
      [4,       9],
      [5..8,   12],
      [9..10,  16],
      [11..12, 20],
    ]
  )
}.freeze

Instance Attribute Summary

Attributes inherited from Base

#d66_sort_type, #default_cmp_op, #default_target_number, #randomizer, #reroll_dice_reroll_threshold, #round_type, #sides_implicit_d, #upper_dice_reroll_threshold

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#change_text, #check_result, command_pattern, #enable_debug, #enabled_d9?, #eval, eval, #grich_text, #initialize, prefixes_pattern, register_prefix, register_prefix_from_super_class, #sort_add_dice?, #sort_barabara_dice?

Methods included from Translate

#translate

Constructor Details

This class inherits a constructor from BCDice::Base

Class Method Details

.ppc_parserCommand::Parser

Returns PPCコマンドの構文解析器.

Returns:



48
49
50
51
52
53
54
55
56
# File 'lib/bcdice/game_system/BattleTech.rb', line 48

def self.ppc_parser
  return @ppc_parser if defined?(@ppc_parser) && @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

Instance Method Details

#consciousness_roll(damage) ⇒ Result

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

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

Parameters:

  • damage (Integer)

    メック戦士へのダメージ(1〜6)

Returns:

See Also:

  • A Game of Armored Combat」ルールブックp. 44


315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
# 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) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# 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) ⇒ Result?

PPCコマンドを実行する

Parameters:

Returns:



360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
# 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) ⇒ Object



163
164
165
166
167
168
169
# 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) ⇒ Object



223
224
225
226
227
228
229
230
231
232
233
# 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) ⇒ Object

Parameters:

  • damageFunc (Proc)

    ダメージを返す手続き

  • partTable (RangeTable)

    命中部位表

  • damages (Hash)

    蓄積したダメージの情報



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# 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) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# 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) ⇒ Object

攻撃を1回行い、その結果を返す

Parameters:

  • damage_text (String)

    ダメージを表す文字列

  • hit_part_table (RangeTable)

    命中部位表



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# 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) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# 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) ⇒ Object



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# 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) ⇒ Object

Raises:



104
105
106
107
108
109
110
111
112
113
114
115
# 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_parserCommand::Parser

Returns PPCコマンドの構文解析器.

Returns:



59
60
61
# File 'lib/bcdice/game_system/BattleTech.rb', line 59

def ppc_parser
  self.class.ppc_parser
end