using System; using System.Collections.Generic; using LLib.GameData; using Microsoft.Extensions.Logging; using Questionable.Controller.Steps.Common; using Questionable.Model; using Questionable.Model.Questing; namespace Questionable.Controller.Steps.Interactions; internal static class CriticalEncounter { internal sealed class Factory : ITaskFactory { public IEnumerable CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) { if (step.InteractionType != EInteractionType.CriticalEncounter) { return Array.Empty(); } ushort? criticalEncounterId = step.CriticalEncounterId; if ((!criticalEncounterId.HasValue || criticalEncounterId.GetValueOrDefault() == 0) ? true : false) { throw new InvalidOperationException("CriticalEncounter step requires CriticalEncounterId"); } return new global::_003C_003Ez__ReadOnlySingleElementList(new WaitForCriticalEncounter(step.CriticalEncounterId.Value, step.Comment)); } } internal sealed record WaitForCriticalEncounter(ushort CriticalEncounterId, string? Comment) : ITask { public override string ToString() { return $"WaitForCriticalEncounter({CriticalEncounterId})"; } } internal sealed class WaitForCriticalEncounterExecutor(PublicEventHelper publicEventHelper, MovementController movementController, SendNotification.Executor sendNotificationExecutor, ILogger logger) : TaskExecutor() { private long _nextPollAt; private bool _loggedWaiting; private bool _notified; private bool _navigating; protected override bool Start() { logger.LogDebug("Waiting for critical encounter {Id}", base.Task.CriticalEncounterId); return true; } public override ETaskResult Update() { if (Environment.TickCount64 < _nextPollAt) { return ETaskResult.StillRunning; } PublicEvent? eventById = publicEventHelper.GetEventById(EPublicEventType.DynamicEvent, base.Task.CriticalEncounterId); if (eventById.HasValue) { PublicEvent valueOrDefault = eventById.GetValueOrDefault(); if (!_notified) { logger.LogInformation("Critical encounter {Id} ({Name}) is up", base.Task.CriticalEncounterId, valueOrDefault.Name); sendNotificationExecutor.Start(new SendNotification.Task(EInteractionType.CriticalEncounter, base.Task.Comment ?? valueOrDefault.Name)); _notified = true; } eventById = publicEventHelper.GetCurrentDynamicEvent(); if (eventById.HasValue && eventById.GetValueOrDefault().Id == base.Task.CriticalEncounterId) { return ETaskResult.TaskComplete; } if (valueOrDefault.IsActive) { _nextPollAt = Environment.TickCount64 + 2000; return ETaskResult.StillRunning; } if (!_navigating) { logger.LogInformation("Moving into critical encounter {Id} at {Position}", base.Task.CriticalEncounterId, valueOrDefault.Position); movementController.NavigateTo(EMovementType.Quest, null, valueOrDefault.Position, fly: false, sprint: true, 5f); _navigating = true; } _nextPollAt = Environment.TickCount64 + 1000; return ETaskResult.StillRunning; } if (!_loggedWaiting) { logger.LogDebug("Critical encounter {Id} not active yet, waiting for spawn", base.Task.CriticalEncounterId); _loggedWaiting = true; } _notified = false; _navigating = false; _nextPollAt = Environment.TickCount64 + 1000; return ETaskResult.StillRunning; } public override bool ShouldInterruptOnDamage() { return false; } } }