lib/bcdice/game_system/Dracurouge.rb
# frozen_string_literal: true
module BCDice
module GameSystem
class Dracurouge < Base
# ゲームシステムの識別子
ID = 'Dracurouge'
# ゲームシステム名
NAME = 'ドラクルージュ'
# ゲームシステム名の読みがな
SORT_KEY = 'とらくるうしゆ'
# ダイスボットの使い方
HELP_MESSAGE = <<~MESSAGETEXT
・行い判定(DRx+y)
x:振るサイコロの数(省略時4)、y:渇き修正(省略時0)
例) DR DR6 DR+1 DR5+2
・抗い判定(DRRx)
x:振るサイコロの数
例) DRR3
・原風景表(ST)、叙勲表(CO)、叙勲後表(CA)、遥か過去表(EP)
原罪表(OS)、受難表(PN)、近況表(RS)、平和な過去表(PP)
・堕落表(CTx) x:渇き (例) CT3
・堕落の兆し表(CS)、拡張・堕落の兆し表(ECS)
・絆内容決定表(BT)
・反応表(RTxy)x:血統、y:道 xy省略で一括表示
血統 D:ドラク、R:ローゼンブルク、H:ヘルズガルド、M:ダストハイム
A:アヴァローム N:ノスフェラス、G:ゲイズヴァルト、K:カインシルト
道 F:領主、G:近衛、R:遍歴、W:賢者、J:狩人、N:夜獣
E:将軍、B:僧正、H:空駆、K:船長、L:寵童、V:仲立、U:技師、D:博士
S:星読、G2:後見
例)RT(一括表示)、RTDF(ドラクの領主)、RTAG2(アヴァロームの後見人)
・異端の反応表(HRTxy)x:血統、y:道 xy省略で一括表示
血統 L:異端卿、V:ヴルコラク、N:ナハツェーラ、K:カルンシュタイン
G:グリマルキン、S:ストリガ、M:メリュジーヌ、F:フォーン
H:ホムンクルス、E:エナメルム、S2:サングィナリエ、A:アールヴ
V2:ヴィーヴル、L2:ルーガルー、A2:アルラウネ、F2:フリッガ
道 W:野伏、N:流浪、S:密使、H:魔女、F:剣士、X:検体
例)HRT(一括表示)、HRTVW(ヴルコラクの野伏)、HRTF2X(フリッガの検体)
・D66ダイスあり
MESSAGETEXT
def initialize(command)
super(command)
@d66_sort_type = D66SortType::NO_SORT
end
def eval_game_system_specific_command(command)
roll_conduct_dice(command) ||
roll_resist_dice(command) ||
getReactionDiceCommandResult(command) ||
getHeresyReactionDiceCommandResult(command) ||
getCorruptionDiceCommandResult(command) ||
roll_tables(command, self.class::TABLES)
end
private
# 行い判定 (DRx+y)
def roll_conduct_dice(command)
m = /^DR(\d*[1-9])?(\+\d+)?$/.match(command)
unless m
return nil
end
dice_count = m[1]&.to_i || 4
thirsty_point = m[2].to_i
dice_list = @randomizer.roll_barabara(dice_count, 6).sort
glory_dice = count_glory_dice(dice_list)
dice_list += Array.new(glory_dice, 10)
calculation_process = apply_thirsty_point(dice_list, thirsty_point)
sequence = [
"(#{command})",
"#{dice_count}D6#{Format.modifier(thirsty_point)}",
calculation_process,
"[ #{dice_list.join(', ')} ]",
].compact
return sequence.join(" > ")
end
def count_glory_dice(dice_list)
one_count = dice_list.count(1)
six_count = dice_list.count(6)
return (one_count / 2) + (six_count / 2)
end
def apply_thirsty_point(dice_list, thirsty_point)
return nil if thirsty_point == 0
idx = dice_list.rindex { |i| i <= 6 }
text_list = dice_list.map(&:to_s)
text_list[idx] += "+#{thirsty_point}"
dice_list[idx] += thirsty_point
return "[ #{text_list.join(', ')} ]"
end
# 抗い判定 (DRRx)
def roll_resist_dice(command)
m = /^DRR(\d+)$/.match(command)
unless m
return nil
end
dice_count = m[1].to_i
dice_count = 4 if dice_count == 0
dice_list = @randomizer.roll_barabara(dice_count, 6).sort
total = dice_list.sum()
return "(#{command}) > #{dice_count}D6 > [ #{dice_list.join(', ')} ] > #{total}"
end
def getReactionDiceCommandResult(command)
return nil unless command =~ /^RT((\w\d*)(\w\d*))?/
typeText1 = Regexp.last_match(2)
typeText2 = Regexp.last_match(3)
name = translate("Dracurouge.RT.name")
blood = translate("Dracurouge.RT.blood")
style = translate("Dracurouge.RT.style")
return getReactionText(name, typeText1, typeText2, blood, style)
end
def getHeresyReactionDiceCommandResult(command)
return nil unless command =~ /^HRT((\w\d*)(\w\d*))?/
typeText1 = Regexp.last_match(2)
typeText2 = Regexp.last_match(3)
name = translate("Dracurouge.HRT.name")
blood = translate("Dracurouge.HRT.blood")
style = translate("Dracurouge.HRT.style")
return getReactionText(name, typeText1, typeText2, blood, style)
end
def getReactionText(name, typeText1, typeText2, infos1, infos2)
return nil unless checkTypeText(typeText1, infos1)
return nil unless checkTypeText(typeText2, infos2)
ten_value = @randomizer.roll_once(6)
one_value = @randomizer.roll_once(6)
number = "#{ten_value}#{one_value}"
isBefore = (ten_value < 4)
infos = isBefore ? infos1 : infos2
typeText = (isBefore ? typeText1 : typeText2)
index = (one_value - 1) + (isBefore ? (ten_value - 1) : (ten_value - 4)) * 6
debug("index", index)
if typeText.nil?
resultText = getReactionTextFull(infos, index)
else
info = infos[typeText.to_sym]
return nil if info.nil?
resultText = getReactionTex(info, index)
end
return "#{name}(#{number}) > #{resultText}"
end
def checkTypeText(typeText, infos)
return true if typeText.nil?
return infos.keys.include?(typeText.to_sym)
end
def getReactionTextFull(infos, index)
resultTexts = []
infos.each_value do |info|
resultTexts << getReactionTex(info, index)
end
return resultTexts.join('/')
end
def getReactionTex(info, index)
typeName = info[:name]
text = info[:table][index]
return "#{typeName}:#{text}"
end
def getCorruptionDiceCommandResult(command)
return nil unless command =~ /^CT(\d+)$/
modify = Regexp.last_match(1).to_i
name = translate("Dracurouge.CT.name")
table =
[
[0, translate("Dracurouge.CT.table.0")],
[1, translate("Dracurouge.CT.table.1")],
[3, translate("Dracurouge.CT.table.3")],
[5, translate("Dracurouge.CT.table.5")],
[6, translate("Dracurouge.CT.table.6")],
[7, translate("Dracurouge.CT.table.7")],
[8, translate("Dracurouge.CT.table.8")],
[99, translate("Dracurouge.CT.table.99")],
]
dice_list = @randomizer.roll_barabara(2, 6)
number = dice_list.sum()
number_text = dice_list.join(",")
index = (number - modify)
debug('index', index)
text = get_table_by_number(index, table)
return "2D6[#{number_text}]-#{modify} > #{name}(#{index}) > #{text}"
end
class YearTable
# @param key [String]
# @param locale [Symbol]
# @param years [Array<String>]
# @return [YearTable]
def self.from_i18n(key, locale, years)
table = I18n.translate(key, locale: locale, raise: true)
items = table[:items].zip(years)
return new(table[:name], table[:year_title], items)
end
# @param name [String]
# @param year_title [String]
# @param items [Array<Array<(String, String)>>]
def initialize(name, year_title, items)
@name = name
@year_title = year_title
@items = items.freeze
end
# @param randomizer [Randomizer]
# @return [String]
def roll(randomizer)
tens, ones = randomizer.roll_barabara(2, 6)
index = (tens - 1) * 6 + (ones - 1)
text, year_expr = @items[index]
interim_expr = year_expr.gsub(/\d+D6+/) { |expr| roll_d6x(expr, randomizer) }
year = ArithmeticEvaluator.eval(interim_expr.gsub("×", "*"))
"#{@name}(#{tens}#{ones}) > #{text} > #{@year_title}:#{year_expr} > (#{interim_expr}) > #{@year_title}:#{year}年"
end
private
# D66と同様の形式でD666などにも対応したダイスロール
#
# @param expr [String]
# @param randomizer [Randomizer]
# @return [Integer]
def roll_d6x(expr, randomizer)
times, sides = expr.split("D", 2)
times = times.to_i
list = Array.new(times) do
randomizer.roll_barabara(sides.length, 6)
.reverse # テスト系の互換性のために反転する
.map.with_index { |x, idx| x * (10**idx) }
.sum()
end
return list.sum()
end
end
class << self
private
def translate_tables(locale)
{
"CO" => YearTable.from_i18n(
"Dracurouge.table.CO",
locale,
[
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"10+2D6",
"7+1D6×1D6",
"14+1D6×1D6",
"7+1D6×1D6",
"10+1D6×1D6",
"7+1D6×1D6",
"10+2D6",
"7+1D6×1D6",
"14+1D6×1D6",
"10+2D6",
"7+1D6×1D6",
"14+1D6×1D6",
"18+1D6×1D6",
"10+2D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"30+1D6×1D6",
"14+1D6×1D6",
"7+1D6×1D6",
"14+1D6×1D6",
"10+2D6",
"14+1D6×1D6",
"14+1D6×1D6",
"7+4D6",
"14+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
]
),
"CA" => YearTable.from_i18n(
"Dracurouge.table.CA",
locale,
[
"2D6×10",
"1D6×1D6",
"1D6×1D6",
"2D6×5",
"2D6×10",
"1D6×1D6",
"2D6×10",
"1D6×5",
"2D6×10",
"2D6×3",
"1D6×1D6",
"1D6×1D6",
"2D6×10",
"2D6×10",
"2D6×20",
"2D6×10",
"2D6×20",
"1D6×1D6",
"1D6×3",
"1D6×1D6",
"1D6×5",
"2D6×10",
"1D6×1D6",
"2D6×10",
"2D6",
"1D6×1D6",
"2D6",
"1D6×1D6",
"2D6×20",
"2D6×10",
"1D6×1D6",
"2D6×50",
"2D6×10",
"1D6×1D6",
"2D6×5",
"1D6×1D6",
]
),
"EP" => YearTable.from_i18n(
"Dracurouge.table.EP",
locale,
[
"1D66+1300",
"1D666",
"1D666",
"1D666",
"1D66+1250",
"1D666",
"3D6×100",
"2D6×100",
"1D66+1210",
"1D666",
"2D6×100",
"3D6×100",
"1D66+1300",
"2D6×100",
"1D6+1250",
"1D666",
"1D666",
"1D666",
"1D66+1250",
"2D6×100",
"1D666",
"3D6×100",
"2D6×100",
"2D6×100",
"1D6×150",
"2D6×100",
"1D66+1250",
"1D66+400",
"1212",
"2D6×100",
"2D6×100",
"1D66×10",
"3D6×100",
"3D6×100",
"1D66+1300",
"1D66+1833",
]
),
"OS" => YearTable.from_i18n(
"Dracurouge.table.OS",
locale,
[
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"13+1D6×1D6",
"7+1D6×1D6",
"13+1D6×1D6",
"7+1D6×1D6",
"13+1D6×1D6",
"13+1D6×1D6",
"13+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"10+1D6×1D6",
"7+1D6×1D6",
"15+1D6×1D6",
"6+2D6",
"7+1D6×1D6",
"7+1D6×1D6",
"13+1D6×1D6",
"35+1D6×1D6",
"9+2D6",
"13+1D6×1D6",
"9+2D6",
"6+2D6",
"7+1D6×1D6",
"7+2D6",
"7+1D6×1D5",
"7+1D6×1D6",
"13+1D6×1D6",
"7+1D6×1D6",
]
),
"RS" => YearTable.from_i18n(
"Dracurouge.table.RS",
locale,
[
"2D6×10",
"1D6×1D6",
"1D6×1D6",
"1D6×1D6",
"3D6×30",
"3D6×30",
"2D6×10",
"2D6×10",
"2D6×10",
"2D6×10",
"2D6×20",
"1D6×1D6",
"1D6×1D6",
"1D6×1D6",
"1",
"2D6×10",
"2D6×20",
"1D6×1D6",
"1D6×1D6",
"3D6×30",
"3D6×20",
"1D6×1D6",
"3D6×30",
"3D6×20",
"1D6×1D6",
"1D6×1D6",
"1D6×1D6",
"1D6×1D6",
"1D6×10",
"2D6×10",
"3D6×50",
"1D6×1D6",
"3D6×20",
"2D6×10",
"1D6×1D6",
"3D6×50",
]
),
"PP" => YearTable.from_i18n(
"Dracurouge.table.PP",
locale,
[
"8+2D6",
"6+2D6",
"7+1D6×1D6",
"15+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"6+2D6",
"7+1D6×1D6",
"9+2D6",
"15+1D6×1D6",
"9+3D6",
"7+1D6×1D6",
"10+1D6×1D6",
"9+2D6",
"9+2D6",
"9+3D6",
"6+2D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"6+2D6",
"10+1D6×1D6",
"7+1D6×1D6",
"12+1D6×1D6",
"15+1D6×1D6",
"9+3D6",
"7+1D6×1D6",
"7+1D6×1D6",
"7+1D6×1D6",
"12+4D6",
]
),
"ST" => DiceTable::D66Table.from_i18n("Dracurouge.table.ST", locale),
"PN" => DiceTable::D66Table.from_i18n("Dracurouge.table.PN", locale),
"CS" => DiceTable::Table.from_i18n("Dracurouge.table.CS", locale),
"ECS" => DiceTable::D66Table.from_i18n("Dracurouge.table.ECS", locale),
"BT" => DiceTable::Table.from_i18n("Dracurouge.table.BT", locale),
}
end
end
TABLES = translate_tables(:ja_jp)
register_prefix('DR', 'RT', 'HRT', 'CT', TABLES.keys)
end
end
end