muffin v7.4.7

This commit is contained in:
alydev 2025-12-28 12:35:39 +10:00
parent 1cc65e495d
commit 63f975ff4f
16 changed files with 1659 additions and 939 deletions

View file

@ -16,6 +16,7 @@ using Questionable.Controller.Steps;
using Questionable.Controller.Steps.Interactions;
using Questionable.Controller.Steps.Shared;
using Questionable.Data;
using Questionable.External;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
@ -115,6 +116,8 @@ internal sealed class QuestController : MiniTaskController<QuestController>
private readonly SinglePlayerDutyConfigComponent _singlePlayerDutyConfigComponent;
private readonly AutoDutyIpc _autoDutyIpc;
private readonly ILogger<QuestController> _logger;
private readonly object _progressLock = new object();
@ -155,6 +158,8 @@ internal sealed class QuestController : MiniTaskController<QuestController>
private static readonly TimeSpan EscDoublePressWindow = TimeSpan.FromSeconds(1L);
private HashSet<string> _stopConditionsMetAtStart = new HashSet<string>();
private const char ClipboardSeparator = ';';
public EAutomationType AutomationType
@ -236,7 +241,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
public event AutomationTypeChangedEventHandler? AutomationTypeChanged;
public QuestController(IClientState clientState, IObjectTable objectTable, GameFunctions gameFunctions, QuestFunctions questFunctions, MovementController movementController, CombatController combatController, GatheringController gatheringController, ILogger<QuestController> logger, QuestRegistry questRegistry, JournalData journalData, IKeyState keyState, IChatGui chatGui, ICondition condition, IToastGui toastGui, Configuration configuration, TaskCreator taskCreator, IServiceProvider serviceProvider, InterruptHandler interruptHandler, IDataManager dataManager, SinglePlayerDutyConfigComponent singlePlayerDutyConfigComponent)
public QuestController(IClientState clientState, IObjectTable objectTable, GameFunctions gameFunctions, QuestFunctions questFunctions, MovementController movementController, CombatController combatController, GatheringController gatheringController, ILogger<QuestController> logger, QuestRegistry questRegistry, JournalData journalData, IKeyState keyState, IChatGui chatGui, ICondition condition, IToastGui toastGui, Configuration configuration, TaskCreator taskCreator, IServiceProvider serviceProvider, InterruptHandler interruptHandler, IDataManager dataManager, SinglePlayerDutyConfigComponent singlePlayerDutyConfigComponent, AutoDutyIpc autoDutyIpc)
: base(chatGui, condition, serviceProvider, interruptHandler, dataManager, logger)
{
_clientState = clientState;
@ -255,6 +260,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
_configuration = configuration;
_taskCreator = taskCreator;
_singlePlayerDutyConfigComponent = singlePlayerDutyConfigComponent;
_autoDutyIpc = autoDutyIpc;
_logger = logger;
_toastGui.ErrorToast += base.OnErrorToast;
_toastGui.Toast += OnNormalToast;
@ -361,33 +367,46 @@ internal sealed class QuestController : MiniTaskController<QuestController>
if (_configuration.Stop.Enabled && _startedQuest != null)
{
string text = _startedQuest.Quest.Id.ToString();
if (_configuration.Stop.LevelToStopAfter && IsRunning && _objectTable[0] is IPlayerCharacter playerCharacter && playerCharacter.Level >= _configuration.Stop.TargetLevel)
if (_configuration.Stop.LevelStopMode != Configuration.EStopConditionMode.Off && IsRunning && _objectTable[0] is IPlayerCharacter playerCharacter && playerCharacter.Level >= _configuration.Stop.TargetLevel)
{
_logger.LogInformation("Reached level stop condition (current: {CurrentLevel}, target: {TargetLevel})", playerCharacter.Level, _configuration.Stop.TargetLevel);
_chatGui.Print($"Character level {playerCharacter.Level} reached target level {_configuration.Stop.TargetLevel}.", "Questionable", 576);
Stop($"Level stop condition reached [{playerCharacter.Level}]");
return;
string item = $"level:{_configuration.Stop.TargetLevel}";
if (_configuration.Stop.LevelStopMode != Configuration.EStopConditionMode.Pause || !_stopConditionsMetAtStart.Contains(item))
{
_logger.LogInformation("Reached level stop condition (current: {CurrentLevel}, target: {TargetLevel}, mode: {Mode})", playerCharacter.Level, _configuration.Stop.TargetLevel, _configuration.Stop.LevelStopMode);
_chatGui.Print($"Character level {playerCharacter.Level} reached target level {_configuration.Stop.TargetLevel}.", "Questionable", 576);
Stop($"Level stop condition reached [{playerCharacter.Level}]");
return;
}
}
if (_configuration.Stop.QuestSequences.TryGetValue(text, out var value) && value.HasValue)
{
int sequence = _startedQuest.Sequence;
if (sequence >= value.Value && IsRunning)
{
_logger.LogInformation("Reached quest-specific sequence stop condition (quest: {QuestId}, sequence: {CurrentSequence}, target: {TargetSequence})", _startedQuest.Quest.Id, sequence, value.Value);
_chatGui.Print($"Quest '{_startedQuest.Quest.Info.Name}' reached sequence {sequence}, configured stop sequence is {value.Value}.", "Questionable", 576);
Stop($"Quest-specific sequence stop condition reached [{text}@{sequence}]");
return;
Configuration.EStopConditionMode valueOrDefault = _configuration.Stop.QuestStopModes.GetValueOrDefault(text, Configuration.EStopConditionMode.Pause);
string item2 = $"questseq:{text}:{sequence}";
if (valueOrDefault != Configuration.EStopConditionMode.Pause || !_stopConditionsMetAtStart.Contains(item2))
{
_logger.LogInformation("Reached quest-specific sequence stop condition (quest: {QuestId}, sequence: {CurrentSequence}, target: {TargetSequence})", _startedQuest.Quest.Id, sequence, value.Value);
_chatGui.Print($"Quest '{_startedQuest.Quest.Info.Name}' reached sequence {sequence}, configured stop sequence is {value.Value}.", "Questionable", 576);
Stop($"Quest-specific sequence stop condition reached [{text}@{sequence}]");
return;
}
}
}
else if (_configuration.Stop.SequenceToStopAfter && CurrentQuest != null)
else if (_configuration.Stop.SequenceStopMode != Configuration.EStopConditionMode.Off && CurrentQuest != null)
{
int sequence2 = CurrentQuest.Sequence;
if (sequence2 >= _configuration.Stop.TargetSequence && IsRunning)
{
_logger.LogInformation("Reached global quest sequence stop condition (sequence: {CurrentSequence}, target: {TargetSequence})", sequence2, _configuration.Stop.TargetSequence);
_chatGui.Print($"Quest sequence {sequence2} reached target sequence {_configuration.Stop.TargetSequence}.", "Questionable", 576);
Stop($"Sequence stop condition reached [{sequence2}]");
return;
string item3 = $"sequence:{text}:{sequence2}";
if (_configuration.Stop.SequenceStopMode != Configuration.EStopConditionMode.Pause || !_stopConditionsMetAtStart.Contains(item3))
{
_logger.LogInformation("Reached global quest sequence stop condition (sequence: {CurrentSequence}, target: {TargetSequence}, mode: {Mode})", sequence2, _configuration.Stop.TargetSequence, _configuration.Stop.SequenceStopMode);
_chatGui.Print($"Quest sequence {sequence2} reached target sequence {_configuration.Stop.TargetSequence}.", "Questionable", 576);
Stop($"Sequence stop condition reached [{sequence2}]");
return;
}
}
}
}
@ -402,17 +421,14 @@ internal sealed class QuestController : MiniTaskController<QuestController>
if (step == 0 || step == 255)
{
flag2 = true;
goto IL_0691;
goto IL_07e7;
}
}
flag2 = false;
goto IL_0691;
goto IL_07e7;
}
goto IL_0695;
IL_0691:
flag = flag2;
goto IL_0695;
IL_0695:
goto IL_07eb;
IL_07eb:
if (flag && DateTime.Now >= CurrentQuest.StepProgress.StartedAt.AddSeconds(15.0))
{
lock (_progressLock)
@ -427,6 +443,10 @@ internal sealed class QuestController : MiniTaskController<QuestController>
CheckAutoRefreshCondition();
UpdateCurrentTask();
}
return;
IL_07e7:
flag = flag2;
goto IL_07eb;
}
private void CheckAutoRefreshCondition()
@ -563,7 +583,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
int num = ManualPriorityQuests.RemoveAll((Quest q) => _questFunctions.IsQuestComplete(q.Id));
if (num > 0)
{
_logger.LogInformation("Removed {Count} completed priority quest(s)", num);
_logger.LogInformation("Removed {Count} completed priority {QuestWord}", num, (num == 1) ? "quest" : "quests");
}
}
if (_pendingQuest != null)
@ -632,6 +652,19 @@ internal sealed class QuestController : MiniTaskController<QuestController>
}
if (elementId == null || elementId.Value == 0)
{
(bool isLevelLocked, int levelsNeeded, int requiredLevel, string? questName) msqLevelLockInfo = _questFunctions.GetMsqLevelLockInfo();
bool item = msqLevelLockInfo.isLevelLocked;
int item2 = msqLevelLockInfo.levelsNeeded;
int item3 = msqLevelLockInfo.requiredLevel;
string item4 = msqLevelLockInfo.questName;
int currentPlayerLevel = (_objectTable[0] as IPlayerCharacter)?.Level ?? 0;
if (item && _autoDutyIpc.IsConfiguredToRunLevelingMode(currentPlayerLevel) && AutomationType == EAutomationType.Automatic && !_condition[ConditionFlag.BoundByDuty] && _taskQueue.AllTasksComplete)
{
_logger.LogInformation("MSQ '{QuestName}' requires level {RequiredLevel}, current level is {CurrentLevel} ({LevelsNeeded} levels needed). Starting AutoDuty Leveling mode.", item4, item3, item3 - item2, item2);
_taskQueue.Enqueue(new Duty.StartLevelingModeTask(item3, item4));
_taskQueue.Enqueue(new Duty.WaitLevelingModeTask(item3));
return;
}
if (_startedQuest != null)
{
switch (mainScenarioQuestState)
@ -668,16 +701,28 @@ internal sealed class QuestController : MiniTaskController<QuestController>
_startedQuest = new QuestProgress(quest, b);
if (_objectTable[0] is IPlayerCharacter playerCharacter && playerCharacter.Level < quest.Info.Level)
{
_logger.LogInformation("Stopping automation, player level ({PlayerLevel}) < quest level ({QuestLevel}", playerCharacter.Level, quest.Info.Level);
Stop("Quest level too high");
return;
if (_autoDutyIpc.IsConfiguredToRunLevelingMode(playerCharacter.Level) && AutomationType == EAutomationType.Automatic && !_condition[ConditionFlag.BoundByDuty])
{
_logger.LogInformation("Player level ({PlayerLevel}) < quest level ({QuestLevel}), starting AutoDuty Leveling mode", playerCharacter.Level, quest.Info.Level);
ClearTasksInternal();
_taskQueue.Enqueue(new Duty.StartLevelingModeTask(quest.Info.Level, quest.Info.Name));
_taskQueue.Enqueue(new Duty.WaitLevelingModeTask(quest.Info.Level));
}
else
{
_logger.LogInformation("Stopping automation, player level ({PlayerLevel}) < quest level ({QuestLevel})", playerCharacter.Level, quest.Info.Level);
Stop("Quest level too high");
}
}
if (AutomationType == EAutomationType.SingleQuestB)
else
{
_logger.LogInformation("Single quest is finished");
AutomationType = EAutomationType.Manual;
if (AutomationType == EAutomationType.SingleQuestB)
{
_logger.LogInformation("Single quest is finished");
AutomationType = EAutomationType.Manual;
}
CheckNextTasks("Different Quest");
}
CheckNextTasks("Different Quest");
}
else if (_startedQuest != null)
{
@ -853,6 +898,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
_nextQuest = null;
_gatheringQuest = null;
_lastTaskUpdate = DateTime.Now;
_stopConditionsMetAtStart.Clear();
ResetAutoRefreshState();
}
}
@ -967,6 +1013,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
{
if (!CheckAndBlockForStopConditions())
{
RecordStopConditionsMetAtStart();
AutomationType = EAutomationType.Automatic;
ExecuteNextStep();
}
@ -979,6 +1026,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
{
if (!CheckAndBlockForStopConditions())
{
RecordStopConditionsMetAtStart();
AutomationType = EAutomationType.GatheringOnly;
ExecuteNextStep();
}
@ -991,6 +1039,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
{
if (!CheckAndBlockForStopConditions())
{
RecordStopConditionsMetAtStart();
AutomationType = EAutomationType.SingleQuestA;
ExecuteNextStep();
}
@ -1006,13 +1055,53 @@ internal sealed class QuestController : MiniTaskController<QuestController>
}
}
private void RecordStopConditionsMetAtStart()
{
_stopConditionsMetAtStart.Clear();
if (!_configuration.Stop.Enabled)
{
return;
}
if (_configuration.Stop.LevelStopMode == Configuration.EStopConditionMode.Pause && _objectTable[0] is IPlayerCharacter playerCharacter && playerCharacter.Level >= _configuration.Stop.TargetLevel)
{
_stopConditionsMetAtStart.Add($"level:{_configuration.Stop.TargetLevel}");
_logger.LogDebug("Recording level stop condition as already met at start: {Level}", _configuration.Stop.TargetLevel);
}
if (_configuration.Stop.SequenceStopMode == Configuration.EStopConditionMode.Pause && _startedQuest != null)
{
string text = _startedQuest.Quest.Id.ToString();
if (!_configuration.Stop.QuestSequences.ContainsKey(text))
{
int sequence = _startedQuest.Sequence;
if (sequence >= _configuration.Stop.TargetSequence)
{
_stopConditionsMetAtStart.Add($"sequence:{text}:{sequence}");
_logger.LogDebug("Recording global sequence stop condition as already met at start: quest {QuestId}, sequence {Sequence}", text, sequence);
}
}
}
foreach (ElementId item in _configuration.Stop.QuestsToStopAfter)
{
string text2 = item.ToString();
if (_configuration.Stop.QuestStopModes.GetValueOrDefault(text2, Configuration.EStopConditionMode.Pause) == Configuration.EStopConditionMode.Pause && _configuration.Stop.QuestSequences.TryGetValue(text2, out var value) && value.HasValue && _questFunctions.IsQuestAccepted(item))
{
QuestProgressInfo questProgressInfo = _questFunctions.GetQuestProgressInfo(item);
if (questProgressInfo != null && questProgressInfo.Sequence >= value.Value)
{
_stopConditionsMetAtStart.Add($"questseq:{text2}:{questProgressInfo.Sequence}");
_logger.LogDebug("Recording quest sequence stop condition as already met at start: quest {QuestId}, sequence {Sequence}", text2, questProgressInfo.Sequence);
}
}
}
}
private bool CheckAndBlockForStopConditions()
{
if (!_configuration.Stop.Enabled)
{
return false;
}
if (_configuration.Stop.LevelToStopAfter && _objectTable[0] is IPlayerCharacter playerCharacter && playerCharacter.Level >= _configuration.Stop.TargetLevel)
if (_configuration.Stop.LevelStopMode == Configuration.EStopConditionMode.Stop && _objectTable[0] is IPlayerCharacter playerCharacter && playerCharacter.Level >= _configuration.Stop.TargetLevel)
{
_logger.LogInformation("Blocking start: Level stop condition already met (current: {CurrentLevel}, target: {TargetLevel})", playerCharacter.Level, _configuration.Stop.TargetLevel);
_chatGui.Print($"Cannot start: Character level {playerCharacter.Level} has reached target level {_configuration.Stop.TargetLevel}.", "Questionable", 576);
@ -1021,6 +1110,10 @@ internal sealed class QuestController : MiniTaskController<QuestController>
foreach (ElementId item in _configuration.Stop.QuestsToStopAfter)
{
string key = item.ToString();
if (_configuration.Stop.QuestStopModes.GetValueOrDefault(key, Configuration.EStopConditionMode.Pause) != Configuration.EStopConditionMode.Stop)
{
continue;
}
if (_configuration.Stop.QuestSequences.TryGetValue(key, out var value) && value.HasValue)
{
if (!_questFunctions.IsQuestAccepted(item))
@ -1048,7 +1141,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
return true;
}
}
if (_configuration.Stop.SequenceToStopAfter && _startedQuest != null)
if (_configuration.Stop.SequenceStopMode == Configuration.EStopConditionMode.Stop && _startedQuest != null)
{
string key2 = _startedQuest.Quest.Id.ToString();
if (!_configuration.Stop.QuestSequences.ContainsKey(key2))
@ -1075,6 +1168,23 @@ internal sealed class QuestController : MiniTaskController<QuestController>
var (questSequence, step, flag) = GetNextStep();
if (CurrentQuest == null || questSequence == null)
{
_logger.LogDebug("ExecuteNextStep: No current quest or sequence. Checking leveling mode conditions.");
if (AutomationType == EAutomationType.Automatic && !_condition[ConditionFlag.BoundByDuty])
{
(bool isLevelLocked, int levelsNeeded, int requiredLevel, string? questName) msqLevelLockInfo = _questFunctions.GetMsqLevelLockInfo();
bool item = msqLevelLockInfo.isLevelLocked;
int item2 = msqLevelLockInfo.levelsNeeded;
int item3 = msqLevelLockInfo.requiredLevel;
string item4 = msqLevelLockInfo.questName;
int currentPlayerLevel = (_objectTable[0] as IPlayerCharacter)?.Level ?? 0;
if (item && _autoDutyIpc.IsConfiguredToRunLevelingMode(currentPlayerLevel))
{
_logger.LogInformation("MSQ '{QuestName}' requires level {RequiredLevel}, current level is {CurrentLevel} ({LevelsNeeded} levels needed). Starting AutoDuty Leveling mode.", item4, item3, item3 - item2, item2);
_taskQueue.Enqueue(new Duty.StartLevelingModeTask(item3, item4));
_taskQueue.Enqueue(new Duty.WaitLevelingModeTask(item3));
return;
}
}
if (CurrentQuestDetails?.Progress.Quest.Id is SatisfactionSupplyNpcId && CurrentQuestDetails?.Progress.Sequence == 1)
{
(QuestProgress, ECurrentQuestType)? currentQuestDetails = CurrentQuestDetails;
@ -1086,23 +1196,23 @@ internal sealed class QuestController : MiniTaskController<QuestController>
_logger.LogInformation("Completed delivery quest");
SetGatheringQuest(null);
Stop("Gathering quest complete");
goto IL_01dc;
goto IL_02d0;
}
}
}
_logger.LogWarning("Could not retrieve next quest step, not doing anything [{QuestId}, {Sequence}, {Step}]", CurrentQuest?.Quest.Id, CurrentQuest?.Sequence, CurrentQuest?.Step);
goto IL_01dc;
goto IL_02d0;
}
goto IL_01e8;
IL_01e8:
goto IL_02dc;
IL_02dc:
_movementController.Stop();
_combatController.Stop("Execute next step");
_gatheringController.Stop("Execute next step");
try
{
foreach (ITask item in _taskCreator.CreateTasks(CurrentQuest.Quest, CurrentQuest.Sequence, questSequence, step))
foreach (ITask item5 in _taskCreator.CreateTasks(CurrentQuest.Quest, CurrentQuest.Sequence, questSequence, step))
{
_taskQueue.Enqueue(item);
_taskQueue.Enqueue(item5);
}
ResetAutoRefreshState();
return;
@ -1114,12 +1224,12 @@ internal sealed class QuestController : MiniTaskController<QuestController>
Stop("Tasks failed to create");
return;
}
IL_01dc:
IL_02d0:
if (CurrentQuest == null || !flag)
{
return;
}
goto IL_01e8;
goto IL_02dc;
}
public string ToStatString()