diff --git a/src/diceBot/ChaosFlare.rb b/src/diceBot/ChaosFlare.rb index fce2f1423..b5d792a33 100644 --- a/src/diceBot/ChaosFlare.rb +++ b/src/diceBot/ChaosFlare.rb @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # frozen_string_literal: true +require 'utils/command_parser' + class ChaosFlare < DiceBot # ゲームシステムの識別子 ID = 'Chaos Flare' @@ -12,10 +14,34 @@ class ChaosFlare < DiceBot # ダイスボットの使い方 HELP_MESSAGE = <=目標値] + CF以外は全て省略可能 + 例: + - CF 2D6,クリティカル値12,ファンブル値2で判定 + - CF+10@10 修正値+10,クリティカル値10で判定 + - CF+10#3 修正値+10,ファンブル値3で判定 + - CF+10>=10 目標値を指定した場合、差分値も出力する + - 3CF+10@10#3>=10 3D6での判定 + - CF@9#3+8>=10 + +2D6 + ファンブル値2で判定する。クリティカルの判定は行われない。 + 目標値が設定された場合、差分値を出力する。 + - 2D6+4>=10 + +各種表 + FT: 因縁表 + FTx: 数値を指定すると因果表の値を出力する + - FT -> 11から66の間でランダム決定 + - FT23 -> 23の項目を出力 + - FT0 + - FT7 INFO_MESSAGE_TEXT + setPrefixes(['\d*CF.*', 'FT\d*']) + # ダイスボット設定後に行う処理 # @return [void] def postSet @@ -28,7 +54,7 @@ def postSet end end - # ゲーム別成功度判定(2D6) + # ゲーム別成功度判定(2D6)。以前の処理をそのまま残しています。 def check_2D6(total, dice_total, _dice_list, cmp_op, target) output = '' @@ -52,4 +78,109 @@ def check_2D6(total, dice_total, _dice_list, cmp_op, target) return output end + + def rollDiceCommand(command) + if command.start_with? "FT" + roll_fate_table(command) + else + cf_roll(command) + end + end + + private + + # 因縁表 + def roll_fate_table(command) + m = /^FT(\d+)?/.match(command) + if m[1] + num = m[1].to_i + if [0, 7].include?(num) + return "因果表(#{num}) > #{FATE_TABLE[num][0]}" + end + + dice1 = num / 10 + dice2 = num % 10 + if !(1..6).include?(dice1) || !(1..6).include?(dice2) + return nil + end + else + dice1, = roll(1, 6) + dice2, = roll(1, 6) + end + + index1 = dice1 + index2 = (dice2 / 2) - 1 + return "因果表(#{dice1}#{dice2}) > #{FATE_TABLE[index1][index2]}" + end + + # カオスフレア専用コマンド + # @param command [String] + # @return [String, nil] + def cf_roll(command) + parser = CommandParser.new(/\d*CF/) + + @cmd = parser.parse(command) + unless @cmd + return nil + end + + times = @cmd.command == "CF" ? 2 : @cmd.command.to_i + critical = @cmd.critical || 12 + fumble = @cmd.fumble || 2 + @cmd.dollar = nil + + if times < 0 || ![:>=, nil].include?(@cmd.cmp_op) + return nil + end + + dice_total, dice_list_text = roll(times, 6) + + is_critical = dice_total >= critical + is_fumble = dice_total <= fumble + + total = + if is_critical + 30 + elsif is_fumble + -20 + else + dice_total + end + + total += @cmd.modify_number + + sequence = [ + "(#{@cmd.to_s(:after_modify_number)})", + "#{dice_total}[#{dice_list_text}]", + total.to_s, + ("0" if total < 0), + ("クリティカル" if is_critical), + ("ファンブル" if is_fumble), + ("差分値 #{difference(total)}" if @cmd.target_number), + ].compact + + return sequence.join(" > ") + end + + # @param total [Integer] 合計値 + # @return [Integer] 差分値 + def difference(total) + if total < 0 + -@cmd.target_number + else + total - @cmd.target_number + end + end + + # 表を振るのに使う定数的なやつ。 + FATE_TABLE = [ + ["腐れ縁"], + ["純愛", "親近感", "庇護"], + ["信頼", "感服", "共感"], + ["友情", "尊敬", "慕情"], + ["好敵手", "期待", "借り"], + ["興味", "憎悪", "悲しみ"], + ["恐怖", "執着", "利用"], + ["任意"] + ].freeze end diff --git a/src/test/data/ChaosFlare.txt b/src/test/data/ChaosFlare.txt index 7a1b48894..081be9f0c 100644 --- a/src/test/data/ChaosFlare.txt +++ b/src/test/data/ChaosFlare.txt @@ -237,3 +237,94 @@ input: output: Chaos Flare : (2D6>=7) > 9[4,5] > 9 > 成功 > 差分値2 rand:4/6,5/6 +============================ +input: +FT +output: +Chaos Flare : 因果表(45) > 期待 +rand:4/6,5/6 +============================ +input: +FT45 +output: +Chaos Flare : 因果表(45) > 期待 +rand: +============================ +input: +SFT +output: +Chaos Flare : 因果表(45) > 期待###secret dice### +rand:4/6,5/6 +============================ +input: +FT0 +output: +Chaos Flare : 因果表(0) > 腐れ縁 +rand: +============================ +input: +FT7 +output: +Chaos Flare : 因果表(7) > 任意 +rand: +============================ +input: +3CF+10+5-10@10#3>=10 +output: +Chaos Flare : (3CF+5@10#3>=10) > 10[4,5,1] > 35 > クリティカル > 差分値 25 +rand:4/6,5/6,1/6 +============================ +input: +3CF@10#3+10+5-10>=10 +output: +Chaos Flare : (3CF+5@10#3>=10) > 10[4,5,1] > 35 > クリティカル > 差分値 25 +rand:4/6,5/6,1/6 +============================ +input: +S3CF+10+5-10@10#3>=10 +output: +Chaos Flare : (3CF+5@10#3>=10) > 10[4,5,1] > 35 > クリティカル > 差分値 25###secret dice### +rand:4/6,5/6,1/6 +============================ +input: +CF +output: +Chaos Flare : (CF) > 9[4,5] > 9 +rand:4/6,5/6 +============================ +input: +SCF +output: +Chaos Flare : (CF) > 9[4,5] > 9###secret dice### +rand:4/6,5/6 +============================ +input: +3CF +output: +Chaos Flare : (3CF) > 10[4,5,1] > 10 +rand:4/6,5/6,1/6 +============================ +input: +CF@9 +output: +Chaos Flare : (CF@9) > 9[4,5] > 30 > クリティカル +rand:4/6,5/6 +============================ +input: +CF#5 +output: +Chaos Flare : (CF#5) > 4[1,3] > -20 > 0 > ファンブル +rand:1/6,3/6 +============================ +input: +CF+10@10>=10 +output: +Chaos Flare : (CF+10@10>=10) > 9[4,5] > 19 > 差分値 9 +rand:4/6,5/6 +============================ +input: +CF-10@10>=10 +output: +Chaos Flare : (CF-10@10>=10) > 9[4,5] > -1 > 0 > 差分値 -10 +rand:4/6,5/6 + diff --git a/src/utils/command_parser.rb b/src/utils/command_parser.rb index a53a1ddbc..84ac1a847 100644 --- a/src/utils/command_parser.rb +++ b/src/utils/command_parser.rb @@ -1,5 +1,6 @@ require "utils/ArithmeticEvaluator" require "utils/normalize" +require "utils/modifier_formatter" class CommandParser < ArithmeticEvaluator def initialize(*literals) @@ -7,6 +8,52 @@ def initialize(*literals) @round_type = :omit end + # @!attribute [rw] command + # @return [String] + # @!attribute [rw] critical + # @return [Integer, nil] + # @!attribute [rw] fumble + # @return [Integer, nil] + # @!attribute [rw] dollar + # @return [Integer, nil] + # @!attribute [rw] modify_number + # @return [Integer] + # @!attribute [rw] cmp_op + # @return [Symbol, nil] + # @!attribute [rw] target_number + # @return [Integer, nil] + class Parsed + attr_accessor :command, :critical, :fumble, :dollar, :modify_number, :cmp_op, :target_number + + include ModifierFormatter + + def initialize + @critical = nil + @fumble = nil + @dollar = nil + end + + def to_s(suffix_position = :after_command) + c = @critical ? "@#{@critical}" : nil + f = @fumble ? "##{@fumble}" : nil + d = @dollar ? "$#{@dollar}" : nil + m = format_modifier(@modify_number) + + case suffix_position + when :after_command + [@command, c, f, d, m, @cmp_op, @target_number].join() + when :after_modify_number + [@command, m, c, f, d, @cmp_op, @target_number].join() + when :after_target_number + [@command, m, @cmp_op, @target_number, c, f, d].join() + end + end + end + + # @param expr [String] + # @param rount_type [Symbol] + # @return [CommandParser::Parsed] + # @return [nil] def parse(expr, round_type = :omit) @tokens = tokenize(expr) @idx = 0 @@ -30,10 +77,9 @@ def parse(expr, round_type = :omit) return @parsed end - Parsed = Struct.new(:command, :critical, :fumble, :dollar, :modify_number, :cmp_op, :target_number) - private + # @return [Array] def tokenize(expr) expr.gsub(%r{[\(\)\+\-*/@#\$]|[<>!=]+}) { |e| " #{e} " }.split(' ') end