/// <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); } } }