forked from liza/Questionable
Add AutoDuty integration
This commit is contained in:
parent
f20b5e08a7
commit
8d85a0f896
@ -117,6 +117,9 @@ internal static class QuestStepExtensions
|
||||
Assignment(nameof(QuestStep.ContentFinderConditionId),
|
||||
step.ContentFinderConditionId, emptyStep.ContentFinderConditionId)
|
||||
.AsSyntaxNodeOrToken(),
|
||||
Assignment(nameof(QuestStep.AutoDutyEnabled),
|
||||
step.AutoDutyEnabled, emptyStep.AutoDutyEnabled)
|
||||
.AsSyntaxNodeOrToken(),
|
||||
Assignment(nameof(QuestStep.SkipConditions), step.SkipConditions,
|
||||
emptyStep.SkipConditions)
|
||||
.AsSyntaxNodeOrToken(),
|
||||
|
@ -112,7 +112,8 @@
|
||||
{
|
||||
"TerritoryId": 138,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 4
|
||||
"ContentFinderConditionId": 4,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -71,7 +71,8 @@
|
||||
{
|
||||
"TerritoryId": 146,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 56
|
||||
"ContentFinderConditionId": 56,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -62,7 +62,8 @@
|
||||
{
|
||||
"TerritoryId": 140,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 3
|
||||
"ContentFinderConditionId": 3,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -57,7 +57,8 @@
|
||||
{
|
||||
"TerritoryId": 148,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 2
|
||||
"ContentFinderConditionId": 2,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -44,7 +44,8 @@
|
||||
{
|
||||
"TerritoryId": 153,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 1
|
||||
"ContentFinderConditionId": 1,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -66,7 +66,8 @@
|
||||
{
|
||||
"TerritoryId": 148,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 6
|
||||
"ContentFinderConditionId": 6,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -85,7 +85,8 @@
|
||||
{
|
||||
"TerritoryId": 137,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 8
|
||||
"ContentFinderConditionId": 8,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -45,7 +45,8 @@
|
||||
{
|
||||
"TerritoryId": 139,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 57
|
||||
"ContentFinderConditionId": 57,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -59,7 +59,8 @@
|
||||
{
|
||||
"TerritoryId": 155,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 11
|
||||
"ContentFinderConditionId": 11,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -38,7 +38,8 @@
|
||||
{
|
||||
"TerritoryId": 331,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 58
|
||||
"ContentFinderConditionId": 58,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -45,7 +45,8 @@
|
||||
{
|
||||
"TerritoryId": 147,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 15
|
||||
"ContentFinderConditionId": 15,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -46,7 +46,8 @@
|
||||
{
|
||||
"TerritoryId": 147,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 16
|
||||
"ContentFinderConditionId": 16,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -71,7 +72,8 @@
|
||||
{
|
||||
"TerritoryId": 1053,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 830
|
||||
"ContentFinderConditionId": 830,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -88,7 +88,8 @@
|
||||
{
|
||||
"TerritoryId": 155,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 27
|
||||
"ContentFinderConditionId": 27,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -107,7 +107,8 @@
|
||||
{
|
||||
"TerritoryId": 156,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 32
|
||||
"ContentFinderConditionId": 32,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -71,7 +71,8 @@
|
||||
{
|
||||
"TerritoryId": 155,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 5
|
||||
"ContentFinderConditionId": 5,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -71,7 +71,8 @@
|
||||
{
|
||||
"TerritoryId": 155,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 5
|
||||
"ContentFinderConditionId": 5,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -71,7 +71,8 @@
|
||||
{
|
||||
"TerritoryId": 155,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 5
|
||||
"ContentFinderConditionId": 5,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -38,7 +38,8 @@
|
||||
{
|
||||
"TerritoryId": 397,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 36
|
||||
"ContentFinderConditionId": 36,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -78,7 +78,8 @@
|
||||
{
|
||||
"TerritoryId": 398,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 37
|
||||
"ContentFinderConditionId": 37,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -42,7 +42,8 @@
|
||||
{
|
||||
"TerritoryId": 418,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 39
|
||||
"ContentFinderConditionId": 39,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -59,7 +59,8 @@
|
||||
{
|
||||
"TerritoryId": 419,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 34
|
||||
"ContentFinderConditionId": 34,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -110,7 +110,8 @@
|
||||
{
|
||||
"TerritoryId": 399,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 31
|
||||
"ContentFinderConditionId": 31,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -62,7 +62,8 @@
|
||||
{
|
||||
"TerritoryId": 402,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 38
|
||||
"ContentFinderConditionId": 38,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -77,7 +77,8 @@
|
||||
{
|
||||
"TerritoryId": 463,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 141
|
||||
"ContentFinderConditionId": 141,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -57,7 +57,8 @@
|
||||
{
|
||||
"TerritoryId": 155,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 182
|
||||
"ContentFinderConditionId": 182,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -109,7 +109,8 @@
|
||||
{
|
||||
"TerritoryId": 152,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 219
|
||||
"ContentFinderConditionId": 219,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -87,7 +87,8 @@
|
||||
{
|
||||
"TerritoryId": 680,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 238
|
||||
"ContentFinderConditionId": 238,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -114,7 +114,8 @@
|
||||
{
|
||||
"TerritoryId": 614,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 241
|
||||
"ContentFinderConditionId": 241,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -114,7 +114,8 @@
|
||||
{
|
||||
"TerritoryId": 620,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 242
|
||||
"ContentFinderConditionId": 242,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -98,7 +98,8 @@
|
||||
{
|
||||
"TerritoryId": 621,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 279
|
||||
"ContentFinderConditionId": 279,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -40,7 +40,8 @@
|
||||
{
|
||||
"TerritoryId": 614,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 585
|
||||
"ContentFinderConditionId": 585,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -27,7 +27,8 @@
|
||||
{
|
||||
"TerritoryId": 829,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 611
|
||||
"ContentFinderConditionId": 611,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -120,7 +120,8 @@
|
||||
{
|
||||
"TerritoryId": 813,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 676
|
||||
"ContentFinderConditionId": 676,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -49,7 +49,8 @@
|
||||
{
|
||||
"TerritoryId": 816,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 649
|
||||
"ContentFinderConditionId": 649,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -61,7 +61,8 @@
|
||||
{
|
||||
"TerritoryId": 817,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 651
|
||||
"ContentFinderConditionId": 651,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -62,7 +62,8 @@
|
||||
{
|
||||
"TerritoryId": 814,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 659
|
||||
"ContentFinderConditionId": 659,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -40,7 +40,8 @@
|
||||
{
|
||||
"TerritoryId": 814,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 714
|
||||
"ContentFinderConditionId": 714,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -55,7 +55,8 @@
|
||||
{
|
||||
"TerritoryId": 957,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 783
|
||||
"ContentFinderConditionId": 783,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -71,7 +71,8 @@
|
||||
{
|
||||
"TerritoryId": 957,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 789
|
||||
"ContentFinderConditionId": 789,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -39,7 +39,8 @@
|
||||
{
|
||||
"TerritoryId": 961,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 787
|
||||
"ContentFinderConditionId": 787,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -38,7 +38,8 @@
|
||||
{
|
||||
"TerritoryId": 956,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 786
|
||||
"ContentFinderConditionId": 786,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -63,7 +64,8 @@
|
||||
{
|
||||
"TerritoryId": 1030,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 790
|
||||
"ContentFinderConditionId": 790,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -23,7 +23,8 @@
|
||||
{
|
||||
"TerritoryId": 957,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 844
|
||||
"ContentFinderConditionId": 844,
|
||||
"AutoDutyEnabled": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -71,7 +71,8 @@
|
||||
{
|
||||
"TerritoryId": 1056,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 869
|
||||
"ContentFinderConditionId": 869,
|
||||
"AutoDutyEnabled": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -57,7 +57,8 @@
|
||||
"TerritoryId": 958,
|
||||
"InteractionType": "Duty",
|
||||
"Comment": "Lapis Manalis",
|
||||
"ContentFinderConditionId": 896
|
||||
"ContentFinderConditionId": 896,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -160,7 +160,8 @@
|
||||
{
|
||||
"TerritoryId": 962,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 822
|
||||
"ContentFinderConditionId": 822,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -24,7 +24,8 @@
|
||||
{
|
||||
"TerritoryId": 1162,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 823
|
||||
"ContentFinderConditionId": 823,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -58,7 +58,8 @@
|
||||
{
|
||||
"TerritoryId": 1185,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 826
|
||||
"ContentFinderConditionId": 826,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -23,7 +23,8 @@
|
||||
{
|
||||
"TerritoryId": 1187,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 824
|
||||
"ContentFinderConditionId": 824,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -115,7 +115,8 @@
|
||||
{
|
||||
"TerritoryId": 1189,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 829
|
||||
"ContentFinderConditionId": 829,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -60,7 +60,8 @@
|
||||
{
|
||||
"TerritoryId": 1219,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 831
|
||||
"ContentFinderConditionId": 831,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -39,7 +39,8 @@
|
||||
{
|
||||
"TerritoryId": 1191,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 825
|
||||
"ContentFinderConditionId": 825,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -56,7 +56,8 @@
|
||||
{
|
||||
"TerritoryId": 1192,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 827
|
||||
"ContentFinderConditionId": 827,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -122,7 +122,8 @@
|
||||
{
|
||||
"TerritoryId": 1191,
|
||||
"InteractionType": "Duty",
|
||||
"ContentFinderConditionId": 1008
|
||||
"ContentFinderConditionId": 1008,
|
||||
"AutoDutyEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1217,6 +1217,9 @@
|
||||
"exclusiveMinimum": 0,
|
||||
"exclusiveMaximum": 3000
|
||||
},
|
||||
"AutoDutyEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"DataId": {
|
||||
"type": "null"
|
||||
},
|
||||
|
@ -74,6 +74,7 @@ public sealed class QuestStep
|
||||
|
||||
public JumpDestination? JumpDestination { get; set; }
|
||||
public uint? ContentFinderConditionId { get; set; }
|
||||
public bool AutoDutyEnabled { get; set; }
|
||||
public SkipConditions? SkipConditions { get; set; }
|
||||
|
||||
public List<List<QuestWorkValue>?> RequiredQuestVariables { get; set; } = new();
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Dalamud.Configuration;
|
||||
using System.Collections.Generic;
|
||||
using Dalamud.Configuration;
|
||||
using Dalamud.Game.Text;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using LLib.ImGui;
|
||||
@ -12,6 +13,7 @@ internal sealed class Configuration : IPluginConfiguration
|
||||
public int Version { get; set; } = 1;
|
||||
public int PluginSetupCompleteVersion { get; set; }
|
||||
public GeneralConfiguration General { get; } = new();
|
||||
public DutyConfiguration Duties { get; } = new();
|
||||
public NotificationConfiguration Notifications { get; } = new();
|
||||
public AdvancedConfiguration Advanced { get; } = new();
|
||||
public WindowConfig DebugWindowConfig { get; } = new();
|
||||
@ -32,6 +34,13 @@ internal sealed class Configuration : IPluginConfiguration
|
||||
public bool ConfigureTextAdvance { get; set; } = true;
|
||||
}
|
||||
|
||||
internal sealed class DutyConfiguration
|
||||
{
|
||||
public bool RunInstancedContentWithAutoDuty { get; set; }
|
||||
public HashSet<uint> WhitelistedDutyCfcIds { get; set; } = [];
|
||||
public HashSet<uint> BlacklistedDutyCfcIds { get; set; } = [];
|
||||
}
|
||||
|
||||
internal sealed class NotificationConfiguration
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
@ -29,7 +29,8 @@ internal sealed class QuestRegistry
|
||||
private readonly LeveData _leveData;
|
||||
|
||||
private readonly ICallGateProvider<object> _reloadDataIpc;
|
||||
private readonly Dictionary<ElementId, Quest> _quests = new();
|
||||
private readonly Dictionary<ElementId, Quest> _quests = [];
|
||||
private readonly Dictionary<uint, (ElementId QuestId, QuestStep Step)> _contentFinderConditionIds = [];
|
||||
|
||||
public QuestRegistry(IDalamudPluginInterface pluginInterface, QuestData questData,
|
||||
QuestValidator questValidator, JsonSchemaValidator jsonSchemaValidator,
|
||||
@ -55,6 +56,7 @@ internal sealed class QuestRegistry
|
||||
{
|
||||
_questValidator.Reset();
|
||||
_quests.Clear();
|
||||
_contentFinderConditionIds.Clear();
|
||||
|
||||
LoadQuestsFromAssembly();
|
||||
LoadQuestsFromProjectDirectory();
|
||||
@ -70,6 +72,7 @@ internal sealed class QuestRegistry
|
||||
"Failed to load all quests from user directory (some may have been successfully loaded)");
|
||||
}
|
||||
|
||||
LoadCfcIds();
|
||||
ValidateQuests();
|
||||
Reloaded?.Invoke(this, EventArgs.Empty);
|
||||
try
|
||||
@ -142,6 +145,18 @@ internal sealed class QuestRegistry
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadCfcIds()
|
||||
{
|
||||
foreach (var quest in _quests.Values)
|
||||
{
|
||||
foreach (var dutyStep in quest.AllSteps().Where(x =>
|
||||
x.Step.InteractionType == EInteractionType.Duty && x.Step.ContentFinderConditionId != null))
|
||||
{
|
||||
_contentFinderConditionIds[dutyStep.Step.ContentFinderConditionId!.Value] = (quest.Id, dutyStep.Step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateQuests()
|
||||
{
|
||||
_questValidator.Validate(_quests.Values.Where(x => x.Source != Quest.ESource.Assembly).ToList());
|
||||
@ -223,4 +238,16 @@ internal sealed class QuestRegistry
|
||||
.Where(x => IsKnownQuest(x.QuestId))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public bool TryGetDutyByContentFinderConditionId(uint cfcId, out bool autoDutyEnabledByDefault)
|
||||
{
|
||||
if (_contentFinderConditionIds.TryGetValue(cfcId, out var value))
|
||||
{
|
||||
autoDutyEnabledByDefault = value.Step.AutoDutyEnabled;
|
||||
return true;
|
||||
}
|
||||
|
||||
autoDutyEnabledByDefault = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ internal static class SendNotification
|
||||
{
|
||||
internal sealed class Factory(
|
||||
AutomatonIpc automatonIpc,
|
||||
AutoDutyIpc autoDutyIpc,
|
||||
TerritoryData territoryData) : SimpleTaskFactory
|
||||
{
|
||||
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
|
||||
@ -21,7 +22,7 @@ internal static class SendNotification
|
||||
{
|
||||
EInteractionType.Snipe when !automatonIpc.IsAutoSnipeEnabled =>
|
||||
new Task(step.InteractionType, step.Comment),
|
||||
EInteractionType.Duty =>
|
||||
EInteractionType.Duty when !autoDutyIpc.IsConfiguredToRunContent(step.ContentFinderConditionId, step.AutoDutyEnabled) =>
|
||||
new Task(step.InteractionType, step.ContentFinderConditionId.HasValue
|
||||
? territoryData.GetContentFinderConditionName(step.ContentFinderConditionId.Value)
|
||||
: step.Comment),
|
||||
|
@ -1,6 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Questionable.Controller.Steps.Shared;
|
||||
using Questionable.Data;
|
||||
using Questionable.External;
|
||||
using Questionable.Functions;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
@ -9,26 +13,86 @@ namespace Questionable.Controller.Steps.Interactions;
|
||||
|
||||
internal static class Duty
|
||||
{
|
||||
internal sealed class Factory : SimpleTaskFactory
|
||||
internal sealed class Factory(AutoDutyIpc autoDutyIpc) : ITaskFactory
|
||||
{
|
||||
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
|
||||
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
|
||||
{
|
||||
if (step.InteractionType != EInteractionType.Duty)
|
||||
return null;
|
||||
yield break;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(step.ContentFinderConditionId);
|
||||
return new Task(step.ContentFinderConditionId.Value);
|
||||
|
||||
if (autoDutyIpc.IsConfiguredToRunContent(step.ContentFinderConditionId, step.AutoDutyEnabled))
|
||||
{
|
||||
yield return new StartAutoDutyTask(step.ContentFinderConditionId.Value);
|
||||
yield return new WaitAutoDutyTask(step.ContentFinderConditionId.Value);
|
||||
yield return new WaitAtEnd.WaitNextStepOrSequence();
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new OpenDutyFinderTask(step.ContentFinderConditionId.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record Task(uint ContentFinderConditionId) : ITask
|
||||
internal sealed record StartAutoDutyTask(uint ContentFinderConditionId) : ITask
|
||||
{
|
||||
public override string ToString() => $"StartAutoDuty({ContentFinderConditionId})";
|
||||
}
|
||||
|
||||
internal sealed class StartAutoDutyExecutor(
|
||||
AutoDutyIpc autoDutyIpc,
|
||||
TerritoryData territoryData,
|
||||
IClientState clientState) : TaskExecutor<StartAutoDutyTask>
|
||||
{
|
||||
protected override bool Start()
|
||||
{
|
||||
autoDutyIpc.StartInstance(Task.ContentFinderConditionId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override ETaskResult Update()
|
||||
{
|
||||
if (!territoryData.TryGetTerritoryIdForContentFinderCondition(Task.ContentFinderConditionId,
|
||||
out uint territoryId))
|
||||
throw new TaskException("Failed to get territory ID for content finder condition");
|
||||
|
||||
return clientState.TerritoryType == territoryId ? ETaskResult.TaskComplete : ETaskResult.StillRunning;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record WaitAutoDutyTask(uint ContentFinderConditionId) : ITask
|
||||
{
|
||||
public override string ToString() => $"Wait(AutoDuty, left instance {ContentFinderConditionId})";
|
||||
}
|
||||
|
||||
internal sealed class WaitAutoDutyExecutor(
|
||||
AutoDutyIpc autoDutyIpc,
|
||||
TerritoryData territoryData,
|
||||
IClientState clientState) : TaskExecutor<WaitAutoDutyTask>
|
||||
{
|
||||
protected override bool Start() => true;
|
||||
|
||||
public override ETaskResult Update()
|
||||
{
|
||||
if (!territoryData.TryGetTerritoryIdForContentFinderCondition(Task.ContentFinderConditionId,
|
||||
out uint territoryId))
|
||||
throw new TaskException("Failed to get territory ID for content finder condition");
|
||||
|
||||
return clientState.TerritoryType != territoryId && autoDutyIpc.IsStopped()
|
||||
? ETaskResult.TaskComplete
|
||||
: ETaskResult.StillRunning;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record OpenDutyFinderTask(uint ContentFinderConditionId) : ITask
|
||||
{
|
||||
public override string ToString() => $"OpenDutyFinder({ContentFinderConditionId})";
|
||||
}
|
||||
|
||||
internal sealed class OpenDutyWindowExecutor(
|
||||
internal sealed class OpenDutyFinderExecutor(
|
||||
GameFunctions gameFunctions,
|
||||
ICondition condition) : TaskExecutor<Task>
|
||||
ICondition condition) : TaskExecutor<OpenDutyFinderTask>
|
||||
{
|
||||
protected override bool Start()
|
||||
{
|
||||
|
@ -8,6 +8,7 @@ using Dalamud.Plugin.Services;
|
||||
using Questionable.Controller.Steps.Common;
|
||||
using Questionable.Controller.Utils;
|
||||
using Questionable.Data;
|
||||
using Questionable.External;
|
||||
using Questionable.Functions;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
@ -19,7 +20,8 @@ internal static class WaitAtEnd
|
||||
internal sealed class Factory(
|
||||
IClientState clientState,
|
||||
ICondition condition,
|
||||
TerritoryData territoryData)
|
||||
TerritoryData territoryData,
|
||||
AutoDutyIpc autoDutyIpc)
|
||||
: ITaskFactory
|
||||
{
|
||||
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
|
||||
@ -50,7 +52,7 @@ internal static class WaitAtEnd
|
||||
case EInteractionType.Snipe:
|
||||
return [new WaitNextStepOrSequence()];
|
||||
|
||||
case EInteractionType.Duty:
|
||||
case EInteractionType.Duty when !autoDutyIpc.IsConfiguredToRunContent(step.ContentFinderConditionId, step.AutoDutyEnabled):
|
||||
case EInteractionType.SinglePlayerDuty:
|
||||
return [new EndAutomation()];
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using Lumina.Excel.Sheets;
|
||||
|
||||
@ -15,6 +18,7 @@ internal sealed class TerritoryData
|
||||
private readonly ImmutableDictionary<ushort, uint> _dutyTerritories;
|
||||
private readonly ImmutableDictionary<uint, string> _instanceNames;
|
||||
private readonly ImmutableDictionary<uint, string> _contentFinderConditionNames;
|
||||
private readonly ImmutableDictionary<uint, uint> _contentFinderConditionIds;
|
||||
|
||||
public TerritoryData(IDataManager dataManager)
|
||||
{
|
||||
@ -40,11 +44,14 @@ internal sealed class TerritoryData
|
||||
|
||||
_instanceNames = dataManager.GetExcelSheet<ContentFinderCondition>()
|
||||
.Where(x => x.RowId > 0 && x.Content.RowId != 0 && x.ContentLinkType == 1 && x.ContentType.RowId != 6)
|
||||
.ToImmutableDictionary(x => x.Content.RowId, x => x.Name.ToString());
|
||||
.ToImmutableDictionary(x => x.Content.RowId, x => x.Name.ToDalamudString().ToString());
|
||||
|
||||
_contentFinderConditionNames = dataManager.GetExcelSheet<ContentFinderCondition>()
|
||||
.Where(x => x.RowId > 0 && x.Content.RowId != 0 && x.ContentLinkType == 1 && x.ContentType.RowId != 6)
|
||||
.ToImmutableDictionary(x => x.RowId, x => x.Name.ToString());
|
||||
.ToImmutableDictionary(x => x.RowId, x => FixName(x.Name.ToDalamudString().ToString(), dataManager.Language));
|
||||
_contentFinderConditionIds = dataManager.GetExcelSheet<ContentFinderCondition>()
|
||||
.Where(x => x.RowId > 0 && x.Content.RowId != 0 && x.ContentLinkType == 1 && x.ContentType.RowId != 6)
|
||||
.ToImmutableDictionary(x => x.RowId, x => x.TerritoryType.RowId);
|
||||
}
|
||||
|
||||
public string? GetName(ushort territoryId) => _territoryNames.GetValueOrDefault(territoryId);
|
||||
@ -68,4 +75,15 @@ internal sealed class TerritoryData
|
||||
public string? GetInstanceName(ushort instanceId) => _instanceNames.GetValueOrDefault(instanceId);
|
||||
|
||||
public string? GetContentFinderConditionName(uint cfcId) => _contentFinderConditionNames.GetValueOrDefault(cfcId);
|
||||
|
||||
public bool TryGetTerritoryIdForContentFinderCondition(uint cfcId, out uint territoryId) =>
|
||||
_contentFinderConditionIds.TryGetValue(cfcId, out territoryId);
|
||||
|
||||
private static string FixName(string name, ClientLanguage language)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name) || language != ClientLanguage.English)
|
||||
return name;
|
||||
|
||||
return string.Concat(name[0].ToString().ToUpper(CultureInfo.InvariantCulture), name.AsSpan(1));
|
||||
}
|
||||
}
|
||||
|
89
Questionable/External/AutoDutyIpc.cs
vendored
Normal file
89
Questionable/External/AutoDutyIpc.cs
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Ipc;
|
||||
using Dalamud.Plugin.Ipc.Exceptions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Controller.Steps;
|
||||
using Questionable.Data;
|
||||
|
||||
namespace Questionable.External;
|
||||
|
||||
internal sealed class AutoDutyIpc
|
||||
{
|
||||
private readonly Configuration _configuration;
|
||||
private readonly TerritoryData _territoryData;
|
||||
private readonly ILogger<AutoDutyIpc> _logger;
|
||||
private readonly ICallGateSubscriber<uint,bool> _contentHasPath;
|
||||
private readonly ICallGateSubscriber<uint,int,bool,object> _run;
|
||||
private readonly ICallGateSubscriber<bool> _isStopped;
|
||||
|
||||
public AutoDutyIpc(IDalamudPluginInterface pluginInterface, Configuration configuration, TerritoryData territoryData, ILogger<AutoDutyIpc> logger)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_territoryData = territoryData;
|
||||
_logger = logger;
|
||||
_contentHasPath = pluginInterface.GetIpcSubscriber<uint, bool>("AutoDuty.ContentHasPath");
|
||||
_run = pluginInterface.GetIpcSubscriber<uint, int, bool, object>("AutoDuty.Run");
|
||||
_isStopped = pluginInterface.GetIpcSubscriber<bool>("AutoDuty.IsStopped");
|
||||
}
|
||||
|
||||
public bool IsConfiguredToRunContent(uint? cfcId, bool autoDutyEnabled)
|
||||
{
|
||||
if (cfcId == null)
|
||||
return false;
|
||||
|
||||
if (!_configuration.Duties.RunInstancedContentWithAutoDuty)
|
||||
return false;
|
||||
|
||||
if (_configuration.Duties.BlacklistedDutyCfcIds.Contains(cfcId.Value))
|
||||
return false;
|
||||
|
||||
if (_configuration.Duties.WhitelistedDutyCfcIds.Contains(cfcId.Value) &&
|
||||
_territoryData.TryGetTerritoryIdForContentFinderCondition(cfcId.Value, out _))
|
||||
return true;
|
||||
|
||||
return autoDutyEnabled && HasPath(cfcId.Value);
|
||||
}
|
||||
|
||||
public bool HasPath(uint cfcId)
|
||||
{
|
||||
if (!_territoryData.TryGetTerritoryIdForContentFinderCondition(cfcId, out uint territoryType))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
return _contentHasPath.InvokeFunc(territoryType);
|
||||
}
|
||||
catch (IpcError e)
|
||||
{
|
||||
_logger.LogWarning("Unable to query AutoDuty for path in territory {TerritoryType}: {Message}", territoryType, e.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void StartInstance(uint cfcId)
|
||||
{
|
||||
if (!_territoryData.TryGetTerritoryIdForContentFinderCondition(cfcId, out uint territoryType))
|
||||
throw new TaskException($"Unknown ContentFinderConditionId {cfcId}");
|
||||
|
||||
try
|
||||
{
|
||||
_run.InvokeAction(territoryType, 0, true);
|
||||
}
|
||||
catch (IpcError e)
|
||||
{
|
||||
throw new TaskException($"Unable to run content with AutoDuty: {e.Message}", e);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsStopped()
|
||||
{
|
||||
try
|
||||
{
|
||||
return _isStopped.InvokeFunc();
|
||||
}
|
||||
catch (IpcError)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -129,6 +129,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
|
||||
serviceCollection.AddSingleton<TextAdvanceIpc>();
|
||||
serviceCollection.AddSingleton<NotificationMasterIpc>();
|
||||
serviceCollection.AddSingleton<AutomatonIpc>();
|
||||
serviceCollection.AddSingleton<AutoDutyIpc>();
|
||||
}
|
||||
|
||||
private static void AddTaskFactories(ServiceCollection serviceCollection)
|
||||
@ -178,7 +179,9 @@ public sealed class QuestionablePlugin : IDalamudPlugin
|
||||
.AddTaskFactoryAndExecutor<AethernetShard.Attune, AethernetShard.Factory, AethernetShard.DoAttune>();
|
||||
serviceCollection.AddTaskFactoryAndExecutor<Aetheryte.Attune, Aetheryte.Factory, Aetheryte.DoAttune>();
|
||||
serviceCollection.AddTaskFactoryAndExecutor<Combat.Task, Combat.Factory, Combat.HandleCombat>();
|
||||
serviceCollection.AddTaskFactoryAndExecutor<Duty.Task, Duty.Factory, Duty.OpenDutyWindowExecutor>();
|
||||
serviceCollection.AddTaskFactoryAndExecutor<Duty.OpenDutyFinderTask, Duty.Factory, Duty.OpenDutyFinderExecutor>();
|
||||
serviceCollection.AddTaskExecutor<Duty.StartAutoDutyTask, Duty.StartAutoDutyExecutor>();
|
||||
serviceCollection.AddTaskExecutor<Duty.WaitAutoDutyTask, Duty.WaitAutoDutyExecutor>();
|
||||
serviceCollection.AddTaskFactory<Emote.Factory>();
|
||||
serviceCollection.AddTaskExecutor<Emote.UseOnObject, Emote.UseOnObjectExecutor>();
|
||||
serviceCollection.AddTaskExecutor<Emote.UseOnSelf, Emote.UseOnSelfExecutor>();
|
||||
|
@ -1,7 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Components;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
@ -12,19 +16,28 @@ using ImGuiNET;
|
||||
using LLib.ImGui;
|
||||
using Lumina.Excel.Sheets;
|
||||
using Questionable.Controller;
|
||||
using Questionable.Data;
|
||||
using Questionable.External;
|
||||
using Questionable.Model;
|
||||
using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany;
|
||||
|
||||
namespace Questionable.Windows;
|
||||
|
||||
internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
||||
{
|
||||
private const string DutyClipboardPrefix = "qst:duty:";
|
||||
private const string DutyClipboardSeparator = ";";
|
||||
private const string DutyWhitelistPrefix = "+";
|
||||
private const string DutyBlacklistPrefix = "-";
|
||||
|
||||
private static readonly List<(uint Id, string Name)> DefaultMounts = [(0, "Mount Roulette")];
|
||||
|
||||
private readonly IDalamudPluginInterface _pluginInterface;
|
||||
private readonly NotificationMasterIpc _notificationMasterIpc;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly CombatController _combatController;
|
||||
private readonly QuestRegistry _questRegistry;
|
||||
private readonly AutoDutyIpc _autoDutyIpc;
|
||||
|
||||
private readonly uint[] _mountIds;
|
||||
private readonly string[] _mountNames;
|
||||
@ -34,17 +47,38 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
||||
private readonly string[] _grandCompanyNames =
|
||||
["None (manually pick quest)", "Maelstrom", "Twin Adder", "Immortal Flames"];
|
||||
|
||||
private readonly string[] _supportedCfcOptions =
|
||||
[
|
||||
$"{SeIconChar.Circle.ToIconChar()} Enabled (Default)",
|
||||
$"{SeIconChar.Circle.ToIconChar()} Enabled",
|
||||
$"{SeIconChar.Cross.ToIconChar()} Disabled"
|
||||
];
|
||||
|
||||
private readonly string[] _unsupportedCfcOptions =
|
||||
[
|
||||
$"{SeIconChar.Cross.ToIconChar()} Disabled (Default)",
|
||||
$"{SeIconChar.Circle.ToIconChar()} Enabled",
|
||||
$"{SeIconChar.Cross.ToIconChar()} Disabled"
|
||||
];
|
||||
|
||||
private readonly Dictionary<EExpansionVersion, List<DutyInfo>> _contentFinderConditionNames;
|
||||
|
||||
public ConfigWindow(IDalamudPluginInterface pluginInterface,
|
||||
NotificationMasterIpc notificationMasterIpc,
|
||||
Configuration configuration,
|
||||
IDataManager dataManager,
|
||||
CombatController combatController)
|
||||
CombatController combatController,
|
||||
TerritoryData territoryData,
|
||||
QuestRegistry questRegistry,
|
||||
AutoDutyIpc autoDutyIpc)
|
||||
: base("Config - Questionable###QuestionableConfig", ImGuiWindowFlags.AlwaysAutoResize)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
_notificationMasterIpc = notificationMasterIpc;
|
||||
_configuration = configuration;
|
||||
_combatController = combatController;
|
||||
_questRegistry = questRegistry;
|
||||
_autoDutyIpc = autoDutyIpc;
|
||||
|
||||
var mounts = dataManager.GetExcelSheet<Mount>()
|
||||
.Where(x => x is { RowId: > 0, Icon: > 0 })
|
||||
@ -54,10 +88,41 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
||||
.ToList();
|
||||
_mountIds = DefaultMounts.Select(x => x.Id).Concat(mounts.Select(x => x.MountId)).ToArray();
|
||||
_mountNames = DefaultMounts.Select(x => x.Name).Concat(mounts.Select(x => x.Name)).ToArray();
|
||||
|
||||
_contentFinderConditionNames = dataManager.GetExcelSheet<DawnContent>()
|
||||
.Where(x => x.RowId > 0)
|
||||
.Select(x => x.Content.ValueNullable)
|
||||
.Where(x => x != null)
|
||||
.Select(x => x!.Value)
|
||||
.Select(x => new
|
||||
{
|
||||
Expansion = (EExpansionVersion)x.TerritoryType.Value.ExVersion.RowId,
|
||||
CfcId = x.RowId,
|
||||
Name = territoryData.GetContentFinderConditionName(x.RowId) ?? "?",
|
||||
TerritoryId = x.TerritoryType.RowId,
|
||||
ContentType = x.ContentType.RowId,
|
||||
Level = x.ClassJobLevelRequired,
|
||||
x.SortKey
|
||||
})
|
||||
.GroupBy(x => x.Expansion)
|
||||
.ToDictionary(x => x.Key,
|
||||
x => x.OrderBy(y => y.Level)
|
||||
.ThenBy(y => y.ContentType)
|
||||
.ThenBy(y => y.SortKey)
|
||||
.Select(y => new DutyInfo(y.CfcId, y.TerritoryId, $"{SeIconChar.LevelEn.ToIconChar()}{FormatLevel(y.Level)} {y.Name}"))
|
||||
.ToList());
|
||||
}
|
||||
|
||||
public WindowConfig WindowConfig => _configuration.ConfigWindowConfig;
|
||||
|
||||
private static string FormatLevel(int level)
|
||||
{
|
||||
if (level == 0)
|
||||
return string.Empty;
|
||||
|
||||
return $"{FormatLevel(level / 10)}{(SeIconChar.Number0 + level % 10).ToIconChar()}";
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
using var tabBar = ImRaii.TabBar("QuestionableConfigTabs");
|
||||
@ -65,6 +130,7 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
||||
return;
|
||||
|
||||
DrawGeneralTab();
|
||||
DrawDutiesTab();
|
||||
DrawNotificationsTab();
|
||||
DrawAdvancedTab();
|
||||
}
|
||||
@ -138,6 +204,175 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDutiesTab()
|
||||
{
|
||||
using var tab = ImRaii.TabItem("Duties");
|
||||
if (!tab)
|
||||
return;
|
||||
|
||||
bool runInstancedContentWithAutoDuty = _configuration.Duties.RunInstancedContentWithAutoDuty;
|
||||
if (ImGui.Checkbox("Run instanced content with AutoDuty and BossMod", ref runInstancedContentWithAutoDuty))
|
||||
{
|
||||
_configuration.Duties.RunInstancedContentWithAutoDuty = runInstancedContentWithAutoDuty;
|
||||
Save();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGuiComponents.HelpMarker(
|
||||
"The combat module used for this is configured by AutoDuty, ignoring whichever selection you've made in Questionable's \"General\" configuration.");
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
using (ImRaii.Disabled(!runInstancedContentWithAutoDuty))
|
||||
{
|
||||
ImGui.Text(
|
||||
"Questionable includes a default list of duties that work if AutoDuty and BossMod are installed.");
|
||||
|
||||
ImGui.Text("The included list of duties can change with each update, and is based on the following spreadsheet:");
|
||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.GlobeEurope, "Open AutoDuty spreadsheet"))
|
||||
Util.OpenLink(
|
||||
"https://docs.google.com/spreadsheets/d/151RlpqRcCpiD_VbQn6Duf-u-S71EP7d0mx3j1PDNoNA/edit?pli=1#gid=0");
|
||||
|
||||
ImGui.Separator();
|
||||
ImGui.Text("You can override the dungeon settings for each individual dungeon/trial:");
|
||||
|
||||
using (var child = ImRaii.Child("DutyConfiguration", new Vector2(-1, 400), true))
|
||||
{
|
||||
if (child)
|
||||
{
|
||||
foreach (EExpansionVersion expansion in Enum.GetValues<EExpansionVersion>())
|
||||
{
|
||||
if (ImGui.CollapsingHeader(expansion.ToString()))
|
||||
{
|
||||
using var table = ImRaii.Table($"Duties{expansion}", 2, ImGuiTableFlags.SizingFixedFit);
|
||||
if (table)
|
||||
{
|
||||
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch);
|
||||
ImGui.TableSetupColumn("Options", ImGuiTableColumnFlags.WidthFixed, 200f);
|
||||
|
||||
if (_contentFinderConditionNames.TryGetValue(expansion, out var cfcNames))
|
||||
{
|
||||
foreach (var (cfcId, territoryId, name) in cfcNames)
|
||||
{
|
||||
if (_questRegistry.TryGetDutyByContentFinderConditionId(cfcId,
|
||||
out bool autoDutyEnabledByDefault))
|
||||
{
|
||||
ImGui.TableNextRow();
|
||||
|
||||
string[] labels = autoDutyEnabledByDefault
|
||||
? _supportedCfcOptions
|
||||
: _unsupportedCfcOptions;
|
||||
int value = 0;
|
||||
if (_configuration.Duties.WhitelistedDutyCfcIds.Contains(cfcId))
|
||||
value = 1;
|
||||
if (_configuration.Duties.BlacklistedDutyCfcIds.Contains(cfcId))
|
||||
value = 2;
|
||||
|
||||
if (ImGui.TableNextColumn())
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(name);
|
||||
if (ImGui.IsItemHovered() && _configuration.Advanced.AdditionalStatusInformation)
|
||||
{
|
||||
using var tooltip = ImRaii.Tooltip();
|
||||
if (tooltip)
|
||||
{
|
||||
ImGui.TextUnformatted(name);
|
||||
ImGui.Separator();
|
||||
ImGui.BulletText($"TerritoryId: {territoryId}");
|
||||
ImGui.BulletText($"ContentFinderConditionId: {cfcId}");
|
||||
}
|
||||
}
|
||||
|
||||
if (runInstancedContentWithAutoDuty && !_autoDutyIpc.HasPath(cfcId))
|
||||
ImGuiComponents.HelpMarker("This duty is not supported by AutoDuty", FontAwesomeIcon.Times, ImGuiColors.DalamudRed);
|
||||
}
|
||||
|
||||
if (ImGui.TableNextColumn())
|
||||
{
|
||||
using var _ = ImRaii.PushId($"##Dungeon{cfcId}");
|
||||
ImGui.SetNextItemWidth(200);
|
||||
if (ImGui.Combo(string.Empty, ref value, labels, labels.Length))
|
||||
{
|
||||
_configuration.Duties.WhitelistedDutyCfcIds.Remove(cfcId);
|
||||
_configuration.Duties.BlacklistedDutyCfcIds.Remove(cfcId);
|
||||
|
||||
if (value == 1)
|
||||
_configuration.Duties.WhitelistedDutyCfcIds.Add(cfcId);
|
||||
else if (value == 2)
|
||||
_configuration.Duties.BlacklistedDutyCfcIds.Add(cfcId);
|
||||
|
||||
Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using (ImRaii.Disabled(_configuration.Duties.WhitelistedDutyCfcIds.Count +
|
||||
_configuration.Duties.BlacklistedDutyCfcIds.Count == 0))
|
||||
{
|
||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Copy, "Export to clipboard"))
|
||||
{
|
||||
var whitelisted =
|
||||
_configuration.Duties.WhitelistedDutyCfcIds.Select(x => $"{DutyWhitelistPrefix}{x}");
|
||||
var blacklisted =
|
||||
_configuration.Duties.BlacklistedDutyCfcIds.Select(x => $"{DutyBlacklistPrefix}{x}");
|
||||
string text = DutyClipboardPrefix + Convert.ToBase64String(Encoding.UTF8.GetBytes(
|
||||
string.Join(DutyClipboardSeparator, whitelisted.Concat(blacklisted))));
|
||||
ImGui.SetClipboardText(text);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
string? clipboardText = GetClipboardText();
|
||||
using (ImRaii.Disabled(clipboardText == null || !clipboardText.StartsWith(DutyClipboardPrefix, StringComparison.InvariantCulture)))
|
||||
{
|
||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Paste, "Import from Clipboard"))
|
||||
{
|
||||
clipboardText = clipboardText!.Substring(DutyClipboardPrefix.Length);
|
||||
string text = Encoding.UTF8.GetString(Convert.FromBase64String(clipboardText));
|
||||
|
||||
_configuration.Duties.WhitelistedDutyCfcIds.Clear();
|
||||
_configuration.Duties.BlacklistedDutyCfcIds.Clear();
|
||||
foreach (string part in text.Split(DutyClipboardSeparator))
|
||||
{
|
||||
if (part.StartsWith(DutyWhitelistPrefix, StringComparison.InvariantCulture) &&
|
||||
uint.TryParse(part.AsSpan(DutyWhitelistPrefix.Length), CultureInfo.InvariantCulture,
|
||||
out uint whitelistedCfcId))
|
||||
_configuration.Duties.WhitelistedDutyCfcIds.Add(whitelistedCfcId);
|
||||
|
||||
if (part.StartsWith(DutyBlacklistPrefix, StringComparison.InvariantCulture) &&
|
||||
uint.TryParse(part.AsSpan(DutyBlacklistPrefix.Length), CultureInfo.InvariantCulture,
|
||||
out uint blacklistedCfcId))
|
||||
_configuration.Duties.WhitelistedDutyCfcIds.Add(blacklistedCfcId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
using (var unused = ImRaii.Disabled(!ImGui.IsKeyDown(ImGuiKey.ModCtrl)))
|
||||
{
|
||||
if (ImGui.Button("Reset to default"))
|
||||
{
|
||||
_configuration.Duties.WhitelistedDutyCfcIds.Clear();
|
||||
_configuration.Duties.BlacklistedDutyCfcIds.Clear();
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||
ImGui.SetTooltip("Hold CTRL to enable this button.");
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawNotificationsTab()
|
||||
{
|
||||
using var tab = ImRaii.TabItem("Notifications");
|
||||
@ -231,4 +466,21 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
||||
private void Save() => _pluginInterface.SavePluginConfig(_configuration);
|
||||
|
||||
public void SaveWindowConfig() => Save();
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation for <see cref="ImGui.GetClipboardText"/> throws an NullReferenceException if the clipboard is empty, maybe also if it doesn't contain text.
|
||||
/// </summary>
|
||||
private unsafe string? GetClipboardText()
|
||||
{
|
||||
byte* ptr = ImGuiNative.igGetClipboardText();
|
||||
if (ptr == null)
|
||||
return null;
|
||||
|
||||
int byteCount = 0;
|
||||
while (ptr[byteCount] != 0)
|
||||
++byteCount;
|
||||
return Encoding.UTF8.GetString(ptr, byteCount);
|
||||
}
|
||||
|
||||
private sealed record DutyInfo(uint CfcId, uint TerritoryId, string Name);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user