Beispiel #1
0
        /// <summary>
        /// 1ログ1スペルに対して判定する
        /// </summary>
        /// <param name="spell">スペル</param>
        /// <param name="logLine">ログ</param>
        public void MatchCore(
            Models.Spell spell,
            string logLine)
        {
            var regex        = spell.Regex;
            var notifyNeeded = false;
            var matched      = false;

            if (!spell.IsInstance)
            {
                // マッチング計測開始
                spell.StartMatching();

                // 開始条件を確認する
                if (ConditionUtility.CheckConditionsForSpell(spell))
                {
                    // 正規表現が無効?
                    if (!spell.RegexEnabled ||
                        regex == null)
                    {
                        var keyword = spell.KeywordReplaced;
                        if (string.IsNullOrWhiteSpace(keyword))
                        {
                            return;
                        }

                        // キーワードが含まれるか?
                        if (logLine.Contains(keyword, StringComparison.OrdinalIgnoreCase))
                        {
                            matched = true;
                            var targetSpell = spell;

                            // ヒットしたログを格納する
                            targetSpell.MatchedLog = logLine;

                            // スペル名(表示テキスト)を置換する
                            var replacedTitle = ConditionUtility.GetReplacedTitle(targetSpell);

                            // PC名を置換する
                            replacedTitle = FFXIVPlugin.Instance.ReplacePartyMemberName(
                                replacedTitle,
                                Settings.Default.PCNameInitialOnDisplayStyle);

                            targetSpell.SpellTitleReplaced = replacedTitle;
                            targetSpell.UpdateDone         = false;
                            targetSpell.OverDone           = false;
                            targetSpell.BeforeDone         = false;
                            targetSpell.TimeupDone         = false;

                            var now = DateTime.Now;

                            // ホットバーからリキャスト時間の読込みを試みる
                            double d = 0d;
                            if (!this.TryGetHotbarRecast(targetSpell, out d))
                            {
                                d = targetSpell.RecastTime;
                            }

                            targetSpell.CompleteScheduledTime = now.AddSeconds(d);

                            targetSpell.MatchDateTime = now;

                            // マッチング計測終了
                            spell.EndMatching();

                            // マッチ時点のサウンドを再生する
                            targetSpell.Play(targetSpell.MatchSound, targetSpell.MatchAdvancedConfig);
                            targetSpell.Play(targetSpell.MatchTextToSpeak, targetSpell.MatchAdvancedConfig);

                            notifyNeeded = true;

                            // 遅延サウンドタイマを開始する
                            targetSpell.StartOverSoundTimer();
                            targetSpell.StartBeforeSoundTimer();
                            targetSpell.StartTimeupSoundTimer();
                        }
                    }
                    else
                    {
                        // 正規表現でマッチングする
                        var match = regex.Match(logLine);
                        if (match.Success)
                        {
                            matched = true;
#if DEBUG
                            if (logLine.Contains("MARK"))
                            {
                                Debug.WriteLine("MARK");
                            }
#endif
                            var targetSpell = default(Spell);

                            void setTitle()
                            {
                                targetSpell = spell;

                                // ヒットしたログを格納する
                                targetSpell.MatchedLog = logLine;

                                // スペル名(表示テキスト)を置換する
                                var replacedTitle = match.Result(ConditionUtility.GetReplacedTitle(targetSpell));

                                // PC名を置換する
                                replacedTitle = FFXIVPlugin.Instance.ReplacePartyMemberName(
                                    replacedTitle,
                                    Settings.Default.PCNameInitialOnDisplayStyle);

                                // インスタンス化する?
                                if (targetSpell.ToInstance)
                                {
                                    // 同じタイトルのインスタンススペルを探す
                                    // 存在すればそれを使用して、なければ新しいインスタンスを生成する
                                    targetSpell = SpellTable.Instance.GetOrAddInstance(
                                        replacedTitle,
                                        targetSpell);
                                }
                                else
                                {
                                    targetSpell.SpellTitleReplaced = replacedTitle;
                                }
                            }

                            // スペルタイトルを編集する
                            setTitle();

                            targetSpell.UpdateDone = false;
                            targetSpell.OverDone   = false;
                            targetSpell.BeforeDone = false;
                            targetSpell.TimeupDone = false;

                            var now = DateTime.Now;

                            // 効果時間を決定する
                            // グループ "duration" をキャプチャーしていた場合は効果時間を置換する
                            // 最大値9999を超えていた場合は無視する
                            var duration = targetSpell.RecastTime;

                            var d = 0d;
                            var durationAsText = match.Groups["duration"].Value;
                            if (double.TryParse(durationAsText, out d) &&
                                d < 9999)
                            {
                                duration = d;
                            }
                            else
                            {
                                // ホットバーからリキャスト時間の読込を試みる
                                if (this.TryGetHotbarRecast(targetSpell, out d))
                                {
                                    duration = d;
                                }
                            }

                            targetSpell.CompleteScheduledTime = now.AddSeconds(duration);

                            // スペル対象を保存する
                            // グループ "target" をキャプチャーしていた場合はその文字列を保存する
                            var targetName = match.Groups["target"].Value;
                            if (!string.IsNullOrWhiteSpace(targetName))
                            {
                                targetSpell.TargetName = targetName;
                            }

                            // マッチ日時を格納する
                            targetSpell.MatchDateTime = now;

                            // マッチング計測終了
                            spell.EndMatching();

                            // マッチ時点のサウンドを再生する
                            targetSpell.Play(targetSpell.MatchSound, targetSpell.MatchAdvancedConfig);

                            if (!string.IsNullOrWhiteSpace(targetSpell.MatchTextToSpeak))
                            {
                                var tts = match.Result(targetSpell.MatchTextToSpeak);
                                targetSpell.Play(tts, targetSpell.MatchAdvancedConfig);
                            }

                            notifyNeeded = true;

                            // 遅延サウンドタイマを開始する
                            targetSpell.StartOverSoundTimer();
                            targetSpell.StartBeforeSoundTimer();
                            targetSpell.StartTimeupSoundTimer();
                        }
                    }
                }
            }

            // 延長をマッチングする
            if (spell.MatchDateTime > DateTime.MinValue)
            {
                var keywords      = new string[] { spell.KeywordForExtendReplaced1, spell.KeywordForExtendReplaced2 };
                var regexes       = new Regex[] { spell.RegexForExtend1, spell.RegexForExtend2 };
                var timeToExtends = new double[] { spell.RecastTimeExtending1, spell.RecastTimeExtending2 };

                for (int i = 0; i < 2; i++)
                {
                    var keywordToExtend = keywords[i];
                    var regexToExtend   = regexes[i];
                    var timeToExtend    = timeToExtends[i];

                    // マッチングする
                    var exntended = false;

                    if (!spell.RegexEnabled ||
                        regexToExtend == null)
                    {
                        if (!string.IsNullOrWhiteSpace(keywordToExtend))
                        {
                            exntended = logLine.Contains(keywordToExtend, StringComparison.OrdinalIgnoreCase);
                        }
                    }
                    else
                    {
                        var match = regexToExtend.Match(logLine);
                        exntended = match.Success;

                        if (exntended)
                        {
                            // targetをキャプチャーしている?
                            if (!string.IsNullOrWhiteSpace(spell.TargetName))
                            {
                                var targetName = match.Groups["target"].Value;
                                if (!string.IsNullOrWhiteSpace(targetName))
                                {
                                    // targetが当初のマッチングと一致するか確認する
                                    if (spell.TargetName != targetName)
                                    {
                                        exntended = false;
                                    }
                                }
                            }
                        }
                    }

                    if (!exntended)
                    {
                        continue;
                    }

                    var now = DateTime.Now;

                    // リキャストタイムを延長する
                    var newSchedule = spell.CompleteScheduledTime.AddSeconds(timeToExtend);
                    spell.BeforeDone = false;
                    spell.UpdateDone = false;

                    if (spell.ExtendBeyondOriginalRecastTime)
                    {
                        if (spell.UpperLimitOfExtension > 0)
                        {
                            var newDuration = (newSchedule - now).TotalSeconds;
                            if (newDuration > (double)spell.UpperLimitOfExtension)
                            {
                                newSchedule = newSchedule.AddSeconds(
                                    (newDuration - (double)spell.UpperLimitOfExtension) * -1);
                            }
                        }
                    }
                    else
                    {
                        var newDuration = (newSchedule - now).TotalSeconds;
                        if (newDuration > (double)spell.RecastTime)
                        {
                            newSchedule = newSchedule.AddSeconds(
                                (newDuration - (double)spell.RecastTime) * -1);
                        }
                    }

                    spell.CompleteScheduledTime = newSchedule;
                    spell.MatchDateTime         = now;

                    notifyNeeded = true;

                    // 遅延サウンドタイマを開始(更新)する
                    spell.StartOverSoundTimer();
                    spell.StartBeforeSoundTimer();
                    spell.StartTimeupSoundTimer();
                }
            }
            // end if 延長マッチング

            // ホットバー情報を更新する
            if (!matched)
            {
                var now = DateTime.Now;

                if (spell.CompleteScheduledTime > now)
                {
                    double d;
                    if (this.TryGetHotbarRecast(spell, out d))
                    {
                        // ホットバー情報と0.6秒以上乖離したら補正する
                        var newSchedule = now.AddSeconds(d);

                        if (Math.Abs((newSchedule - spell.CompleteScheduledTime).TotalSeconds)
                            >= 0.6d)
                        {
                            spell.CompleteScheduledTime = newSchedule;
                            spell.BeforeDone            = false;
                            spell.UpdateDone            = false;

                            notifyNeeded = true;

                            spell.StartOverSoundTimer();
                            spell.StartBeforeSoundTimer();
                            spell.StartTimeupSoundTimer();
                        }
                    }
                }
            }

            // ACT標準のSpellTimerに変更を通知する
            if (notifyNeeded)
            {
                this.UpdateNormalSpellTimer(spell, false);
                this.NotifyNormalSpellTimer(spell);
            }
        }
Beispiel #2
0
        /// <summary>
        /// Spellをマッチングする
        /// </summary>
        /// <param name="spells">Spell</param>
        /// <param name="logLines">ログ</param>
        private void MatchSpells(
            IReadOnlyList <SpellTimer> spells,
            IReadOnlyList <string> logLines)
        {
            foreach (var logLine in logLines)
            {
                // マッチする?
                spells.AsParallel().ForAll(spell =>
                {
                    var regex        = spell.Regex;
                    var notifyNeeded = false;

                    if (!spell.IsInstance)
                    {
                        // 開始条件を確認する
                        if (ConditionUtility.CheckConditionsForSpell(spell))
                        {
                            // 正規表現が無効?
                            if (!spell.RegexEnabled ||
                                regex == null)
                            {
                                var keyword = spell.KeywordReplaced;
                                if (string.IsNullOrWhiteSpace(keyword))
                                {
                                    return;
                                }

                                // キーワードが含まれるか?
                                if (logLine.ToUpper().Contains(
                                        keyword.ToUpper()))
                                {
                                    var targetSpell = spell;

                                    // ヒットしたログを格納する
                                    targetSpell.MatchedLog = logLine;

                                    // スペル名(表示テキスト)を置換する
                                    var replacedTitle = ConditionUtility.GetReplacedTitle(targetSpell);

                                    targetSpell.SpellTitleReplaced    = replacedTitle;
                                    targetSpell.MatchDateTime         = DateTime.Now;
                                    targetSpell.UpdateDone            = false;
                                    targetSpell.OverDone              = false;
                                    targetSpell.BeforeDone            = false;
                                    targetSpell.TimeupDone            = false;
                                    targetSpell.CompleteScheduledTime = targetSpell.MatchDateTime.AddSeconds(targetSpell.RecastTime);

                                    // マッチ時点のサウンドを再生する
                                    this.Play(targetSpell.MatchSound);
                                    this.Play(targetSpell.MatchTextToSpeak);

                                    notifyNeeded = true;
                                }
                            }
                            else
                            {
                                // 正規表現でマッチングする
                                var match = regex.Match(logLine);
                                if (match.Success)
                                {
                                    var targetSpell = spell;

                                    // ヒットしたログを格納する
                                    targetSpell.MatchedLog = logLine;

                                    // スペル名(表示テキスト)を置換する
                                    var replacedTitle = match.Result(ConditionUtility.GetReplacedTitle(targetSpell));

                                    // インスタンス化する?
                                    if (spell.ToInstance)
                                    {
                                        // 同じタイトルのインスタンススペルを探す
                                        // 存在すればそれを使用して、なければ新しいインスタンスを生成する
                                        targetSpell = SpellTimerTable.GetOrAddInstance(
                                            replacedTitle,
                                            spell);
                                    }

                                    targetSpell.SpellTitleReplaced = replacedTitle;
                                    targetSpell.MatchDateTime      = DateTime.Now;
                                    targetSpell.UpdateDone         = false;
                                    targetSpell.OverDone           = false;
                                    targetSpell.BeforeDone         = false;
                                    targetSpell.TimeupDone         = false;

                                    // 効果時間を決定する
                                    // グループ "duration" をキャプチャーしていた場合は効果時間を置換する
                                    var durationAsText = match.Groups["duration"].Value;
                                    long duration;
                                    if (!long.TryParse(durationAsText, out duration))
                                    {
                                        duration = targetSpell.RecastTime;
                                    }

                                    targetSpell.CompleteScheduledTime = targetSpell.MatchDateTime.AddSeconds(duration);

                                    // マッチ時点のサウンドを再生する
                                    this.Play(targetSpell.MatchSound);

                                    if (!string.IsNullOrWhiteSpace(targetSpell.MatchTextToSpeak))
                                    {
                                        var tts = match.Result(targetSpell.MatchTextToSpeak);
                                        this.Play(tts);
                                    }

                                    notifyNeeded = true;
                                }
                            }
                        }
                    }

                    // 延長をマッチングする
                    if (spell.MatchDateTime > DateTime.MinValue)
                    {
                        var keywords      = new string[] { spell.KeywordForExtendReplaced1, spell.KeywordForExtendReplaced2 };
                        var regexes       = new Regex[] { spell.RegexForExtend1, spell.RegexForExtend2 };
                        var timeToExtends = new long[] { spell.RecastTimeExtending1, spell.RecastTimeExtending2 };

                        for (int i = 0; i < 2; i++)
                        {
                            var keywordToExtend = keywords[i];
                            var regexToExtend   = regexes[i];
                            var timeToExtend    = timeToExtends[i];

                            // マッチングする
                            var match = false;

                            if (!spell.RegexEnabled ||
                                regexToExtend == null)
                            {
                                if (!string.IsNullOrWhiteSpace(keywordToExtend))
                                {
                                    match = logLine.ToUpper().Contains(keywordToExtend.ToUpper());
                                }
                            }
                            else
                            {
                                match = regexToExtend.Match(logLine).Success;
                            }

                            if (!match)
                            {
                                continue;
                            }

                            var now = DateTime.Now;

                            // リキャストタイムを延長する
                            var newSchedule  = spell.CompleteScheduledTime.AddSeconds(timeToExtend);
                            spell.BeforeDone = false;
                            spell.UpdateDone = false;

                            if (spell.ExtendBeyondOriginalRecastTime)
                            {
                                if (spell.UpperLimitOfExtension > 0)
                                {
                                    var newDuration = (newSchedule - now).TotalSeconds;
                                    if (newDuration > (double)spell.UpperLimitOfExtension)
                                    {
                                        newSchedule = newSchedule.AddSeconds(
                                            (newDuration - (double)spell.UpperLimitOfExtension) * -1);
                                    }
                                }
                            }
                            else
                            {
                                var newDuration = (newSchedule - now).TotalSeconds;
                                if (newDuration > (double)spell.RecastTime)
                                {
                                    newSchedule = newSchedule.AddSeconds(
                                        (newDuration - (double)spell.RecastTime) * -1);
                                }
                            }

                            spell.MatchDateTime         = now;
                            spell.CompleteScheduledTime = newSchedule;

                            notifyNeeded = true;
                        }
                    }
                    // end if 延長マッチング

                    // ACT標準のSpellTimerに変更を通知する
                    if (notifyNeeded)
                    {
                        this.updateNormalSpellTimer(spell, false);
                        this.notifyNormalSpellTimer(spell);
                    }
                });
                // end loop spells
            }

            // スペルの更新とサウンド処理を行う
            foreach (var spell in spells)
            {
                var regex = spell.Regex;

                // Repeat対象のSpellを更新する
                if (spell.RepeatEnabled &&
                    spell.MatchDateTime > DateTime.MinValue)
                {
                    if (DateTime.Now >= spell.MatchDateTime.AddSeconds(spell.RecastTime))
                    {
                        spell.MatchDateTime = DateTime.Now;
                        spell.UpdateDone    = false;
                        spell.OverDone      = false;
                        spell.TimeupDone    = false;
                    }
                }

                // n秒後のSoundを再生する
                if (spell.OverTime > 0 &&
                    !spell.OverDone &&
                    spell.MatchDateTime > DateTime.MinValue)
                {
                    var over = spell.MatchDateTime.AddSeconds(spell.OverTime);

                    if (DateTime.Now >= over)
                    {
                        this.Play(spell.OverSound);
                        if (!string.IsNullOrWhiteSpace(spell.OverTextToSpeak))
                        {
                            var tts = spell.RegexEnabled && regex != null?
                                      regex.Replace(spell.MatchedLog, spell.OverTextToSpeak) :
                                          spell.OverTextToSpeak;

                            this.Play(tts);
                        }

                        spell.OverDone = true;
                    }
                }

                // リキャストn秒前のSoundを再生する
                if (spell.BeforeTime > 0 &&
                    !spell.BeforeDone &&
                    spell.MatchDateTime > DateTime.MinValue)
                {
                    if (spell.CompleteScheduledTime > DateTime.MinValue)
                    {
                        var before = spell.CompleteScheduledTime.AddSeconds(spell.BeforeTime * -1);

                        if (DateTime.Now >= before)
                        {
                            this.Play(spell.BeforeSound);
                            if (!string.IsNullOrWhiteSpace(spell.BeforeTextToSpeak))
                            {
                                var tts = spell.RegexEnabled && regex != null?
                                          regex.Replace(spell.MatchedLog, spell.BeforeTextToSpeak) :
                                              spell.BeforeTextToSpeak;

                                this.Play(tts);
                            }

                            spell.BeforeDone = true;
                        }
                    }
                }

                // リキャスト完了のSoundを再生する
                if (spell.RecastTime > 0 &&
                    !spell.TimeupDone &&
                    spell.MatchDateTime > DateTime.MinValue)
                {
                    if (spell.CompleteScheduledTime > DateTime.MinValue &&
                        DateTime.Now >= spell.CompleteScheduledTime)
                    {
                        this.Play(spell.TimeupSound);
                        if (!string.IsNullOrWhiteSpace(spell.TimeupTextToSpeak))
                        {
                            var tts = spell.RegexEnabled && regex != null?
                                      regex.Replace(spell.MatchedLog, spell.TimeupTextToSpeak) :
                                          spell.TimeupTextToSpeak;

                            this.Play(tts);
                        }

                        spell.TimeupDone = true;
                    }
                }

                // インスタンス化したスペルを削除する
                if (spell.IsInstance)
                {
                    SpellTimerTable.TryRemoveInstance(spell);
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Spellをマッチングする
        /// </summary>
        /// <param name="spells">Spell</param>
        /// <param name="logLines">ログ</param>
        private void MatchSpells(
            IReadOnlyList <Models.SpellTimer> spells,
            IReadOnlyList <string> logLines)
        {
            foreach (var logLine in logLines)
            {
                // マッチする?
                spells.AsParallel().ForAll(spell =>
                {
                    var regex        = spell.Regex;
                    var notifyNeeded = false;

                    if (!spell.IsInstance)
                    {
                        // 開始条件を確認する
                        if (ConditionUtility.CheckConditionsForSpell(spell))
                        {
                            // 正規表現が無効?
                            if (!spell.RegexEnabled ||
                                regex == null)
                            {
                                var keyword = spell.KeywordReplaced;
                                if (string.IsNullOrWhiteSpace(keyword))
                                {
                                    return;
                                }

                                // キーワードが含まれるか?
                                if (logLine.ToUpper().Contains(
                                        keyword.ToUpper()))
                                {
                                    var targetSpell = spell;

                                    // ヒットしたログを格納する
                                    targetSpell.MatchedLog = logLine;

                                    // スペル名(表示テキスト)を置換する
                                    var replacedTitle = ConditionUtility.GetReplacedTitle(targetSpell);

                                    targetSpell.SpellTitleReplaced    = replacedTitle;
                                    targetSpell.MatchDateTime         = DateTime.Now;
                                    targetSpell.UpdateDone            = false;
                                    targetSpell.OverDone              = false;
                                    targetSpell.BeforeDone            = false;
                                    targetSpell.TimeupDone            = false;
                                    targetSpell.CompleteScheduledTime = targetSpell.MatchDateTime.AddSeconds(targetSpell.RecastTime);

                                    // マッチ時点のサウンドを再生する
                                    this.Play(targetSpell.MatchSound);
                                    this.Play(targetSpell.MatchTextToSpeak);

                                    notifyNeeded = true;

                                    // 遅延サウンドタイマを開始する
                                    targetSpell.StartOverSoundTimer();
                                    targetSpell.StartBeforeSoundTimer();
                                    targetSpell.StartTimeupSoundTimer();
                                }
                            }
                            else
                            {
                                // 正規表現でマッチングする
                                var match = regex.Match(logLine);
                                if (match.Success)
                                {
                                    var targetSpell = spell;

                                    // ヒットしたログを格納する
                                    targetSpell.MatchedLog = logLine;

                                    // スペル名(表示テキスト)を置換する
                                    var replacedTitle = match.Result(ConditionUtility.GetReplacedTitle(targetSpell));

                                    // インスタンス化する?
                                    if (spell.ToInstance)
                                    {
                                        // 同じタイトルのインスタンススペルを探す
                                        // 存在すればそれを使用して、なければ新しいインスタンスを生成する
                                        targetSpell = SpellTimerTable.GetOrAddInstance(
                                            replacedTitle,
                                            spell);

                                        // インスタンスのガーベージタイマをスタートする
                                        targetSpell.StartGarbageInstanceTimer();
                                    }

                                    targetSpell.SpellTitleReplaced = replacedTitle;
                                    targetSpell.MatchDateTime      = DateTime.Now;
                                    targetSpell.UpdateDone         = false;
                                    targetSpell.OverDone           = false;
                                    targetSpell.BeforeDone         = false;
                                    targetSpell.TimeupDone         = false;

                                    // 効果時間を決定する
                                    // グループ "duration" をキャプチャーしていた場合は効果時間を置換する
                                    var durationAsText = match.Groups["duration"].Value;
                                    double duration;
                                    if (!double.TryParse(durationAsText, out duration))
                                    {
                                        duration = targetSpell.RecastTime;
                                    }

                                    targetSpell.CompleteScheduledTime = targetSpell.MatchDateTime.AddSeconds(duration);

                                    // スペル対象を保存する
                                    // グループ "target" をキャプチャーしていた場合はその文字列を保存する
                                    var targetName = match.Groups["target"].Value;
                                    if (!string.IsNullOrWhiteSpace(targetName))
                                    {
                                        targetSpell.TargetName = targetName;
                                    }

                                    // マッチ時点のサウンドを再生する
                                    this.Play(targetSpell.MatchSound);

                                    if (!string.IsNullOrWhiteSpace(targetSpell.MatchTextToSpeak))
                                    {
                                        var tts = match.Result(targetSpell.MatchTextToSpeak);
                                        this.Play(tts);
                                    }

                                    notifyNeeded = true;

                                    // 遅延サウンドタイマを開始する
                                    targetSpell.StartOverSoundTimer();
                                    targetSpell.StartBeforeSoundTimer();
                                    targetSpell.StartTimeupSoundTimer();
                                }
                            }
                        }
                    }

                    // 延長をマッチングする
                    if (spell.MatchDateTime > DateTime.MinValue)
                    {
                        var keywords      = new string[] { spell.KeywordForExtendReplaced1, spell.KeywordForExtendReplaced2 };
                        var regexes       = new Regex[] { spell.RegexForExtend1, spell.RegexForExtend2 };
                        var timeToExtends = new double[] { spell.RecastTimeExtending1, spell.RecastTimeExtending2 };

                        for (int i = 0; i < 2; i++)
                        {
                            var keywordToExtend = keywords[i];
                            var regexToExtend   = regexes[i];
                            var timeToExtend    = timeToExtends[i];

                            // マッチングする
                            var matched = false;

                            if (!spell.RegexEnabled ||
                                regexToExtend == null)
                            {
                                if (!string.IsNullOrWhiteSpace(keywordToExtend))
                                {
                                    matched = logLine.ToUpper().Contains(keywordToExtend.ToUpper());
                                }
                            }
                            else
                            {
                                var match = regexToExtend.Match(logLine);
                                matched   = match.Success;

                                if (matched)
                                {
                                    // targetをキャプチャーしている?
                                    if (!string.IsNullOrWhiteSpace(spell.TargetName))
                                    {
                                        var targetName = match.Groups["target"].Value;
                                        if (!string.IsNullOrWhiteSpace(targetName))
                                        {
                                            // targetが当初のマッチングと一致するか確認する
                                            if (spell.TargetName != targetName)
                                            {
                                                matched = false;
                                            }
                                        }
                                    }
                                }
                            }

                            if (!matched)
                            {
                                continue;
                            }

                            var now = DateTime.Now;

                            // リキャストタイムを延長する
                            var newSchedule  = spell.CompleteScheduledTime.AddSeconds(timeToExtend);
                            spell.BeforeDone = false;
                            spell.UpdateDone = false;

                            if (spell.ExtendBeyondOriginalRecastTime)
                            {
                                if (spell.UpperLimitOfExtension > 0)
                                {
                                    var newDuration = (newSchedule - now).TotalSeconds;
                                    if (newDuration > (double)spell.UpperLimitOfExtension)
                                    {
                                        newSchedule = newSchedule.AddSeconds(
                                            (newDuration - (double)spell.UpperLimitOfExtension) * -1);
                                    }
                                }
                            }
                            else
                            {
                                var newDuration = (newSchedule - now).TotalSeconds;
                                if (newDuration > (double)spell.RecastTime)
                                {
                                    newSchedule = newSchedule.AddSeconds(
                                        (newDuration - (double)spell.RecastTime) * -1);
                                }
                            }

                            spell.MatchDateTime         = now;
                            spell.CompleteScheduledTime = newSchedule;

                            notifyNeeded = true;

                            // 遅延サウンドタイマを開始(更新)する
                            spell.StartOverSoundTimer();
                            spell.StartBeforeSoundTimer();
                            spell.StartTimeupSoundTimer();
                        }
                    }
                    // end if 延長マッチング

                    // ACT標準のSpellTimerに変更を通知する
                    if (notifyNeeded)
                    {
                        this.UpdateNormalSpellTimer(spell, false);
                        this.NotifyNormalSpellTimer(spell);
                    }
                });
                // end loop of Spells
            }
            // end loop of LogLines
        }
        /// <summary>
        /// 1ログ1スペルに対して判定する
        /// </summary>
        /// <param name="spell">スペル</param>
        /// <param name="logLine">ログ</param>
        public void MatchCore(
            Models.SpellTimer spell,
            string logLine)
        {
            var regex        = spell.Regex;
            var notifyNeeded = false;

            if (!spell.IsInstance)
            {
                // マッチング計測開始
                spell.StartMatching();

                // 開始条件を確認する
                if (ConditionUtility.CheckConditionsForSpell(spell))
                {
                    // 正規表現が無効?
                    if (!spell.RegexEnabled ||
                        regex == null)
                    {
                        var keyword = spell.KeywordReplaced;
                        if (string.IsNullOrWhiteSpace(keyword))
                        {
                            return;
                        }

                        // キーワードが含まれるか?
                        if (logLine.Contains(keyword, StringComparison.OrdinalIgnoreCase))
                        {
                            var targetSpell = spell;

                            // ヒットしたログを格納する
                            targetSpell.MatchedLog = logLine;

                            // スペル名(表示テキスト)を置換する
                            var replacedTitle = ConditionUtility.GetReplacedTitle(targetSpell);

                            // PC名を置換する
                            replacedTitle = FFXIVPlugin.Instance.ReplacePartyMemberName(replacedTitle);

                            targetSpell.SpellTitleReplaced = replacedTitle;
                            targetSpell.UpdateDone         = false;
                            targetSpell.OverDone           = false;
                            targetSpell.BeforeDone         = false;
                            targetSpell.TimeupDone         = false;

                            var now = DateTime.Now;
                            targetSpell.CompleteScheduledTime = now.AddSeconds(targetSpell.RecastTime);
                            targetSpell.MatchDateTime         = now;

                            // マッチング計測終了
                            spell.EndMatching();

                            // マッチ時点のサウンドを再生する
                            this.Play(targetSpell.MatchSound);
                            this.Play(targetSpell.MatchTextToSpeak);

                            // DISCORDに通知する?
                            if (targetSpell.NotifyToDiscord)
                            {
                                DiscordBridge.Instance.SendMessageDelegate?.Invoke(
                                    $"{replacedTitle} {targetSpell.RecastTime:N0}");
                            }

                            notifyNeeded = true;

                            // 遅延サウンドタイマを開始する
                            targetSpell.StartOverSoundTimer();
                            targetSpell.StartBeforeSoundTimer();
                            targetSpell.StartTimeupSoundTimer();
                        }
                    }
                    else
                    {
                        // 正規表現でマッチングする
                        var match = regex.Match(logLine);
                        if (match.Success)
                        {
                            var targetSpell = spell;

                            // ヒットしたログを格納する
                            targetSpell.MatchedLog = logLine;

                            // スペル名(表示テキスト)を置換する
                            var replacedTitle = match.Result(ConditionUtility.GetReplacedTitle(targetSpell));

                            // PC名を置換する
                            replacedTitle = FFXIVPlugin.Instance.ReplacePartyMemberName(replacedTitle);

                            // インスタンス化する?
                            if (spell.ToInstance)
                            {
                                // 同じタイトルのインスタンススペルを探す
                                // 存在すればそれを使用して、なければ新しいインスタンスを生成する
                                targetSpell = SpellTimerTable.Instance.GetOrAddInstance(
                                    replacedTitle,
                                    spell);

                                // インスタンスのガーベージタイマをスタートする
                                targetSpell.StartGarbageInstanceTimer();
                            }

                            targetSpell.SpellTitleReplaced = replacedTitle;
                            targetSpell.UpdateDone         = false;
                            targetSpell.OverDone           = false;
                            targetSpell.BeforeDone         = false;
                            targetSpell.TimeupDone         = false;

                            var now = DateTime.Now;

                            // 効果時間を決定する
                            // グループ "duration" をキャプチャーしていた場合は効果時間を置換する
                            var    durationAsText = match.Groups["duration"].Value;
                            double duration;
                            if (!double.TryParse(durationAsText, out duration))
                            {
                                duration = targetSpell.RecastTime;
                            }

                            targetSpell.CompleteScheduledTime = now.AddSeconds(duration);

                            // スペル対象を保存する
                            // グループ "target" をキャプチャーしていた場合はその文字列を保存する
                            var targetName = match.Groups["target"].Value;
                            if (!string.IsNullOrWhiteSpace(targetName))
                            {
                                targetSpell.TargetName = targetName;
                            }

                            // マッチ日時を格納する
                            targetSpell.MatchDateTime = now;

                            // マッチング計測終了
                            spell.EndMatching();

                            // マッチ時点のサウンドを再生する
                            this.Play(targetSpell.MatchSound);

                            if (!string.IsNullOrWhiteSpace(targetSpell.MatchTextToSpeak))
                            {
                                var tts = match.Result(targetSpell.MatchTextToSpeak);
                                this.Play(tts);
                            }

                            // DISCORDに通知する?
                            if (targetSpell.NotifyToDiscord)
                            {
                                DiscordBridge.Instance.SendMessageDelegate?.Invoke(
                                    $"{replacedTitle} {targetSpell.RecastTime:N0}");
                            }

                            notifyNeeded = true;

                            // 遅延サウンドタイマを開始する
                            targetSpell.StartOverSoundTimer();
                            targetSpell.StartBeforeSoundTimer();
                            targetSpell.StartTimeupSoundTimer();
                        }
                    }
                }
            }

            // 延長をマッチングする
            if (spell.MatchDateTime > DateTime.MinValue)
            {
                var keywords      = new string[] { spell.KeywordForExtendReplaced1, spell.KeywordForExtendReplaced2 };
                var regexes       = new Regex[] { spell.RegexForExtend1, spell.RegexForExtend2 };
                var timeToExtends = new double[] { spell.RecastTimeExtending1, spell.RecastTimeExtending2 };

                for (int i = 0; i < 2; i++)
                {
                    var keywordToExtend = keywords[i];
                    var regexToExtend   = regexes[i];
                    var timeToExtend    = timeToExtends[i];

                    // マッチングする
                    var matched = false;

                    if (!spell.RegexEnabled ||
                        regexToExtend == null)
                    {
                        if (!string.IsNullOrWhiteSpace(keywordToExtend))
                        {
                            matched = logLine.Contains(keywordToExtend, StringComparison.OrdinalIgnoreCase);
                        }
                    }
                    else
                    {
                        var match = regexToExtend.Match(logLine);
                        matched = match.Success;

                        if (matched)
                        {
                            // targetをキャプチャーしている?
                            if (!string.IsNullOrWhiteSpace(spell.TargetName))
                            {
                                var targetName = match.Groups["target"].Value;
                                if (!string.IsNullOrWhiteSpace(targetName))
                                {
                                    // targetが当初のマッチングと一致するか確認する
                                    if (spell.TargetName != targetName)
                                    {
                                        matched = false;
                                    }
                                }
                            }
                        }
                    }

                    if (!matched)
                    {
                        continue;
                    }

                    var now = DateTime.Now;

                    // リキャストタイムを延長する
                    var newSchedule = spell.CompleteScheduledTime.AddSeconds(timeToExtend);
                    spell.BeforeDone = false;
                    spell.UpdateDone = false;

                    if (spell.ExtendBeyondOriginalRecastTime)
                    {
                        if (spell.UpperLimitOfExtension > 0)
                        {
                            var newDuration = (newSchedule - now).TotalSeconds;
                            if (newDuration > (double)spell.UpperLimitOfExtension)
                            {
                                newSchedule = newSchedule.AddSeconds(
                                    (newDuration - (double)spell.UpperLimitOfExtension) * -1);
                            }
                        }
                    }
                    else
                    {
                        var newDuration = (newSchedule - now).TotalSeconds;
                        if (newDuration > (double)spell.RecastTime)
                        {
                            newSchedule = newSchedule.AddSeconds(
                                (newDuration - (double)spell.RecastTime) * -1);
                        }
                    }

                    spell.CompleteScheduledTime = newSchedule;
                    spell.MatchDateTime         = now;

                    notifyNeeded = true;

                    // 遅延サウンドタイマを開始(更新)する
                    spell.StartOverSoundTimer();
                    spell.StartBeforeSoundTimer();
                    spell.StartTimeupSoundTimer();
                }
            }
            // end if 延長マッチング

            // ACT標準のSpellTimerに変更を通知する
            if (notifyNeeded)
            {
                this.UpdateNormalSpellTimer(spell, false);
                this.NotifyNormalSpellTimer(spell);
            }
        }