muffin v7.4.5

This commit is contained in:
alydev 2025-12-23 10:08:49 +10:00
parent 8461a561d3
commit bb09805213
11 changed files with 881 additions and 891 deletions

View file

@ -18,9 +18,7 @@ public static class DataManagerExtensions
public static string? GetString<T>(this IDataManager dataManager, string key, IPluginLog? pluginLog) where T : struct, IQuestDialogueText, IExcelRow<T> public static string? GetString<T>(this IDataManager dataManager, string key, IPluginLog? pluginLog) where T : struct, IQuestDialogueText, IExcelRow<T>
{ {
string text = dataManager.GetSeString<T>(key)?.WithCertainMacroCodeReplacements(); return dataManager.GetSeString<T>(key)?.WithCertainMacroCodeReplacements();
pluginLog?.Verbose($"{typeof(T).Name}.{key} => {text}");
return text;
} }
public static Regex? GetRegex<T>(this IDataManager dataManager, string key, IPluginLog? pluginLog) where T : struct, IQuestDialogueText, IExcelRow<T> public static Regex? GetRegex<T>(this IDataManager dataManager, string key, IPluginLog? pluginLog) where T : struct, IQuestDialogueText, IExcelRow<T>
@ -30,9 +28,7 @@ public static class DataManagerExtensions
{ {
return null; return null;
} }
string text = string.Join("", seString.Select((ReadOnlySePayload payload) => (payload.Type == ReadOnlySePayloadType.Text) ? Regex.Escape(payload.ToString()) : "(.*)")); return new Regex(string.Join("", seString.Select((ReadOnlySePayload payload) => (payload.Type == ReadOnlySePayloadType.Text) ? Regex.Escape(payload.ToString()) : "(.*)")));
pluginLog?.Verbose($"{typeof(T).Name}.{key} => /{text}/");
return new Regex(text);
} }
public static ReadOnlySeString? GetSeString<T>(this IDataManager dataManager, uint rowId, Func<T, ReadOnlySeString?> mapper) where T : struct, IExcelRow<T> public static ReadOnlySeString? GetSeString<T>(this IDataManager dataManager, uint rowId, Func<T, ReadOnlySeString?> mapper) where T : struct, IExcelRow<T>
@ -49,9 +45,7 @@ public static class DataManagerExtensions
public static string? GetString<T>(this IDataManager dataManager, uint rowId, Func<T, ReadOnlySeString?> mapper, IPluginLog? pluginLog = null) where T : struct, IExcelRow<T> public static string? GetString<T>(this IDataManager dataManager, uint rowId, Func<T, ReadOnlySeString?> mapper, IPluginLog? pluginLog = null) where T : struct, IExcelRow<T>
{ {
string text = dataManager.GetSeString(rowId, mapper)?.WithCertainMacroCodeReplacements(); return dataManager.GetSeString(rowId, mapper)?.WithCertainMacroCodeReplacements();
pluginLog?.Verbose($"{typeof(T).Name}.{rowId} => {text}");
return text;
} }
public static Regex? GetRegex<T>(this IDataManager dataManager, uint rowId, Func<T, ReadOnlySeString?> mapper, IPluginLog? pluginLog = null) where T : struct, IExcelRow<T> public static Regex? GetRegex<T>(this IDataManager dataManager, uint rowId, Func<T, ReadOnlySeString?> mapper, IPluginLog? pluginLog = null) where T : struct, IExcelRow<T>
@ -61,9 +55,7 @@ public static class DataManagerExtensions
{ {
return null; return null;
} }
Regex regex = seString.ToRegex(); return seString.ToRegex();
pluginLog?.Verbose($"{typeof(T).Name}.{rowId} => /{regex}/");
return regex;
} }
public static Regex? GetRegex<T>(this T excelRow, Func<T, ReadOnlySeString?> mapper, IPluginLog? pluginLog) where T : struct, IExcelRow<T> public static Regex? GetRegex<T>(this T excelRow, Func<T, ReadOnlySeString?> mapper, IPluginLog? pluginLog) where T : struct, IExcelRow<T>
@ -74,9 +66,7 @@ public static class DataManagerExtensions
{ {
return null; return null;
} }
Regex regex = text.ToRegex(); return text.ToRegex();
pluginLog?.Verbose($"{typeof(T).Name}.regex => /{regex}/");
return regex;
} }
public static Regex ToRegex(this ReadOnlySeString? text) public static Regex ToRegex(this ReadOnlySeString? text)

View file

@ -69,7 +69,6 @@ internal sealed class CreditsController : IDisposable
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, CreditScrollArray, _creditScrollHandler); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, CreditScrollArray, _creditScrollHandler);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, CreditArray, _creditHandler); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, CreditArray, _creditHandler);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, CreditPlayerArray, _creditPlayerHandler); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, CreditPlayerArray, _creditPlayerHandler);
_logger.LogDebug("CreditsController: registered listeners and ready to skip up to {MaxSkips} successive cutscenes.", _maxConsecutiveSkips);
} }
} }
} }

View file

@ -6,7 +6,6 @@ using Dalamud.Game.ClientState.Conditions;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Client.System.Input; using FFXIVClientStructs.FFXIV.Client.System.Input;
using FFXIVClientStructs.FFXIV.Client.System.String;
using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Common; using Questionable.Controller.Steps.Common;
@ -37,10 +36,12 @@ internal static class Dive
} }
} }
internal sealed class DoDive(ICondition condition, ILogger<DoDive> logger) : AbstractDelayedTaskExecutor<Task>(TimeSpan.FromSeconds(5L)) internal sealed class DoDive(ICondition condition, ILogger<DoDive> logger) : AbstractDelayedTaskExecutor<Task>(TimeSpan.FromSeconds(5L)), IStoppableTaskExecutor, ITaskExecutor
{ {
private readonly Queue<(uint Type, nint Key)> _keysToPress = new Queue<(uint, nint)>(); private readonly Queue<(uint Type, nint Key)> _keysToPress = new Queue<(uint, nint)>();
private readonly HashSet<nint> _pressedKeys = new HashSet<nint>();
private int _attempts; private int _attempts;
protected override bool StartInternal() protected override bool StartInternal()
@ -67,11 +68,30 @@ internal static class Dive
} }
logger.LogDebug("{Action} key {KeyCode:X2}", (result.Item1 == 256) ? "Pressing" : "Releasing", result.Item2); logger.LogDebug("{Action} key {KeyCode:X2}", (result.Item1 == 256) ? "Pressing" : "Releasing", result.Item2);
NativeMethods.SendMessage((nint)Device.Instance()->hWnd, result.Item1, result.Item2, IntPtr.Zero); NativeMethods.SendMessage((nint)Device.Instance()->hWnd, result.Item1, result.Item2, IntPtr.Zero);
if (result.Item1 == 256)
{
_pressedKeys.Add(result.Item2);
}
else if (result.Item1 == 257)
{
_pressedKeys.Remove(result.Item2);
}
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
} }
return base.Update(); return base.Update();
} }
public unsafe void StopNow()
{
foreach (nint pressedKey in _pressedKeys)
{
logger.LogDebug("Releasing stuck key {KeyCode:X2} on stop", pressedKey);
NativeMethods.SendMessage((nint)Device.Instance()->hWnd, 257u, pressedKey, IntPtr.Zero);
}
_pressedKeys.Clear();
_keysToPress.Clear();
}
public override bool ShouldInterruptOnDamage() public override bool ShouldInterruptOnDamage()
{ {
return false; return false;
@ -83,7 +103,7 @@ internal static class Dive
{ {
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
} }
if (_attempts >= 3) if (_attempts >= 5)
{ {
throw new TaskException("Please dive manually."); throw new TaskException("Please dive manually.");
} }
@ -94,22 +114,25 @@ internal static class Dive
private unsafe void Descend() private unsafe void Descend()
{ {
byte* ptr = stackalloc byte[16]; Keybind* keybind = UIInputData.Instance()->GetKeybind(InputId.MOVE_DESCENT);
Utf8String* name = Utf8String.FromString("MOVE_DESCENT"); if (keybind == null)
UIInputData.Instance()->GetKeybindByName(name, (Keybind*)ptr); {
SeVirtualKey seVirtualKey = (SeVirtualKey)(*(uint*)ptr); throw new TaskException("Could not find descent keybind");
byte b = ptr[4]; }
SeVirtualKey seVirtualKey2 = (SeVirtualKey)((uint*)ptr)[2]; Span<KeySetting> keySettings = keybind->KeySettings;
byte b2 = ptr[12]; SeVirtualKey key = keySettings[0].Key;
logger.LogInformation("Dive keybind: {Key1} + {Modifier1}, {Key2} + {Modifier2}", seVirtualKey, b, seVirtualKey2, b2); KeyModifierFlag keyModifier = keySettings[0].KeyModifier;
SeVirtualKey key2 = keySettings[1].Key;
KeyModifierFlag keyModifier2 = keySettings[1].KeyModifier;
logger.LogInformation("Dive keybind: {Key1} + {Modifier1}, {Key2} + {Modifier2}", key, keyModifier, key2, keyModifier2);
int num = 2; int num = 2;
List<List<nint>> list = new List<List<nint>>(num); List<List<nint>> list = new List<List<nint>>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<List<nint>> span = CollectionsMarshal.AsSpan(list); Span<List<nint>> span = CollectionsMarshal.AsSpan(list);
int num2 = 0; int num2 = 0;
span[num2] = GetKeysToPress(seVirtualKey, b); span[num2] = GetKeysToPress(key, keyModifier);
num2++; num2++;
span[num2] = GetKeysToPress(seVirtualKey2, b2); span[num2] = GetKeysToPress(key2, keyModifier2);
List<nint> list2 = (from x in list List<nint> list2 = (from x in list
where x != null where x != null
select (x)).MinBy((List<nint> x) => x.Count); select (x)).MinBy((List<nint> x) => x.Count);
@ -120,10 +143,12 @@ internal static class Dive
foreach (nint item in list2) foreach (nint item in list2)
{ {
_keysToPress.Enqueue((256u, item)); _keysToPress.Enqueue((256u, item));
_keysToPress.Enqueue((0u, 0)); for (int num3 = 0; num3 < 15; num3++)
{
_keysToPress.Enqueue((0u, 0)); _keysToPress.Enqueue((0u, 0));
} }
for (int num3 = 0; num3 < 5; num3++) }
for (int num4 = 0; num4 < 5; num4++)
{ {
_keysToPress.Enqueue((0u, 0)); _keysToPress.Enqueue((0u, 0));
} }
@ -146,18 +171,18 @@ internal static class Dive
public static extern nint SendMessage(nint hWnd, uint Msg, nint wParam, nint lParam); public static extern nint SendMessage(nint hWnd, uint Msg, nint wParam, nint lParam);
} }
private static List<nint>? GetKeysToPress(SeVirtualKey key, byte modifier) private static List<nint>? GetKeysToPress(SeVirtualKey key, KeyModifierFlag modifier)
{ {
List<nint> list = new List<nint>(); List<nint> list = new List<nint>();
if ((modifier & 1) != 0) if ((modifier & KeyModifierFlag.Ctrl) != KeyModifierFlag.None)
{ {
list.Add(17); list.Add(17);
} }
if ((modifier & 2) != 0) if ((modifier & KeyModifierFlag.Shift) != KeyModifierFlag.None)
{ {
list.Add(16); list.Add(16);
} }
if ((modifier & 4) != 0) if ((modifier & KeyModifierFlag.Alt) != KeyModifierFlag.None)
{ {
list.Add(18); list.Add(18);
} }

View file

@ -131,7 +131,7 @@ internal sealed class QuestRegistry
} }
if (_questData.TryGetQuestInfo(elementId2, out IQuestInfo questInfo)) if (_questData.TryGetQuestInfo(elementId2, out IQuestInfo questInfo))
{ {
goto IL_01c8; goto IL_01ae;
} }
if (elementId2 is UnlockLinkId unlockLinkId) if (elementId2 is UnlockLinkId unlockLinkId)
{ {
@ -147,18 +147,16 @@ internal sealed class QuestRegistry
text = $"Unlock Link {unlockLinkId.Value}"; text = $"Unlock Link {unlockLinkId.Value}";
} }
questInfo = new UnlockLinkQuestInfo(unlockLinkId, text, 0u, dateTime); questInfo = new UnlockLinkQuestInfo(unlockLinkId, text, 0u, dateTime);
_logger.LogDebug("Created UnlockLinkQuestInfo for {QuestId} from assembly", elementId2);
_questData.AddOrReplaceQuestInfo(questInfo); _questData.AddOrReplaceQuestInfo(questInfo);
goto IL_01c8; goto IL_01ae;
} }
_logger.LogWarning("Not loading unknown quest {QuestId} from assembly: Quest not found in quest data", elementId2); _logger.LogWarning("Not loading unknown quest {QuestId} from assembly: Quest not found in quest data", elementId2);
goto end_IL_003d; goto end_IL_003d;
IL_01c8: IL_01ae:
if (flag2 || flag3) if (flag2 || flag3)
{ {
bool flag4 = flag ?? questInfo.IsSeasonalQuest; bool isSeasonal = flag ?? questInfo.IsSeasonalQuest;
_questData.ApplySeasonalOverride(elementId2, flag4, dateTime); _questData.ApplySeasonalOverride(elementId2, isSeasonal, dateTime);
_logger.LogDebug("Applied seasonal override for quest {QuestId} from assembly: IsSeasonal={IsSeasonal}, Expiry={Expiry}", elementId2, flag4, dateTime?.ToString("o") ?? "(null)");
} }
IQuestInfo questInfo2 = _questData.GetQuestInfo(elementId2); IQuestInfo questInfo2 = _questData.GetQuestInfo(elementId2);
Quest quest = new Quest Quest quest = new Quest

File diff suppressed because it is too large Load diff

View file

@ -109,7 +109,6 @@ internal sealed class JournalData
if (journalCategory.RowId != 0) if (journalCategory.RowId != 0)
{ {
SeasonalEventsCategoryId = journalCategory.RowId; SeasonalEventsCategoryId = journalCategory.RowId;
_logger.LogInformation("Resolved 'Seasonal Events' JournalCategory ID: {CategoryId}", SeasonalEventsCategoryId);
} }
else else
{ {
@ -121,7 +120,6 @@ internal sealed class JournalData
SeasonalEventGenreIds = (from x in excelSheet SeasonalEventGenreIds = (from x in excelSheet
where x.RowId != 0 && x.JournalCategory.RowId == journalData.SeasonalEventsCategoryId.Value where x.RowId != 0 && x.JournalCategory.RowId == journalData.SeasonalEventsCategoryId.Value
select x.RowId).ToHashSet(); select x.RowId).ToHashSet();
_logger.LogInformation("Resolved {Count} seasonal event JournalGenre IDs from JournalCategory {CategoryId}: [{Ids}]", SeasonalEventGenreIds.Count, SeasonalEventsCategoryId.Value, string.Join(", ", SeasonalEventGenreIds.OrderBy((uint x) => x)));
} }
else else
{ {
@ -131,7 +129,6 @@ internal sealed class JournalData
if (journalGenre.RowId != 0) if (journalGenre.RowId != 0)
{ {
MoogleDeliveryGenreId = journalGenre.RowId; MoogleDeliveryGenreId = journalGenre.RowId;
_logger.LogInformation("Resolved 'Delivery Moogle Quests' JournalGenre ID: {GenreId}", MoogleDeliveryGenreId);
} }
else else
{ {
@ -171,6 +168,7 @@ internal sealed class JournalData
if (section != null) if (section != null)
{ {
int num = 0; int num = 0;
{
foreach (Category category in section.Categories) foreach (Category category in section.Categories)
{ {
foreach (Genre genre in category.Genres) foreach (Genre genre in category.Genres)
@ -179,13 +177,11 @@ internal sealed class JournalData
num++; num++;
} }
} }
_logger.LogInformation("Marked {Count} genres as under 'Other Quests' (OtherQuestsSectionRowId={Id})", num, valueOrDefault); return;
}
} }
else
{
_logger.LogWarning("OtherQuestsSectionRowId = {Id} found, but matching Section not present in constructed Sections", valueOrDefault); _logger.LogWarning("OtherQuestsSectionRowId = {Id} found, but matching Section not present in constructed Sections", valueOrDefault);
} }
}
else else
{ {
_logger.LogDebug("OtherQuestsSectionRowId not found - falling back to localized name lookup when necessary"); _logger.LogDebug("OtherQuestsSectionRowId not found - falling back to localized name lookup when necessary");

View file

@ -1220,7 +1220,7 @@ internal sealed class QuestData
} }
else else
{ {
_logger.LogWarning("ApplySeasonalOverride: Quest {QuestId} not found in QuestData (could not apply seasonal override)", questId); _ = value is UnlockLinkQuestInfo;
} }
} }
@ -1228,7 +1228,6 @@ internal sealed class QuestData
{ {
ArgumentNullException.ThrowIfNull(info, "info"); ArgumentNullException.ThrowIfNull(info, "info");
_quests[info.QuestId] = info; _quests[info.QuestId] = info;
_logger.LogDebug("Added or replaced QuestInfo for {QuestId} in QuestData", info.QuestId);
} }
static QuestData() static QuestData()

View file

@ -27,7 +27,6 @@ internal sealed class QuestValidator
{ {
_validators = validators.ToList(); _validators = validators.ToList();
_logger = logger; _logger = logger;
_logger.LogInformation("Validators: {Validators}", string.Join(", ", _validators.Select((IQuestValidator x) => x.GetType().Name)));
} }
public void Reset() public void Reset()

View file

@ -265,7 +265,6 @@ internal sealed class SinglePlayerDutyConfigComponent : ConfigComponent
{ {
if (quest.Root.Disabled) if (quest.Root.Disabled)
{ {
_logger.LogDebug("Disabling quest battle for quest {QuestId}, quest is disabled", questId);
return (Enabled: false, Options: singlePlayerDutyOptions); return (Enabled: false, Options: singlePlayerDutyOptions);
} }
QuestStep questStep = (from x in quest.AllSteps() QuestStep questStep = (from x in quest.AllSteps()
@ -277,7 +276,6 @@ internal sealed class SinglePlayerDutyConfigComponent : ConfigComponent
} }
return (Enabled: true, Options: questStep.SinglePlayerDutyOptions ?? singlePlayerDutyOptions); return (Enabled: true, Options: questStep.SinglePlayerDutyOptions ?? singlePlayerDutyOptions);
} }
_logger.LogDebug("Disabling quest battle for quest {QuestId}, unknown quest", questId);
return (Enabled: false, Options: singlePlayerDutyOptions); return (Enabled: false, Options: singlePlayerDutyOptions);
} }

View file

@ -492,7 +492,6 @@ internal sealed class QuestJournalComponent
_categoryCounts.Clear(); _categoryCounts.Clear();
_sectionCounts.Clear(); _sectionCounts.Clear();
bool hideSeasonalEventsFromJournalProgress = _configuration.General.HideSeasonalEventsFromJournalProgress; bool hideSeasonalEventsFromJournalProgress = _configuration.General.HideSeasonalEventsFromJournalProgress;
_logger.LogInformation("Refreshing journal counts. HideSeasonalEventsFromJournalProgress={Hide}", hideSeasonalEventsFromJournalProgress);
foreach (JournalData.Genre genre in _journalData.Genres) foreach (JournalData.Genre genre in _journalData.Genres)
{ {
List<IQuestInfo> source = ((hideSeasonalEventsFromJournalProgress && _journalData.SeasonalEventsCategoryId.HasValue && genre.CategoryId == _journalData.SeasonalEventsCategoryId.Value) ? genre.Quests.Where((IQuestInfo q) => !IsSeasonal(q)).ToList() : genre.Quests.ToList()); List<IQuestInfo> source = ((hideSeasonalEventsFromJournalProgress && _journalData.SeasonalEventsCategoryId.HasValue && genre.CategoryId == _journalData.SeasonalEventsCategoryId.Value) ? genre.Quests.Where((IQuestInfo q) => !IsSeasonal(q)).ToList() : genre.Quests.ToList());

View file

@ -251,33 +251,6 @@ internal sealed class EventInfoComponent
} }
questInfo = quest.Info; questInfo = quest.Info;
} }
try
{
bool flag = false;
DateTime? dateTime = null;
if (questInfo is QuestInfo questInfo2)
{
flag = questInfo2.IsSeasonalQuest || questInfo2.IsSeasonalEvent || questInfo2.SeasonalQuestExpiry is DateTime || (questInfo2.JournalGenre.HasValue && _journalData.SeasonalEventGenreIds.Contains(questInfo2.JournalGenre.Value));
dateTime = questInfo2.SeasonalQuestExpiry;
}
if (flag)
{
if (dateTime.HasValue)
{
DateTime valueOrDefault = dateTime.GetValueOrDefault();
DateTime dateTime2 = NormalizeExpiry(valueOrDefault);
_logger.LogInformation("Seasonal details: Quest {QuestId} '{Name}' rawExpiry={Raw:o} Kind={Kind} TimeOfDay={TimeOfDay} normalizedUtc={Normalized:o}", questInfo.QuestId, questInfo.Name, valueOrDefault, valueOrDefault.Kind, valueOrDefault.TimeOfDay, dateTime2);
}
else
{
_logger.LogInformation("Seasonal details: Quest {QuestId} '{Name}' has no expiry (seasonal flag present). IsSeasonalEvent={IsSeasonalEvent} IsSeasonalQuest={IsSeasonalQuest} JournalGenre={JournalGenre} SeasonalQuestExpiry={SeasonalQuestExpiry}", questInfo.QuestId, questInfo.Name, questInfo is QuestInfo questInfo3 && questInfo3.IsSeasonalEvent, questInfo is QuestInfo questInfo4 && questInfo4.IsSeasonalQuest, (questInfo is QuestInfo questInfo5) ? questInfo5.JournalGenre : ((uint?)null), (questInfo is QuestInfo questInfo6) ? questInfo6.SeasonalQuestExpiry : ((DateTime?)null));
}
}
}
catch (Exception exception)
{
_logger.LogDebug(exception, "Failed to log seasonal details for {QuestId}", questInfo.QuestId);
}
if (_questFunctions.IsQuestUnobtainable(questInfo.QuestId)) if (_questFunctions.IsQuestUnobtainable(questInfo.QuestId))
{ {
if (_alreadyLoggedActiveSeasonalSkip.Add(questInfo.QuestId.Value)) if (_alreadyLoggedActiveSeasonalSkip.Add(questInfo.QuestId.Value))
@ -289,15 +262,15 @@ internal sealed class EventInfoComponent
{ {
if (questExpiry.HasValue) if (questExpiry.HasValue)
{ {
DateTime valueOrDefault2 = questExpiry.GetValueOrDefault(); DateTime valueOrDefault = questExpiry.GetValueOrDefault();
DateTime dateTime3 = NormalizeExpiry(valueOrDefault2); DateTime dateTime = NormalizeExpiry(valueOrDefault);
if (dateTime3 > DateTime.UtcNow) if (dateTime > DateTime.UtcNow)
{ {
yield return questInfo; yield return questInfo;
} }
else if (_alreadyLoggedActiveSeasonalSkip.Add(questInfo.QuestId.Value)) else if (_alreadyLoggedActiveSeasonalSkip.Add(questInfo.QuestId.Value))
{ {
_logger.LogDebug("Skipping UnlockLink quest {QuestId} '{Name}': expiry {Expiry:o} UTC is not in the future", questInfo.QuestId, questInfo.Name, dateTime3); _logger.LogDebug("Skipping UnlockLink quest {QuestId} '{Name}': expiry {Expiry:o} UTC is not in the future", questInfo.QuestId, questInfo.Name, dateTime);
} }
} }
else else
@ -307,24 +280,24 @@ internal sealed class EventInfoComponent
} }
else else
{ {
if (!(questInfo is QuestInfo { SeasonalQuestExpiry: var seasonalQuestExpiry } questInfo7)) if (!(questInfo is QuestInfo { SeasonalQuestExpiry: var seasonalQuestExpiry } questInfo2))
{ {
continue; continue;
} }
if (seasonalQuestExpiry.HasValue) if (seasonalQuestExpiry.HasValue)
{ {
DateTime valueOrDefault3 = seasonalQuestExpiry.GetValueOrDefault(); DateTime valueOrDefault2 = seasonalQuestExpiry.GetValueOrDefault();
DateTime dateTime4 = NormalizeExpiry(valueOrDefault3); DateTime dateTime2 = NormalizeExpiry(valueOrDefault2);
if (dateTime4 > DateTime.UtcNow) if (dateTime2 > DateTime.UtcNow)
{ {
yield return questInfo; yield return questInfo;
} }
else if (_alreadyLoggedActiveSeasonalSkip.Add(questInfo.QuestId.Value)) else if (_alreadyLoggedActiveSeasonalSkip.Add(questInfo.QuestId.Value))
{ {
_logger.LogDebug("Skipping quest {QuestId} '{Name}': seasonal expiry {Expiry:o} UTC is not in the future", questInfo.QuestId, questInfo.Name, dateTime4); _logger.LogDebug("Skipping quest {QuestId} '{Name}': seasonal expiry {Expiry:o} UTC is not in the future", questInfo.QuestId, questInfo.Name, dateTime2);
} }
} }
else if (questInfo7.IsSeasonalQuest && !questInfo7.SeasonalQuestExpiry.HasValue) else if (questInfo2.IsSeasonalQuest && !questInfo2.SeasonalQuestExpiry.HasValue)
{ {
yield return questInfo; yield return questInfo;
} }
@ -354,12 +327,12 @@ internal sealed class EventInfoComponent
return q.JournalGenre.HasValue ? GetJournalGenreName(q.JournalGenre.Value) : q.Name; return q.JournalGenre.HasValue ? GetJournalGenreName(q.JournalGenre.Value) : q.Name;
})) }))
{ {
DateTime dateTime = item.Select(delegate(IQuestInfo q) item.Select(delegate(IQuestInfo q)
{ {
DateTime? dateTime2 = (q as QuestInfo)?.SeasonalQuestExpiry ?? ((q is UnlockLinkQuestInfo unlockLinkQuestInfo) ? unlockLinkQuestInfo.QuestExpiry : ((DateTime?)null)); DateTime? dateTime = (q as QuestInfo)?.SeasonalQuestExpiry ?? ((q is UnlockLinkQuestInfo unlockLinkQuestInfo) ? unlockLinkQuestInfo.QuestExpiry : ((DateTime?)null));
if (dateTime2.HasValue) if (dateTime.HasValue)
{ {
DateTime valueOrDefault = dateTime2.GetValueOrDefault(); DateTime valueOrDefault = dateTime.GetValueOrDefault();
return NormalizeExpiry(valueOrDefault); return NormalizeExpiry(valueOrDefault);
} }
return DateTime.MaxValue; return DateTime.MaxValue;
@ -368,14 +341,9 @@ internal sealed class EventInfoComponent
select (q as UnlockLinkQuestInfo)?.Patch into p select (q as UnlockLinkQuestInfo)?.Patch into p
where !string.IsNullOrEmpty(p) where !string.IsNullOrEmpty(p)
select p).Distinct().ToList(); select p).Distinct().ToList();
string text = ((list.Count == 1) ? list[0] : null); if (list.Count == 1)
if (dateTime != DateTime.MaxValue)
{ {
_logger.LogInformation("Seasonal event '{Name}' ends at {Expiry:o} UTC (patch={Patch})", item.Key, dateTime, text ?? "n/a"); _ = list[0];
}
else
{
_logger.LogInformation("Seasonal event '{Name}' has no expiry (patch={Patch})", item.Key, text ?? "n/a");
} }
} }
} }