Compare commits

...

7 Commits

56 changed files with 360 additions and 48 deletions

View File

@ -1,5 +1,5 @@
<Project>
<PropertyGroup Condition="$(MSBuildProjectName) != 'GatheringPathRenderer'">
<Version>4.19</Version>
<Version>4.20</Version>
</PropertyGroup>
</Project>

View File

@ -21,8 +21,7 @@ internal static class SinglePlayerDutyOptionsExtensions
Assignment(nameof(SinglePlayerDutyOptions.Enabled),
dutyOptions.Enabled, emptyOptions.Enabled)
.AsSyntaxNodeOrToken(),
Assignment(nameof(SinglePlayerDutyOptions.Notes),
dutyOptions.Notes, emptyOptions.Notes)
AssignmentList(nameof(SinglePlayerDutyOptions.Notes), dutyOptions.Notes)
.AsSyntaxNodeOrToken(),
Assignment(nameof(SinglePlayerDutyOptions.Index),
dutyOptions.Index, emptyOptions.Index)

View File

@ -141,7 +141,8 @@
"TerritoryId": 141,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
},
"Fly": true
}

View File

@ -36,7 +36,8 @@
"TerritoryId": 137,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
},
"AetheryteShortcut": "Eastern La Noscea - Wineport",
"Fly": true

View File

@ -117,7 +117,8 @@
"TerritoryId": 152,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
},
"Fly": true
}

View File

@ -119,7 +119,8 @@
"TerritoryId": 152,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -251,7 +251,8 @@
"TerritoryId": 152,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
},
"Fly": true
}

View File

@ -140,7 +140,8 @@
"TerritoryId": 148,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -114,6 +114,7 @@
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true,
"TestedBossModVersion": 292,
"Notes": [
"Healer NPC is only killed after the boss dies; all NPCs need to be killed for the duty to complete"
]

View File

@ -31,6 +31,7 @@
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": false,
"TestedBossModVersion": 292,
"Notes": [
"AI doesn't automatically target newly spawning adds until after the boss died, and dies (tested on CNJ)"
]

View File

@ -79,6 +79,7 @@
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true,
"TestedBossModVersion": 292,
"Notes": [
"(Phase 1) Healer NPCs are only killed after the boss dies - allied NPCs will kill them eventually; all NPCs need to be killed for the duty to complete"
]

View File

@ -71,6 +71,7 @@
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": false,
"TestedBossModVersion": 292,
"Notes": [
"(Phase 1, second enemy group) Stuck with enemy being out of sight -- but still able to attack you (tested on ACN)"
]

View File

@ -75,7 +75,8 @@
"TerritoryId": 134,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -31,6 +31,7 @@
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": false,
"TestedBossModVersion": 292,
"Notes": [
"AI doesn't automatically target newly spawning adds until after the boss died (requires healing luck on ACN)"
]

View File

@ -60,6 +60,7 @@
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true,
"TestedBossModVersion": 292,
"Notes": [
"(Phase 1) Kills PGL NPCs and then the boss - allied NPCs will kill most other NPCs eventually; all NPCs need to be killed for the duty to complete"
]

View File

@ -46,7 +46,8 @@
"TerritoryId": 145,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -80,7 +80,8 @@
"TerritoryId": 130,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
},
"AetheryteShortcut": "Ul'dah",
"AethernetShortcut": [

View File

@ -90,7 +90,8 @@
"TerritoryId": 152,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
},
"AetheryteShortcut": "East Shroud - Hawthorne Hut",
"SkipConditions": {

View File

@ -65,7 +65,8 @@
"TerritoryId": 135,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
},
"AethernetShortcut": [
"[Limsa Lominsa] The Aftcastle",

View File

@ -60,7 +60,8 @@
"TerritoryId": 140,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
},
"AetheryteShortcut": "Western Thanalan - Horizon"
}

View File

@ -160,7 +160,8 @@
"TerritoryId": 141,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -39,7 +39,8 @@
"TerritoryId": 140,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -30,7 +30,8 @@
"TerritoryId": 141,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -67,6 +67,7 @@
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true,
"TestedBossModVersion": 292,
"Notes": [
"(Phase 1) Healer NPCs are only killed after the boss dies - allied NPCs will kill them eventually; all NPCs need to be killed for the duty to complete"
]

View File

@ -66,7 +66,8 @@
"TerritoryId": 152,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -86,6 +86,7 @@
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true,
"TestedBossModVersion": 292,
"Notes": [
"AI will kill initial adds before the boss, but not switch target whenever new enemies spawn; all NPCs need to be killed for the duty to complete"
]

View File

@ -159,7 +159,11 @@
"Z": -805.478
},
"TerritoryId": 140,
"InteractionType": "SinglePlayerDuty"
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true,
"TestedBossModVersion": 292
}
}
]
},

View File

@ -105,7 +105,8 @@
"TerritoryId": 1053,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -68,6 +68,15 @@
{
"Sequence": 3,
"Steps": [
{
"Position": {
"X": -561.9863,
"Y": 9.919454,
"Z": 66.29564
},
"TerritoryId": 152,
"InteractionType": "WalkTo"
},
{
"DataId": 1008276,
"Position": {

View File

@ -78,6 +78,10 @@
"StopDistance": 1,
"TerritoryId": 156,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true,
"TestedBossModVersion": 292
},
"Fly": true
}
]

View File

@ -100,6 +100,28 @@
2
]
},
{
"Position": {
"X": 86.662384,
"Y": 28.34813,
"Z": -627.5218
},
"TerritoryId": 156,
"InteractionType": "WalkTo",
"Fly": true,
"SkipConditions": {
"StepIf": {
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
32
]
}
}
},
{
"DataId": 1009143,
"Position": {
@ -109,7 +131,6 @@
},
"TerritoryId": 156,
"InteractionType": "Interact",
"Fly": true,
"$": "1 112 0 0 0 2 -> 2 96 0 0 0 34",
"CompletionQuestVariablesFlags": [
null,

View File

@ -71,6 +71,14 @@
},
"TerritoryId": 147,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true,
"TestedBossModVersion": 292,
"Notes": [
"Will target Eline first (other NPCs later), and move to some -other- group of NPCs; only re-targets once they're at 1 HP (for Eline) or die",
"If the target isn't in melee range but other NPCs are, whether any AOEs are used for nearby enemies seems random"
]
},
"Fly": true,
"AetheryteShortcut": "Northern Thanalan - Ceruleum Processing Plant"
}

View File

@ -28,7 +28,16 @@
"Z": -328.66406
},
"TerritoryId": 155,
"InteractionType": "SinglePlayerDuty"
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": false,
"TestedBossModVersion": 292,
"Notes": [
"WIP: Needs to be re-tested",
"AI doesn't move after starting the instance, so enemies won't be triggered",
"(First Barrier) If the player is too far south, after being stunned by Vishap's roar, AI doesn't move out of the AOE and dies to the Cauterize"
]
}
}
]
},

View File

@ -46,6 +46,10 @@
},
"TerritoryId": 155,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true,
"TestedBossModVersion": 292
},
"Fly": true
}
]

View File

@ -97,7 +97,8 @@
"InteractionType": "SinglePlayerDuty",
"Fly": true,
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -30,7 +30,11 @@
},
"TerritoryId": 397,
"InteractionType": "SinglePlayerDuty",
"Comment": "Walk straight to Gorgagne Mills basement, ignore footprints"
"Comment": "Walk straight to Gorgagne Mills basement, ignore footprints",
"SinglePlayerDutyOptions": {
"Enabled": true,
"TestedBossModVersion": 292
}
}
]
},

View File

@ -60,7 +60,8 @@
"TerritoryId": 401,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -80,7 +80,8 @@
"[Ishgard] The Tribunal"
],
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -30,7 +30,11 @@
"TerritoryId": 145,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292,
"Notes": [
"Will not move into melee range to kill the gate; Alphinaud will kill it after a while"
]
}
}
]

View File

@ -80,7 +80,8 @@
"InteractionType": "SinglePlayerDuty",
"DisableNavmesh": true,
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -76,7 +76,8 @@
"TerritoryId": 418,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -58,7 +58,8 @@
"Emote": "lookout",
"StopDistance": 0.25,
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -49,7 +49,8 @@
"[Idyllshire] Epilogue Gate (Eastern Hinterlands)"
],
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -70,7 +70,8 @@
"TerritoryId": 402,
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": true
"Enabled": true,
"TestedBossModVersion": 292
}
}
]

View File

@ -87,6 +87,7 @@
"Comment": "Estinien vs. Arch Ultima",
"SinglePlayerDutyOptions": {
"Enabled": false,
"TestedBossModVersion": 292,
"Notes": [
"AI doesn't move automatically for the first boss",
"AI doesn't move automatically for the dialogue with gaius on the bridge",

View File

@ -48,6 +48,7 @@
"InteractionType": "SinglePlayerDuty",
"SinglePlayerDutyOptions": {
"Enabled": false,
"TestedBossModVersion": 292,
"Notes": [
"Doesn't walk to the teleporter to finish the duty"
]

View File

@ -1285,6 +1285,10 @@
"maximum": 1,
"description": "If a quest has multiple solo instances (which affects 5 quests total), indicates which one this is"
},
"TestedBossModVersion": {
"type": "number",
"minimum": 292
},
"$": {
"type": "string"
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Dalamud.Configuration;
using Dalamud.Game.Text;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
@ -45,7 +46,10 @@ internal sealed class Configuration : IPluginConfiguration
internal sealed class SinglePlayerDutyConfiguration
{
public bool RunSoloInstancesWithBossMod { get; set; }
public byte RetryDifficulty { get; set; } = 2;
[SuppressMessage("Performance", "CA1822", Justification = "Will be fixed when no longer WIP")]
public byte RetryDifficulty => 0;
public HashSet<uint> WhitelistedSinglePlayerDutyCfcIds { get; set; } = [];
public HashSet<uint> BlacklistedSinglePlayerDutyCfcIds { get; set; } = [];
}

View File

@ -1,4 +1,6 @@
using Microsoft.Extensions.Logging;
using System.Linq;
using Microsoft.Extensions.Logging;
using Questionable.Data;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
@ -21,7 +23,7 @@ internal static class NextQuest
return null;
// probably irrelevant, since pick up is handled elsewhere (and, in particular, checks for aetherytes and stuff)
if (questFunctions.GetPriorityQuests().Contains(step.NextQuestId))
if (questFunctions.GetPriorityQuests(onlyClassAndRoleQuests: true).Contains(step.NextQuestId))
return null;
return new SetQuestTask(step.NextQuestId, quest.Id);

View File

@ -96,6 +96,12 @@ internal static class Interact
private EInteractionState _interactionState = EInteractionState.None;
private DateTime _continueAt = DateTime.MinValue;
/// <summary>
/// A slight delay when we think an interaction has ended, to make sure that we're processing "Action cancelled"
/// prior to the next step (in case we're attacked).
/// </summary>
private bool delayedFinalCheck;
public Quest? Quest => Task.Quest;
public EInteractionType InteractionType { get; set; }
@ -179,7 +185,14 @@ internal static class Interact
return ETaskResult.StillRunning;
else if (ProgressContext.WasSuccessful() ||
_interactionState == EInteractionState.InteractionConfirmed)
return ETaskResult.TaskComplete;
{
if (delayedFinalCheck)
return ETaskResult.TaskComplete;
_continueAt = DateTime.Now.AddSeconds(0.2);
delayedFinalCheck = true;
return ETaskResult.StillRunning;
}
}
IGameObject? gameObject = gameFunctions.FindObjectByDataId(Task.DataId);

View File

@ -0,0 +1,169 @@
using System;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Group;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
namespace Questionable.Controller.Utils;
internal sealed class PartyWatchDog : IDisposable
{
private readonly QuestController _questController;
private readonly IClientState _clientState;
private readonly IChatGui _chatGui;
private readonly ILogger<PartyWatchDog> _logger;
private ushort? _uncheckedTeritoryId;
public PartyWatchDog(QuestController questController, IClientState clientState, IChatGui chatGui,
ILogger<PartyWatchDog> logger)
{
_questController = questController;
_clientState = clientState;
_chatGui = chatGui;
_logger = logger;
_clientState.TerritoryChanged += TerritoryChanged;
}
private unsafe void TerritoryChanged(ushort newTerritoryId)
{
var intendedUse = (ETerritoryIntendedUseEnum)GameMain.Instance()->CurrentTerritoryIntendedUseId;
switch (intendedUse)
{
case ETerritoryIntendedUseEnum.Gaol:
case ETerritoryIntendedUseEnum.Frontline:
case ETerritoryIntendedUseEnum.LordOfVerminion:
case ETerritoryIntendedUseEnum.Diadem:
case ETerritoryIntendedUseEnum.CrystallineConflict:
case ETerritoryIntendedUseEnum.Battlehall:
case ETerritoryIntendedUseEnum.CrystallineConflict2:
case ETerritoryIntendedUseEnum.DeepDungeon:
case ETerritoryIntendedUseEnum.TreasureMapDuty:
case ETerritoryIntendedUseEnum.Diadem2:
case ETerritoryIntendedUseEnum.RivalWings:
case ETerritoryIntendedUseEnum.Eureka:
case ETerritoryIntendedUseEnum.LeapOfFaith:
case ETerritoryIntendedUseEnum.OceanFishing:
case ETerritoryIntendedUseEnum.Diadem3:
case ETerritoryIntendedUseEnum.Bozja:
case ETerritoryIntendedUseEnum.Battlehall2:
case ETerritoryIntendedUseEnum.Battlehall3:
case ETerritoryIntendedUseEnum.LargeScaleRaid:
case ETerritoryIntendedUseEnum.LargeScaleSavageRaid:
case ETerritoryIntendedUseEnum.Blunderville:
StopIfRunning($"Unsupported Area entered ({newTerritoryId})");
break;
case ETerritoryIntendedUseEnum.Dungeon:
case ETerritoryIntendedUseEnum.VariantDungeon:
case ETerritoryIntendedUseEnum.AllianceRaid:
case ETerritoryIntendedUseEnum.Trial:
case ETerritoryIntendedUseEnum.Raid:
case ETerritoryIntendedUseEnum.Raid2:
case ETerritoryIntendedUseEnum.SeasonalEvent:
case ETerritoryIntendedUseEnum.SeasonalEvent2:
case ETerritoryIntendedUseEnum.CriterionDuty:
case ETerritoryIntendedUseEnum.CriterionSavageDuty:
_uncheckedTeritoryId = newTerritoryId;
_logger.LogInformation("Will check territory {TerritoryId} after loading", newTerritoryId);
break;
}
}
public unsafe void Update()
{
if (_uncheckedTeritoryId == _clientState.TerritoryType && GameMain.Instance()->TerritoryLoadState == 2)
{
var groupManager = GroupManager.Instance();
if (groupManager == null)
return;
byte memberCount = groupManager->MainGroup.MemberCount;
bool isInAlliance = groupManager->MainGroup.IsAlliance;
_logger.LogDebug("Territory {TerritoryId} with {MemberCount} members, alliance: {IsInAlliance}",
_uncheckedTeritoryId, memberCount, isInAlliance);
if (memberCount > 1 || isInAlliance)
StopIfRunning("Other party members present");
_uncheckedTeritoryId = null;
}
}
private void StopIfRunning(string reason)
{
if (_questController.IsRunning || _questController.AutomationType != QuestController.EAutomationType.Manual)
{
_chatGui.PrintError(
$"Stopping Questionable: {reason}. If you believe this to be correct, please restart Questionable manually.",
CommandHandler.MessageTag, CommandHandler.TagColor);
_questController.Stop(reason);
}
}
public void Dispose()
{
_clientState.TerritoryChanged -= TerritoryChanged;
}
// from https://github.com/NightmareXIV/ECommons/blob/f69e460e95134c72592654059843b138b4c01a9e/ECommons/ExcelServices/TerritoryIntendedUseEnum.cs#L5
[UsedImplicitly(ImplicitUseTargetFlags.Members, Reason = "game data")]
private enum ETerritoryIntendedUseEnum : byte
{
CityArea = 0,
OpenWorld = 1,
Inn = 2,
Dungeon = 3,
VariantDungeon = 4,
Gaol = 5,
StartingArea = 6,
QuestArea = 7,
AllianceRaid = 8,
QuestBattle = 9,
Trial = 10,
QuestArea2 = 12,
ResidentialArea = 13,
HousingInstances = 14,
QuestArea3 = 15,
Raid = 16,
Raid2 = 17,
Frontline = 18,
ChocoboSquare = 20,
RestorationEvent = 21,
Sanctum = 22,
GoldSaucer = 23,
LordOfVerminion = 25,
Diadem = 26,
HallOfTheNovice = 27,
CrystallineConflict = 28,
QuestBattle2 = 29,
Barracks = 30,
DeepDungeon = 31,
SeasonalEvent = 32,
TreasureMapDuty = 33,
SeasonalEventDuty = 34,
Battlehall = 35,
CrystallineConflict2 = 37,
Diadem2 = 38,
RivalWings = 39,
Unknown1 = 40,
Eureka = 41,
SeasonalEvent2 = 43,
LeapOfFaith = 44,
MaskedCarnivale = 45,
OceanFishing = 46,
Diadem3 = 47,
Bozja = 48,
IslandSanctuary = 49,
Battlehall2 = 50,
Battlehall3 = 51,
LargeScaleRaid = 52,
LargeScaleSavageRaid = 53,
QuestArea4 = 54,
TribalInstance = 56,
CriterionDuty = 57,
CriterionSavageDuty = 58,
Blunderville = 59,
}
}

View File

@ -7,6 +7,7 @@ using Dalamud.Plugin.Services;
using Microsoft.Extensions.Logging;
using Questionable.Controller;
using Questionable.Controller.GameUi;
using Questionable.Controller.Utils;
using Questionable.Windows;
namespace Questionable;
@ -23,6 +24,7 @@ internal sealed class DalamudInitializer : IDisposable
private readonly ConfigWindow _configWindow;
private readonly IToastGui _toastGui;
private readonly Configuration _configuration;
private readonly PartyWatchDog _partyWatchDog;
private readonly ILogger<DalamudInitializer> _logger;
public DalamudInitializer(
@ -42,6 +44,7 @@ internal sealed class DalamudInitializer : IDisposable
PriorityWindow priorityWindow,
IToastGui toastGui,
Configuration configuration,
PartyWatchDog partyWatchDog,
ILogger<DalamudInitializer> logger)
{
_pluginInterface = pluginInterface;
@ -54,6 +57,7 @@ internal sealed class DalamudInitializer : IDisposable
_configWindow = configWindow;
_toastGui = toastGui;
_configuration = configuration;
_partyWatchDog = partyWatchDog;
_logger = logger;
_windowSystem.AddWindow(oneTimeSetupWindow);
@ -77,6 +81,7 @@ internal sealed class DalamudInitializer : IDisposable
private void FrameworkUpdate(IFramework framework)
{
_partyWatchDog.Update();
_questController.Update();
try

View File

@ -401,14 +401,15 @@ internal sealed unsafe class QuestFunctions
return 1000 * quest.AllSteps().Count(x => x.Step.AetheryteShortcut != null);
}
public List<ElementId> GetPriorityQuests()
public List<ElementId> GetPriorityQuests(bool onlyClassAndRoleQuests = false)
{
List<ElementId> priorityQuests =
[
new QuestId(1157), // Garuda (Hard)
new QuestId(1158), // Titan (Hard)
..QuestData.CrystalTowerQuests
];
List<ElementId> priorityQuests = [];
if (!onlyClassAndRoleQuests)
{
priorityQuests.Add(new QuestId(1157)); // Garuda (Hard)
priorityQuests.Add(new QuestId(1158)); // Titan (Hard)
priorityQuests.AddRange(QuestData.CrystalTowerQuests);
}
EClassJob classJob = (EClassJob?)_clientState.LocalPlayer?.ClassJob.RowId ?? EClassJob.Adventurer;
uint[] shadowbringersRoleQuestChapters = QuestData.AllRoleQuestChapters.Select(x => x[0]).ToArray();

View File

@ -20,6 +20,7 @@ using Questionable.Controller.Steps.Common;
using Questionable.Controller.Steps.Gathering;
using Questionable.Controller.Steps.Interactions;
using Questionable.Controller.Steps.Leves;
using Questionable.Controller.Utils;
using Questionable.Data;
using Questionable.External;
using Questionable.Functions;
@ -260,6 +261,8 @@ public sealed class QuestionablePlugin : IDalamudPlugin
serviceCollection.AddSingleton<ShopController>();
serviceCollection.AddSingleton<InterruptHandler>();
serviceCollection.AddSingleton<PartyWatchDog>();
serviceCollection.AddSingleton<CraftworksSupplyController>();
serviceCollection.AddSingleton<CreditsController>();
serviceCollection.AddSingleton<HelpUiController>();

View File

@ -9,7 +9,7 @@ internal sealed class UniqueStartStopValidator : IQuestValidator
{
public IEnumerable<ValidationIssue> Validate(Quest quest)
{
if (quest.Id is SatisfactionSupplyNpcId)
if (quest.Id is SatisfactionSupplyNpcId or AlliedSocietyDailyId)
yield break;
var questAccepts =

View File

@ -34,7 +34,9 @@ internal sealed class SinglePlayerDutyConfigComponent : ConfigComponent
(EClassJob.BlackMage, "Magical Ranged Role Quests"),
];
#if false
private readonly string[] _retryDifficulties = ["Normal", "Easy", "Very Easy"];
#endif
private readonly TerritoryData _territoryData;
private readonly QuestRegistry _questRegistry;
@ -263,12 +265,19 @@ internal sealed class SinglePlayerDutyConfigComponent : ConfigComponent
using (ImRaii.PushIndent(ImGui.GetFrameHeight() + ImGui.GetStyle().ItemInnerSpacing.X))
{
ImGui.AlignTextToFramePadding();
ImGui.TextColored(ImGuiColors.DalamudRed,
"Work in Progress: For now, this will always use BossMod for combat.");
using (_ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed))
{
ImGui.TextUnformatted("Work in Progress:");
ImGui.BulletText("Will always use BossMod for combat (ignoring the configured combat module).");
ImGui.BulletText("Only a small subset of quest battles have been tested - most of which are in the MSQ.");
ImGui.BulletText("When retrying a failed battle, it will always start at 'Normal' difficulty.");
ImGui.BulletText("Please don't enable this option when using a BossMod fork (such as Reborn);\nwith the missing combat module configuration, it is unlikely to be compatible.");
}
#if false
using (ImRaii.Disabled(!runSoloInstancesWithBossMod))
{
ImGui.Spacing();
int retryDifficulty = Configuration.SinglePlayerDuties.RetryDifficulty;
if (ImGui.Combo("Difficulty when retrying a quest battle", ref retryDifficulty, _retryDifficulties,
_retryDifficulties.Length))
@ -277,6 +286,7 @@ internal sealed class SinglePlayerDutyConfigComponent : ConfigComponent
Save();
}
}
#endif
}
ImGui.Separator();