muffin v7.4.18

This commit is contained in:
alydev 2026-03-26 14:56:12 +10:00
parent 53aa9fdee8
commit f82b9ce2a2
50 changed files with 142364 additions and 230361 deletions

View file

@ -48,26 +48,22 @@ public static class AssemblyFateDefinitionLoader
List<FateActionTarget> list = new List<FateActionTarget>(num); List<FateActionTarget> list = new List<FateActionTarget>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<FateActionTarget> span = CollectionsMarshal.AsSpan(list); Span<FateActionTarget> span = CollectionsMarshal.AsSpan(list);
int num2 = 0; span[0] = new FateActionTarget
span[num2] = new FateActionTarget
{ {
DataId = 18862u, DataId = 18862u,
Action = EAction.CheerRhythmYellow Action = EAction.CheerRhythmYellow
}; };
num2++; span[1] = new FateActionTarget
span[num2] = new FateActionTarget
{ {
DataId = 18860u, DataId = 18860u,
Action = EAction.CheerRhythmBlue Action = EAction.CheerRhythmBlue
}; };
num2++; span[2] = new FateActionTarget
span[num2] = new FateActionTarget
{ {
DataId = 18861u, DataId = 18861u,
Action = EAction.CheerRhythmGreen Action = EAction.CheerRhythmGreen
}; };
num2++; span[3] = new FateActionTarget
span[num2] = new FateActionTarget
{ {
DataId = 18859u, DataId = 18859u,
Action = EAction.CheerRhythmRed Action = EAction.CheerRhythmRed
@ -76,34 +72,34 @@ public static class AssemblyFateDefinitionLoader
obj.RequiredStatusId = EStatus.FaceInTheCrowd; obj.RequiredStatusId = EStatus.FaceInTheCrowd;
obj.TransformNpcDataId = 1055771u; obj.TransformNpcDataId = 1055771u;
obj.TransformNpcPosition = new Vector3(-37.369385f, 5.0000005f, -130.14423f); obj.TransformNpcPosition = new Vector3(-37.369385f, 5.0000005f, -130.14423f);
num2 = 3; num = 3;
List<DialogueChoice> list2 = new List<DialogueChoice>(num2); List<DialogueChoice> list2 = new List<DialogueChoice>(num);
CollectionsMarshal.SetCount(list2, num2); CollectionsMarshal.SetCount(list2, num);
Span<DialogueChoice> span2 = CollectionsMarshal.AsSpan(list2); Span<DialogueChoice> span2 = CollectionsMarshal.AsSpan(list2);
num = 0; span2[0] = new DialogueChoice
span2[num] = new DialogueChoice
{ {
Type = EDialogChoiceType.List, Type = EDialogChoiceType.List,
ExcelSheet = "custom/009/FesPdy2026FateDisguise_00951", ExcelSheet = "custom/009/FesPdy2026FateDisguise_00951",
Prompt = new ExcelRef("TEXT_FESPDY2026FATEDISGUISE_00951_Q1_000_000"), Prompt = new ExcelRef("TEXT_FESPDY2026FATEDISGUISE_00951_Q1_000_000"),
Answer = new ExcelRef("TEXT_FESPDY2026FATEDISGUISE_00951_A1_000_001") Answer = new ExcelRef("TEXT_FESPDY2026FATEDISGUISE_00951_A1_000_001")
}; };
num++; span2[1] = new DialogueChoice
span2[num] = new DialogueChoice
{ {
Type = EDialogChoiceType.List, Type = EDialogChoiceType.List,
ExcelSheet = "custom/009/FesPdy2026FateDisguise_00951", ExcelSheet = "custom/009/FesPdy2026FateDisguise_00951",
Prompt = new ExcelRef("TEXT_FESPDY2026FATEDISGUISE_00951_Q2_000_000"), Prompt = new ExcelRef("TEXT_FESPDY2026FATEDISGUISE_00951_Q2_000_000"),
Answer = new ExcelRef("TEXT_FESPDY2026FATEDISGUISE_00951_A2_100_004") Answer = new ExcelRef("TEXT_FESPDY2026FATEDISGUISE_00951_A2_100_004")
}; };
num++; span2[2] = new DialogueChoice
span2[num] = new DialogueChoice
{ {
Type = EDialogChoiceType.YesNo, Type = EDialogChoiceType.YesNo,
ExcelSheet = "custom/009/FesPdy2026FateDisguise_00951", ExcelSheet = "custom/009/FesPdy2026FateDisguise_00951",
Prompt = new ExcelRef("TEXT_FESPDY2026FATEDISGUISE_00951_CONFIRM_100_004") Prompt = new ExcelRef("TEXT_FESPDY2026FATEDISGUISE_00951_CONFIRM_100_004")
}; };
obj.TransformDialogueChoices = list2; obj.TransformDialogueChoices = list2;
obj.RequiredQuestId = (ushort)5444;
obj.StopAction = EAction.CurtainCall;
obj.EventExpiry = new DateTime(2026, 3, 12, 14, 59, 0, DateTimeKind.Utc);
AddDefinition(1, obj); AddDefinition(1, obj);
} }
} }

View file

@ -90,6 +90,21 @@
"description": "The data id of the NPC/Object/Aetheryte/Aether Current", "description": "The data id of the NPC/Object/Aetheryte/Aether Current",
"exclusiveMinimum": 0 "exclusiveMinimum": 0
}, },
"DataIds": {
"type": "array",
"description": "Multiple data ids to search for (e.g. for clearing randomly spawned objects)",
"items": {
"type": "integer",
"exclusiveMinimum": 0
}
},
"WaypointPositions": {
"type": "array",
"description": "Exit/waypoint positions to walk to after clearing all DataIds objects (e.g. doors between zones in a looping duty)",
"items": {
"$ref": "https://github.com/WigglyMuffin/Questionable/raw/refs/heads/main/Questionable.Model/common-vector3.json"
}
},
"Position": { "Position": {
"$ref": "https://github.com/WigglyMuffin/Questionable/raw/refs/heads/main/Questionable.Model/common-vector3.json" "$ref": "https://github.com/WigglyMuffin/Questionable/raw/refs/heads/main/Questionable.Model/common-vector3.json"
}, },
@ -624,8 +639,9 @@
} }
}, },
"then": { "then": {
"required": [ "anyOf": [
"Position" { "required": ["Position"] },
{ "required": ["DataId"] }
] ]
} }
}, },
@ -1416,7 +1432,10 @@
"Cheer Rhythm: Red", "Cheer Rhythm: Red",
"Cheer Rhythm: Blue", "Cheer Rhythm: Blue",
"Cheer Rhythm: Green", "Cheer Rhythm: Green",
"Cheer Rhythm: Yellow" "Cheer Rhythm: Yellow",
"Pruning Pirouette",
"Roaring Eggscapade",
"The Spriganator"
] ]
} }
}, },

File diff suppressed because it is too large Load diff

View file

@ -15,9 +15,7 @@ public sealed class StringListOrValueConverter : JsonConverter<List<string>>
int num = 1; int num = 1;
List<string> list = new List<string>(num); List<string> list = new List<string>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<string> span = CollectionsMarshal.AsSpan(list); CollectionsMarshal.AsSpan(list)[0] = reader.GetString();
int index = 0;
span[index] = reader.GetString();
return list; return list;
} }
if (reader.TokenType != JsonTokenType.StartArray) if (reader.TokenType != JsonTokenType.StartArray)

View file

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Questionable.Model.Common.Converter;
public sealed class VectorListConverter : JsonConverter<List<Vector3>>
{
private static readonly VectorConverter ItemConverter = new VectorConverter();
public override List<Vector3> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
{
throw new JsonException();
}
List<Vector3> list = new List<Vector3>();
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndArray)
{
return list;
}
list.Add(ItemConverter.Read(ref reader, typeof(Vector3), options));
}
throw new JsonException();
}
public override void Write(Utf8JsonWriter writer, List<Vector3> value, JsonSerializerOptions options)
{
writer.WriteStartArray();
foreach (Vector3 item in value)
{
ItemConverter.Write(writer, item, options);
}
writer.WriteEndArray();
}
}

View file

@ -219,6 +219,18 @@ public sealed class ActionConverter : EnumConverter<EAction>
EAction.CheerRhythmYellow, EAction.CheerRhythmYellow,
"Cheer Rhythm: Yellow" "Cheer Rhythm: Yellow"
}, },
{
EAction.PruningPirouette,
"Pruning Pirouette"
},
{
EAction.RoaringEggscapade,
"Roaring Eggscapade"
},
{
EAction.TheSpriganator,
"The Spriganator"
},
{ {
EAction.CurtainCall, EAction.CurtainCall,
"Curtain Call" "Curtain Call"

View file

@ -66,6 +66,9 @@ public enum EAction
CheerRhythmBlue = 44502, CheerRhythmBlue = 44502,
CheerRhythmGreen = 44503, CheerRhythmGreen = 44503,
CheerRhythmYellow = 44504, CheerRhythmYellow = 44504,
PruningPirouette = 45127,
RoaringEggscapade = 42039,
TheSpriganator = 42038,
CurtainCall = 11063, CurtainCall = 11063,
Prospect = 227, Prospect = 227,
CollectMiner = 240, CollectMiner = 240,

View file

@ -15,6 +15,11 @@ public sealed class QuestStep
public uint? DataId { get; set; } public uint? DataId { get; set; }
public List<uint> DataIds { get; set; } = new List<uint>();
[JsonConverter(typeof(VectorListConverter))]
public List<Vector3> WaypointPositions { get; set; } = new List<Vector3>();
[JsonConverter(typeof(VectorConverter))] [JsonConverter(typeof(VectorConverter))]
public Vector3? Position { get; set; } public Vector3? Position { get; set; }

View file

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text.Json.Serialization;
using Questionable.Model.Common;
using Questionable.Model.Common.Converter;
using Questionable.Model.Questing.Converter;
namespace Questionable.Model.Questing;
public sealed class SeasonalDutyDefinition
{
public string Name { get; set; } = string.Empty;
public ushort TerritoryId { get; set; }
public EAetheryteLocation Aetheryte { get; set; }
public AethernetShortcut? AethernetShortcut { get; set; }
public uint NpcDataId { get; set; }
[JsonConverter(typeof(VectorConverter))]
public Vector3 NpcPosition { get; set; }
public List<DialogueChoice> DialogueChoices { get; set; } = new List<DialogueChoice>();
public ushort DutyTerritoryId { get; set; }
public List<uint> DataIds { get; set; } = new List<uint>();
[JsonConverter(typeof(VectorListConverter))]
public List<Vector3> WaypointPositions { get; set; } = new List<Vector3>();
[JsonConverter(typeof(ActionConverter))]
public EAction Action { get; set; }
public float StopDistance { get; set; }
public ushort? RequiredQuestId { get; set; }
public DateTime? EventExpiry { get; set; }
}

View file

@ -63,6 +63,8 @@ internal sealed class InteractionUiController : IDisposable
private readonly FateController _fateController; private readonly FateController _fateController;
private readonly SeasonalDutyController _seasonalDutyController;
private readonly BossModIpc _bossModIpc; private readonly BossModIpc _bossModIpc;
private readonly Configuration _configuration; private readonly Configuration _configuration;
@ -79,7 +81,7 @@ internal sealed class InteractionUiController : IDisposable
{ {
get get
{ {
if (!_isInitialCheck && !_questController.IsRunning && !_fateController.IsRunning) if (!_isInitialCheck && !_questController.IsRunning && !_fateController.IsRunning && !_seasonalDutyController.IsRunning)
{ {
return _territoryData.IsQuestBattleInstance(_clientState.TerritoryType); return _territoryData.IsQuestBattleInstance(_clientState.TerritoryType);
} }
@ -87,7 +89,7 @@ internal sealed class InteractionUiController : IDisposable
} }
} }
public unsafe InteractionUiController(IAddonLifecycle addonLifecycle, IDataManager dataManager, QuestFunctions questFunctions, AetheryteFunctions aetheryteFunctions, ExcelFunctions excelFunctions, QuestController questController, GatheringPointRegistry gatheringPointRegistry, QuestRegistry questRegistry, QuestData questData, TerritoryData territoryData, IGameGui gameGui, ITargetManager targetManager, IPluginLog pluginLog, IClientState clientState, IObjectTable objectTable, ShopController shopController, FateController fateController, BossModIpc bossModIpc, Configuration configuration, ILogger<InteractionUiController> logger) public unsafe InteractionUiController(IAddonLifecycle addonLifecycle, IDataManager dataManager, QuestFunctions questFunctions, AetheryteFunctions aetheryteFunctions, ExcelFunctions excelFunctions, QuestController questController, GatheringPointRegistry gatheringPointRegistry, QuestRegistry questRegistry, QuestData questData, TerritoryData territoryData, IGameGui gameGui, ITargetManager targetManager, IPluginLog pluginLog, IClientState clientState, IObjectTable objectTable, ShopController shopController, FateController fateController, SeasonalDutyController seasonalDutyController, BossModIpc bossModIpc, Configuration configuration, ILogger<InteractionUiController> logger)
{ {
_addonLifecycle = addonLifecycle; _addonLifecycle = addonLifecycle;
_dataManager = dataManager; _dataManager = dataManager;
@ -105,6 +107,7 @@ internal sealed class InteractionUiController : IDisposable
_objectTable = objectTable; _objectTable = objectTable;
_shopController = shopController; _shopController = shopController;
_fateController = fateController; _fateController = fateController;
_seasonalDutyController = seasonalDutyController;
_bossModIpc = bossModIpc; _bossModIpc = bossModIpc;
_configuration = configuration; _configuration = configuration;
_logger = logger; _logger = logger;
@ -118,6 +121,7 @@ internal sealed class InteractionUiController : IDisposable
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "DifficultySelectYesNo", DifficultySelectYesNoPostSetup); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "DifficultySelectYesNo", DifficultySelectYesNoPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "PointMenu", PointMenuPostSetup); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "PointMenu", PointMenuPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "HousingSelectBlock", HousingSelectBlockPostSetup); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "HousingSelectBlock", HousingSelectBlockPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "EasterMowingResult", EasterMowingResultPostSetup);
if (_gameGui.TryGetAddonByName<AtkUnitBase>("RhythmAction", out var addonPtr)) if (_gameGui.TryGetAddonByName<AtkUnitBase>("RhythmAction", out var addonPtr))
{ {
addonPtr->Close(fireCallback: true); addonPtr->Close(fireCallback: true);
@ -369,9 +373,7 @@ internal sealed class InteractionUiController : IDisposable
int num = 1; int num = 1;
List<EAetheryteLocation> list2 = new List<EAetheryteLocation>(num); List<EAetheryteLocation> list2 = new List<EAetheryteLocation>(num);
CollectionsMarshal.SetCount(list2, num); CollectionsMarshal.SetCount(list2, num);
Span<EAetheryteLocation> span = CollectionsMarshal.AsSpan(list2); CollectionsMarshal.AsSpan(list2)[0] = valueOrDefault;
int index = 0;
span[index] = valueOrDefault;
source = list2; source = list2;
} }
} }
@ -466,6 +468,15 @@ internal sealed class InteractionUiController : IDisposable
list.AddRange(list4.Select((DialogueChoice x) => new DialogueChoiceInfo(null, x))); list.AddRange(list4.Select((DialogueChoice x) => new DialogueChoiceInfo(null, x)));
} }
} }
if (_seasonalDutyController.IsRunning)
{
List<DialogueChoice> list5 = _seasonalDutyController.CurrentDuty?.DialogueChoices;
if (list5 != null && list5.Count > 0)
{
_logger.LogInformation("Adding {Count} dialogue choices from active seasonal duty", list5.Count);
list.AddRange(list5.Select((DialogueChoice x) => new DialogueChoiceInfo(null, x)));
}
}
if (list.Count == 0) if (list.Count == 0)
{ {
_logger.LogDebug("No dialogue choices to check"); _logger.LogDebug("No dialogue choices to check");
@ -658,24 +669,48 @@ internal sealed class InteractionUiController : IDisposable
return; return;
} }
QuestController.QuestProgress nextQuest = _questController.NextQuest; QuestController.QuestProgress nextQuest = _questController.NextQuest;
if ((nextQuest != null && CheckQuestYesNo(addonSelectYesno, nextQuest, text, checkAllSteps)) || !_fateController.IsRunning) if (nextQuest != null && CheckQuestYesNo(addonSelectYesno, nextQuest, text, checkAllSteps))
{ {
return; return;
} }
List<DialogueChoice> list = _fateController.CurrentFate?.TransformDialogueChoices; if (_fateController.IsRunning)
if (list == null)
{ {
return; List<DialogueChoice> list = _fateController.CurrentFate?.TransformDialogueChoices;
} if (list != null)
foreach (DialogueChoice item in list)
{
if (item.Type == EDialogChoiceType.YesNo)
{ {
StringOrRegex stringOrRegex = ResolveReference(null, item.ExcelSheet, item.Prompt, item.PromptIsRegularExpression); foreach (DialogueChoice item in list)
if (stringOrRegex != null && IsMatch(text, stringOrRegex))
{ {
_logger.LogInformation("FATE: Returning {YesNo} for '{Prompt}'", item.Yes ? "Yes" : "No", text); if (item.Type == EDialogChoiceType.YesNo)
addonSelectYesno->AtkUnitBase.FireCallbackInt((!item.Yes) ? 1 : 0); {
StringOrRegex stringOrRegex = ResolveReference(null, item.ExcelSheet, item.Prompt, item.PromptIsRegularExpression);
if (stringOrRegex != null && IsMatch(text, stringOrRegex))
{
_logger.LogInformation("FATE: Returning {YesNo} for '{Prompt}'", item.Yes ? "Yes" : "No", text);
addonSelectYesno->AtkUnitBase.FireCallbackInt((!item.Yes) ? 1 : 0);
return;
}
}
}
}
}
if (!_seasonalDutyController.IsRunning)
{
return;
}
List<DialogueChoice> list2 = _seasonalDutyController.CurrentDuty?.DialogueChoices;
if (list2 == null || list2.Count <= 0)
{
return;
}
foreach (DialogueChoice item2 in list2)
{
if (item2.Type == EDialogChoiceType.YesNo)
{
StringOrRegex stringOrRegex2 = ResolveReference(null, item2.ExcelSheet, item2.Prompt, item2.PromptIsRegularExpression);
if (stringOrRegex2 != null && IsMatch(text, stringOrRegex2))
{
_logger.LogInformation("Seasonal duty: Returning {YesNo} for '{Prompt}'", item2.Yes ? "Yes" : "No", text);
addonSelectYesno->AtkUnitBase.FireCallbackInt((!item2.Yes) ? 1 : 0);
break; break;
} }
} }
@ -1017,6 +1052,35 @@ internal sealed class InteractionUiController : IDisposable
} }
} }
private unsafe void EasterMowingResultPostSetup(AddonEvent type, AddonArgs args)
{
if (_seasonalDutyController.IsRunning)
{
_logger.LogInformation("Dismissing EasterMowingResult");
AtkUnitBase* address = (AtkUnitBase*)args.Addon.Address;
AtkValue* values = stackalloc AtkValue[3]
{
new AtkValue
{
Type = FFXIVClientStructs.FFXIV.Component.GUI.ValueType.Int,
Int = -1
},
new AtkValue
{
Type = FFXIVClientStructs.FFXIV.Component.GUI.ValueType.Int,
Int = 1
},
new AtkValue
{
Type = FFXIVClientStructs.FFXIV.Component.GUI.ValueType.Int,
Int = 1
}
};
address->FireCallback(3u, values);
address->Close(fireCallback: true);
}
}
private StringOrRegex? ResolveReference(Questionable.Model.Quest? quest, string? excelSheet, ExcelRef? excelRef, bool isRegExp) private StringOrRegex? ResolveReference(Questionable.Model.Quest? quest, string? excelSheet, ExcelRef? excelRef, bool isRegExp)
{ {
if (excelRef == null) if (excelRef == null)
@ -1040,6 +1104,7 @@ internal sealed class InteractionUiController : IDisposable
public void Dispose() public void Dispose()
{ {
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "EasterMowingResult", EasterMowingResultPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "HousingSelectBlock", HousingSelectBlockPostSetup); _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "HousingSelectBlock", HousingSelectBlockPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "PointMenu", PointMenuPostSetup); _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "PointMenu", PointMenuPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "DifficultySelectYesNo", DifficultySelectYesNoPostSetup); _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "DifficultySelectYesNo", DifficultySelectYesNoPostSetup);

View file

@ -54,68 +54,37 @@ internal sealed class MovementOverrideController
List<IBlacklistedLocation> list = new List<IBlacklistedLocation>(num); List<IBlacklistedLocation> list = new List<IBlacklistedLocation>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<IBlacklistedLocation> span = CollectionsMarshal.AsSpan(list); Span<IBlacklistedLocation> span = CollectionsMarshal.AsSpan(list);
int num2 = 0; span[0] = new BlacklistedArea(1191, new Vector3(-223.0412f, 31.937134f, -584.03906f), 5f, 7.75f);
span[num2] = new BlacklistedArea(1191, new Vector3(-223.0412f, 31.937134f, -584.03906f), 5f, 7.75f); span[1] = new BlacklistedPoint(128, new Vector3(2f, 40.25f, 36.5f), new Vector3(0.25f, 40.25f, 36.5f));
num2++; span[2] = new BlacklistedPoint(132, new Vector3(29f, -8f, 120.5f), new Vector3(28.265165f, -8.000001f, 120.149734f));
span[num2] = new BlacklistedPoint(128, new Vector3(2f, 40.25f, 36.5f), new Vector3(0.25f, 40.25f, 36.5f)); span[3] = new BlacklistedPoint(132, new Vector3(28.25f, -8f, 125f), new Vector3(27.372725f, -8.200001f, 125.55859f));
num2++; span[4] = new BlacklistedPoint(132, new Vector3(32.25f, -8f, 126.5f), new Vector3(32.022232f, -8.200011f, 126.86095f));
span[num2] = new BlacklistedPoint(132, new Vector3(29f, -8f, 120.5f), new Vector3(28.265165f, -8.000001f, 120.149734f)); span[5] = new BlacklistedPoint(205, new Vector3(26.75f, 0.5f, 20.75f), new Vector3(27.179117f, 0.26728272f, 19.714373f));
num2++; span[6] = new BlacklistedPoint(130, new Vector3(59.5f, 4.25f, -118f), new Vector3(60.551353f, 4f, -119.76446f));
span[num2] = new BlacklistedPoint(132, new Vector3(28.25f, -8f, 125f), new Vector3(27.372725f, -8.200001f, 125.55859f)); span[7] = new BlacklistedPoint(145, new Vector3(-139.75f, -32.25f, 75.25f), new Vector3(-139.57748f, -33.785175f, 77.87906f));
num2++; span[8] = new BlacklistedPoint(146, new Vector3(-201.75f, 10.5f, -265.5f), new Vector3(-203.75235f, 10.130764f, -265.15314f));
span[num2] = new BlacklistedPoint(132, new Vector3(32.25f, -8f, 126.5f), new Vector3(32.022232f, -8.200011f, 126.86095f)); span[9] = new BlacklistedArea(135, new Vector3(156.11499f, 15.518433f, 673.21277f), 0.5f, 5f);
num2++; span[10] = new BlacklistedPoint(139, new Vector3(366f, -2.5f, 95.5f), new Vector3(362.65973f, -3.4f, 96.6896f), 2f);
span[num2] = new BlacklistedPoint(205, new Vector3(26.75f, 0.5f, 20.75f), new Vector3(27.179117f, 0.26728272f, 19.714373f)); span[11] = new BlacklistedPoint(155, new Vector3(-478.75f, 149.25f, -305.75f), new Vector3(-476.1802f, 149.06573f, -304.7811f));
num2++; span[12] = new BlacklistedPoint(351, new Vector3(3.25f, 0.75f, 8.5f), new Vector3(4f, 0f, 9.5f));
span[num2] = new BlacklistedPoint(130, new Vector3(59.5f, 4.25f, -118f), new Vector3(60.551353f, 4f, -119.76446f)); span[13] = new BlacklistedPoint(418, new Vector3(-136.75f, 2.75f, 9f), new Vector3(-138.66408f, 2.0333426f, 8.860787f), 1f);
num2++; span[14] = new BlacklistedPoint(401, new Vector3(-14.75f, -136.75f, 515.75f), new Vector3(-17.631899f, -137.39148f, 512.6676f), 2f);
span[num2] = new BlacklistedPoint(145, new Vector3(-139.75f, -32.25f, 75.25f), new Vector3(-139.57748f, -33.785175f, 77.87906f)); span[15] = new BlacklistedPoint(397, new Vector3(-93.75f, 87.75f, -715.5f), new Vector3(-87.78183f, 87.188995f, -713.3343f), 2f);
num2++; span[16] = new BlacklistedPoint(400, new Vector3(384f, -74f, 648.75f), new Vector3(386.0543f, -72.409454f, 652.0184f), 3f);
span[num2] = new BlacklistedPoint(146, new Vector3(-201.75f, 10.5f, -265.5f), new Vector3(-203.75235f, 10.130764f, -265.15314f)); span[17] = new BlacklistedPoint(399, new Vector3(-514.4851f, 149.63762f, -480.58087f), new Vector3(-528.78656f, 151.17374f, -473.07077f), 5f, RecalculateNavmesh: true);
num2++; span[18] = new BlacklistedPoint(399, new Vector3(-534.5f, 153f, -476.75f), new Vector3(-528.78656f, 151.17374f, -473.07077f), 5f, RecalculateNavmesh: true);
span[num2] = new BlacklistedArea(135, new Vector3(156.11499f, 15.518433f, 673.21277f), 0.5f, 5f); span[19] = new BlacklistedPoint(478, new Vector3(14.5f, 215.25f, -101.5f), new Vector3(18.133032f, 215.44998f, -107.83075f), 5f);
num2++; span[20] = new BlacklistedPoint(478, new Vector3(11f, 215.5f, -104.5f), new Vector3(18.133032f, 215.44998f, -107.83075f), 5f);
span[num2] = new BlacklistedPoint(139, new Vector3(366f, -2.5f, 95.5f), new Vector3(362.65973f, -3.4f, 96.6896f), 2f); span[21] = new BlacklistedPoint(1189, new Vector3(574f, -142.25f, 504.25f), new Vector3(574.44183f, -142.12766f, 507.60065f));
num2++; span[22] = new BlacklistedPoint(814, new Vector3(-324f, 348.75f, -181.75f), new Vector3(-322.75076f, 347.0529f, -177.69328f), 3f);
span[num2] = new BlacklistedPoint(155, new Vector3(-478.75f, 149.25f, -305.75f), new Vector3(-476.1802f, 149.06573f, -304.7811f)); span[23] = new BlacklistedPoint(956, new Vector3(6.25f, -27.75f, -41.5f), new Vector3(5.0831127f, -28.213453f, -42.239136f));
num2++; span[24] = new BlacklistedPoint(1189, new Vector3(-115.75f, -213.75f, 336.5f), new Vector3(-112.40265f, -215.01514f, 339.0067f), 2f);
span[num2] = new BlacklistedPoint(351, new Vector3(3.25f, 0.75f, 8.5f), new Vector3(4f, 0f, 9.5f)); span[25] = new BlacklistedPoint(1190, new Vector3(-292.29004f, 18.598045f, -133.83907f), new Vector3(-288.20895f, 18.652182f, -132.67445f), 4f);
num2++; span[26] = new BlacklistedPoint(1191, new Vector3(-108f, 29.25f, -350.75f), new Vector3(-107.56289f, 29.008266f, -348.80087f));
span[num2] = new BlacklistedPoint(418, new Vector3(-136.75f, 2.75f, 9f), new Vector3(-138.66408f, 2.0333426f, 8.860787f), 1f); span[27] = new BlacklistedPoint(1191, new Vector3(-105.75f, 29.75f, -351f), new Vector3(-105.335304f, 29.017048f, -348.85077f));
num2++; span[28] = new BlacklistedPoint(1186, new Vector3(284.25f, 50.75f, 171.25f), new Vector3(284.25f, 50.75f, 166.25f));
span[num2] = new BlacklistedPoint(401, new Vector3(-14.75f, -136.75f, 515.75f), new Vector3(-17.631899f, -137.39148f, 512.6676f), 2f); span[29] = new BlacklistedPoint(1186, new Vector3(283.75f, 50.75f, 167.25f), new Vector3(284.25f, 50.75f, 166.25f));
num2++; span[30] = new BlacklistedPoint(1186, new Vector3(287.75f, 51.25f, 172f), new Vector3(288.875f, 50.75f, 166.25f));
span[num2] = new BlacklistedPoint(397, new Vector3(-93.75f, 87.75f, -715.5f), new Vector3(-87.78183f, 87.188995f, -713.3343f), 2f);
num2++;
span[num2] = new BlacklistedPoint(400, new Vector3(384f, -74f, 648.75f), new Vector3(386.0543f, -72.409454f, 652.0184f), 3f);
num2++;
span[num2] = new BlacklistedPoint(399, new Vector3(-514.4851f, 149.63762f, -480.58087f), new Vector3(-528.78656f, 151.17374f, -473.07077f), 5f, RecalculateNavmesh: true);
num2++;
span[num2] = new BlacklistedPoint(399, new Vector3(-534.5f, 153f, -476.75f), new Vector3(-528.78656f, 151.17374f, -473.07077f), 5f, RecalculateNavmesh: true);
num2++;
span[num2] = new BlacklistedPoint(478, new Vector3(14.5f, 215.25f, -101.5f), new Vector3(18.133032f, 215.44998f, -107.83075f), 5f);
num2++;
span[num2] = new BlacklistedPoint(478, new Vector3(11f, 215.5f, -104.5f), new Vector3(18.133032f, 215.44998f, -107.83075f), 5f);
num2++;
span[num2] = new BlacklistedPoint(1189, new Vector3(574f, -142.25f, 504.25f), new Vector3(574.44183f, -142.12766f, 507.60065f));
num2++;
span[num2] = new BlacklistedPoint(814, new Vector3(-324f, 348.75f, -181.75f), new Vector3(-322.75076f, 347.0529f, -177.69328f), 3f);
num2++;
span[num2] = new BlacklistedPoint(956, new Vector3(6.25f, -27.75f, -41.5f), new Vector3(5.0831127f, -28.213453f, -42.239136f));
num2++;
span[num2] = new BlacklistedPoint(1189, new Vector3(-115.75f, -213.75f, 336.5f), new Vector3(-112.40265f, -215.01514f, 339.0067f), 2f);
num2++;
span[num2] = new BlacklistedPoint(1190, new Vector3(-292.29004f, 18.598045f, -133.83907f), new Vector3(-288.20895f, 18.652182f, -132.67445f), 4f);
num2++;
span[num2] = new BlacklistedPoint(1191, new Vector3(-108f, 29.25f, -350.75f), new Vector3(-107.56289f, 29.008266f, -348.80087f));
num2++;
span[num2] = new BlacklistedPoint(1191, new Vector3(-105.75f, 29.75f, -351f), new Vector3(-105.335304f, 29.017048f, -348.85077f));
num2++;
span[num2] = new BlacklistedPoint(1186, new Vector3(284.25f, 50.75f, 171.25f), new Vector3(284.25f, 50.75f, 166.25f));
num2++;
span[num2] = new BlacklistedPoint(1186, new Vector3(283.75f, 50.75f, 167.25f), new Vector3(284.25f, 50.75f, 166.25f));
num2++;
span[num2] = new BlacklistedPoint(1186, new Vector3(287.75f, 51.25f, 172f), new Vector3(288.875f, 50.75f, 166.25f));
BlacklistedLocations = list; BlacklistedLocations = list;
} }
} }

View file

@ -22,6 +22,10 @@ internal static class Action
{ {
return Array.Empty<ITask>(); return Array.Empty<ITask>();
} }
if (step.DataIds.Count > 0)
{
return Array.Empty<ITask>();
}
ArgumentNullException.ThrowIfNull(step.Action, "step.Action"); ArgumentNullException.ThrowIfNull(step.Action, "step.Action");
ITask task = OnObject(step.DataId, quest, step.Action.Value, step.CompletionQuestVariablesFlags); ITask task = OnObject(step.DataId, quest, step.Action.Value, step.CompletionQuestVariablesFlags);
if (step.Action.Value.RequiresMount()) if (step.Action.Value.RequiresMount())
@ -86,6 +90,9 @@ internal static class Action
_continueAt = DateTime.Now.AddSeconds(0.5); _continueAt = DateTime.Now.AddSeconds(0.5);
return true; return true;
} }
logger.LogInformation("Object {DataId} is untargetable, using action {Action} without target", base.Task.DataId, base.Task.Action);
_usedAction = gameFunctions.UseAction(base.Task.Action);
_continueAt = DateTime.Now.AddSeconds(0.5);
return true; return true;
} }
_usedAction = gameFunctions.UseAction(base.Task.Action); _usedAction = gameFunctions.UseAction(base.Task.Action);
@ -104,11 +111,18 @@ internal static class Action
if (base.Task.DataId.HasValue) if (base.Task.DataId.HasValue)
{ {
IGameObject gameObject = gameFunctions.FindObjectByDataId(base.Task.DataId.Value); IGameObject gameObject = gameFunctions.FindObjectByDataId(base.Task.DataId.Value);
if (gameObject == null || !gameObject.IsTargetable) if (gameObject == null)
{ {
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
} }
_usedAction = gameFunctions.UseAction(gameObject, base.Task.Action); if (gameObject.IsTargetable)
{
_usedAction = gameFunctions.UseAction(gameObject, base.Task.Action);
}
else
{
_usedAction = gameFunctions.UseAction(base.Task.Action);
}
_continueAt = DateTime.Now.AddSeconds(0.5); _continueAt = DateTime.Now.AddSeconds(0.5);
} }
else else

View file

@ -0,0 +1,347 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using Microsoft.Extensions.Logging;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Interactions;
internal static class ClearObjectsWithAction
{
internal sealed class Factory : ITaskFactory
{
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{
if (step.InteractionType != EInteractionType.Action)
{
return Array.Empty<ITask>();
}
if (step.DataIds.Count == 0)
{
return Array.Empty<ITask>();
}
if (!step.Action.HasValue)
{
return Array.Empty<ITask>();
}
return new global::_003C_003Ez__ReadOnlySingleElementList<ITask>(new ClearTask(step.DataIds, step.Action.Value, step.TerritoryId, step.CalculateActualStopDistance(), step.WaypointPositions));
}
}
internal sealed record ClearTask(List<uint> DataIds, EAction Action, ushort TerritoryId, float StopDistance, List<Vector3> WaypointPositions) : ITask
{
public bool ShouldRedoOnInterrupt()
{
return true;
}
public override string ToString()
{
return $"ClearObjects({Action}, {DataIds.Count} types)";
}
}
internal sealed class ClearTaskExecutor(MovementController movementController, GameFunctions gameFunctions, IClientState clientState, IObjectTable objectTable, ILogger<ClearTaskExecutor> logger) : TaskExecutor<ClearTask>()
{
private enum State
{
FindingObject,
MovingToObject,
UsingAction,
WaitingAfterAction,
MovingToWaypoint,
WaitingForTransition
}
private const float VisitedPositionRadius = 5f;
private State _state;
private DateTime _noObjectFoundSince = DateTime.MaxValue;
private DateTime _waitUntil = DateTime.MinValue;
private DateTime _waypointReachedAt = DateTime.MaxValue;
private Vector3 _lastPlayerPosition;
private readonly List<Vector3> _visitedPositions = new List<Vector3>();
private readonly List<int> _triedWaypointIndices = new List<int>();
protected override bool Start()
{
if (clientState.TerritoryType != base.Task.TerritoryId)
{
return true;
}
return TryFindAndMove();
}
private bool IsVisitedPosition(Vector3 position)
{
foreach (Vector3 visitedPosition in _visitedPositions)
{
if (Vector3.Distance(visitedPosition, position) <= 5f)
{
return true;
}
}
return false;
}
private IGameObject? FindNearestUnvisitedObject()
{
IGameObject gameObject = objectTable[0];
if (gameObject == null)
{
return null;
}
IGameObject result = null;
float num = float.MaxValue;
foreach (IGameObject item in objectTable)
{
ObjectKind objectKind = item.ObjectKind;
if ((objectKind == ObjectKind.Player || objectKind - 8 <= ObjectKind.BattleNpc || objectKind == ObjectKind.Housing) ? true : false)
{
continue;
}
bool flag = false;
foreach (uint dataId in base.Task.DataIds)
{
if (item.BaseId == dataId)
{
flag = true;
break;
}
}
if (flag && !IsVisitedPosition(item.Position))
{
float num2 = Vector3.Distance(gameObject.Position, item.Position);
if (num2 < num)
{
result = item;
num = num2;
}
}
}
return result;
}
private bool AnyMatchingObjectsExist()
{
foreach (IGameObject item in objectTable)
{
ObjectKind objectKind = item.ObjectKind;
if ((objectKind == ObjectKind.Player || objectKind - 8 <= ObjectKind.BattleNpc || objectKind == ObjectKind.Housing) ? true : false)
{
continue;
}
foreach (uint dataId in base.Task.DataIds)
{
if (item.BaseId == dataId)
{
return true;
}
}
}
return false;
}
private bool TryFindAndMove()
{
IGameObject gameObject = FindNearestUnvisitedObject();
if (gameObject == null)
{
_state = State.FindingObject;
if (_noObjectFoundSince == DateTime.MaxValue)
{
_noObjectFoundSince = DateTime.Now;
}
return true;
}
_noObjectFoundSince = DateTime.MaxValue;
Vector3 position = gameObject.Position;
logger.LogInformation("Moving to object {DataId} at {Position}", gameObject.BaseId, position);
movementController.NavigateTo(EMovementType.Quest, gameObject.BaseId, position, fly: false, sprint: true, base.Task.StopDistance);
_state = State.MovingToObject;
return true;
}
private (Vector3 Position, int Index)? FindNextWaypoint()
{
IGameObject gameObject = objectTable[0];
if (gameObject == null || base.Task.WaypointPositions.Count == 0)
{
return null;
}
(Vector3, int)? result = null;
float num = float.MaxValue;
for (int i = 0; i < base.Task.WaypointPositions.Count; i++)
{
if (!_triedWaypointIndices.Contains(i))
{
float num2 = Vector3.Distance(gameObject.Position, base.Task.WaypointPositions[i]);
if (num2 < num)
{
result = (base.Task.WaypointPositions[i], i);
num = num2;
}
}
}
return result;
}
private void NavigateToNextWaypoint()
{
(Vector3, int)? tuple = FindNextWaypoint();
if (!tuple.HasValue)
{
logger.LogInformation("All waypoints tried without transition, resetting");
_triedWaypointIndices.Clear();
tuple = FindNextWaypoint();
if (!tuple.HasValue)
{
logger.LogWarning("No waypoint positions configured, waiting for transition");
_state = State.WaitingForTransition;
_lastPlayerPosition = objectTable[0]?.Position ?? Vector3.Zero;
return;
}
}
_triedWaypointIndices.Add(tuple.Value.Item2);
logger.LogInformation("Moving to waypoint {Index} at {Position}", tuple.Value.Item2, tuple.Value.Item1);
movementController.NavigateTo(EMovementType.Quest, null, tuple.Value.Item1, fly: false, sprint: true, 0f);
_state = State.MovingToWaypoint;
}
public override ETaskResult Update()
{
if (clientState.TerritoryType != base.Task.TerritoryId)
{
return ETaskResult.TaskComplete;
}
switch (_state)
{
case State.FindingObject:
if (_noObjectFoundSince != DateTime.MaxValue && DateTime.Now > _noObjectFoundSince.AddSeconds(3.0))
{
if (AnyMatchingObjectsExist())
{
logger.LogInformation("Matching objects still exist, clearing visited positions and retrying");
_visitedPositions.Clear();
_noObjectFoundSince = DateTime.MaxValue;
}
else
{
logger.LogInformation("No matching objects remain, moving to waypoint");
NavigateToNextWaypoint();
}
return ETaskResult.StillRunning;
}
TryFindAndMove();
return ETaskResult.StillRunning;
case State.MovingToObject:
{
if (movementController.IsPathfinding || movementController.IsPathRunning)
{
return ETaskResult.StillRunning;
}
DateTime movementStartedAt = movementController.MovementStartedAt;
if (movementStartedAt == DateTime.MaxValue || movementStartedAt.AddSeconds(2.0) >= DateTime.Now)
{
return ETaskResult.StillRunning;
}
IGameObject gameObject2 = FindNearestUnvisitedObject();
IGameObject gameObject3 = objectTable[0];
if (gameObject2 != null && gameObject3 != null && Vector3.Distance(gameObject3.Position, gameObject2.Position) <= 5f)
{
_visitedPositions.Add(gameObject2.Position);
_state = State.UsingAction;
}
else
{
logger.LogInformation("Target no longer valid at destination, finding next");
TryFindAndMove();
}
return ETaskResult.StillRunning;
}
case State.UsingAction:
if (gameFunctions.UseAction(base.Task.Action))
{
logger.LogInformation("Used action {Action}", base.Task.Action);
_waitUntil = DateTime.Now.AddSeconds(1.0);
_state = State.WaitingAfterAction;
}
return ETaskResult.StillRunning;
case State.WaitingAfterAction:
if (DateTime.Now < _waitUntil)
{
return ETaskResult.StillRunning;
}
TryFindAndMove();
return ETaskResult.StillRunning;
case State.MovingToWaypoint:
{
if (movementController.IsPathfinding || movementController.IsPathRunning)
{
return ETaskResult.StillRunning;
}
DateTime movementStartedAt2 = movementController.MovementStartedAt;
if (movementStartedAt2 == DateTime.MaxValue || movementStartedAt2.AddSeconds(2.0) >= DateTime.Now)
{
return ETaskResult.StillRunning;
}
logger.LogInformation("Reached waypoint, waiting for zone transition");
_state = State.WaitingForTransition;
_lastPlayerPosition = objectTable[0]?.Position ?? Vector3.Zero;
_waypointReachedAt = DateTime.Now;
return ETaskResult.StillRunning;
}
case State.WaitingForTransition:
{
IGameObject gameObject = objectTable[0];
if (gameObject != null)
{
float num = Vector3.Distance(_lastPlayerPosition, gameObject.Position);
if (num > 50f)
{
logger.LogInformation("Zone transition detected (moved {Distance}), resuming plant clearing", num);
_visitedPositions.Clear();
_triedWaypointIndices.Clear();
_noObjectFoundSince = DateTime.MaxValue;
_state = State.FindingObject;
return ETaskResult.StillRunning;
}
}
if (FindNearestUnvisitedObject() != null)
{
logger.LogInformation("New objects detected, resuming plant clearing");
_visitedPositions.Clear();
_triedWaypointIndices.Clear();
_noObjectFoundSince = DateTime.MaxValue;
TryFindAndMove();
return ETaskResult.StillRunning;
}
if (_waypointReachedAt != DateTime.MaxValue && DateTime.Now > _waypointReachedAt.AddSeconds(3.0))
{
logger.LogInformation("No transition at this waypoint, trying next");
NavigateToNextWaypoint();
}
return ETaskResult.StillRunning;
}
default:
return ETaskResult.TaskComplete;
}
}
public override bool ShouldInterruptOnDamage()
{
return false;
}
}
}

View file

@ -130,10 +130,8 @@ internal static class Dive
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; span[0] = GetKeysToPress(key, keyModifier);
span[num2] = GetKeysToPress(key, keyModifier); span[1] = GetKeysToPress(key2, keyModifier2);
num2++;
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);
@ -144,12 +142,12 @@ internal static class Dive
foreach (nint item in list2) foreach (nint item in list2)
{ {
_keysToPress.Enqueue((256u, item)); _keysToPress.Enqueue((256u, item));
for (int num3 = 0; num3 < 15; num3++) for (int num2 = 0; num2 < 15; num2++)
{ {
_keysToPress.Enqueue((0u, 0)); _keysToPress.Enqueue((0u, 0));
} }
} }
for (int num4 = 0; num4 < 5; num4++) for (int num3 = 0; num3 < 5; num3++)
{ {
_keysToPress.Enqueue((0u, 0)); _keysToPress.Enqueue((0u, 0));
} }

View file

@ -175,12 +175,10 @@ internal static class EquipItem
case 10u: case 10u:
case 11u: case 11u:
{ {
int index = 1; int num = 1;
List<ushort> list4 = new List<ushort>(index); List<ushort> list4 = new List<ushort>(num);
CollectionsMarshal.SetCount(list4, index); CollectionsMarshal.SetCount(list4, num);
Span<ushort> span4 = CollectionsMarshal.AsSpan(list4); CollectionsMarshal.AsSpan(list4)[0] = (ushort)(item.Value.EquipSlotCategory.RowId - 1);
int num = 0;
span4[num] = (ushort)(item.Value.EquipSlotCategory.RowId - 1);
return list4; return list4;
} }
case 12u: case 12u:
@ -188,21 +186,17 @@ internal static class EquipItem
int num = 2; int num = 2;
List<ushort> list3 = new List<ushort>(num); List<ushort> list3 = new List<ushort>(num);
CollectionsMarshal.SetCount(list3, num); CollectionsMarshal.SetCount(list3, num);
Span<ushort> span3 = CollectionsMarshal.AsSpan(list3); Span<ushort> span = CollectionsMarshal.AsSpan(list3);
int index = 0; span[0] = 11;
span3[index] = 11; span[1] = 12;
index++;
span3[index] = 12;
return list3; return list3;
} }
case 13u: case 13u:
{ {
int index = 1; int num = 1;
List<ushort> list2 = new List<ushort>(index); List<ushort> list2 = new List<ushort>(num);
CollectionsMarshal.SetCount(list2, index); CollectionsMarshal.SetCount(list2, num);
Span<ushort> span2 = CollectionsMarshal.AsSpan(list2); CollectionsMarshal.AsSpan(list2)[0] = 0;
int num = 0;
span2[num] = 0;
return list2; return list2;
} }
case 17u: case 17u:
@ -210,9 +204,7 @@ internal static class EquipItem
int num = 1; int num = 1;
List<ushort> list = new List<ushort>(num); List<ushort> list = new List<ushort>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<ushort> span = CollectionsMarshal.AsSpan(list); CollectionsMarshal.AsSpan(list)[0] = 13;
int index = 0;
span[index] = 13;
return list; return list;
} }
default: default:

View file

@ -120,6 +120,8 @@ internal static class FateFarming
private ushort _trackedFateId; private ushort _trackedFateId;
private bool _fateWasActive;
protected override bool Start() protected override bool Start()
{ {
_trackedFateId = gameFunctions.GetCurrentFateId(); _trackedFateId = gameFunctions.GetCurrentFateId();
@ -138,6 +140,13 @@ internal static class FateFarming
logger.LogInformation("Required status {StatusId} lost during FATE action loop, ending cycle to re-apply", base.Task.RequiredStatusId.Value); logger.LogInformation("Required status {StatusId} lost during FATE action loop, ending cycle to re-apply", base.Task.RequiredStatusId.Value);
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
} }
bool flag = gameFunctions.GetCurrentFateId() != 0;
if (_fateWasActive && !flag)
{
logger.LogInformation("FATE {FateId} is no longer active, cycle complete", _trackedFateId);
return ETaskResult.TaskComplete;
}
_fateWasActive = flag;
if (_trackedFateId == 0) if (_trackedFateId == 0)
{ {
_trackedFateId = gameFunctions.GetCurrentFateId(); _trackedFateId = gameFunctions.GetCurrentFateId();
@ -156,8 +165,8 @@ internal static class FateFarming
IGameObject gameObject = gameFunctions.FindObjectByDataId(target.DataId, null, warnIfMissing: false); IGameObject gameObject = gameFunctions.FindObjectByDataId(target.DataId, null, warnIfMissing: false);
if (gameObject != null && gameObject.IsTargetable) if (gameObject != null && gameObject.IsTargetable)
{ {
bool flag = gameFunctions.UseAction(gameObject, target.Action); bool flag2 = gameFunctions.UseAction(gameObject, target.Action);
_nextActionAt = (flag ? DateTime.Now.AddSeconds(2.5) : DateTime.Now.AddSeconds(0.5)); _nextActionAt = (flag2 ? DateTime.Now.AddSeconds(2.5) : DateTime.Now.AddSeconds(0.5));
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
} }
} }

View file

@ -67,9 +67,7 @@ internal static class Jump
int num2 = 1; int num2 = 1;
List<Vector3> list = new List<Vector3>(num2); List<Vector3> list = new List<Vector3>(num2);
CollectionsMarshal.SetCount(list, num2); CollectionsMarshal.SetCount(list, num2);
Span<Vector3> span = CollectionsMarshal.AsSpan(list); CollectionsMarshal.AsSpan(list)[0] = base.Task.JumpDestination.Position;
int index = 0;
span[index] = base.Task.JumpDestination.Position;
movementController.NavigateTo(EMovementType.Quest, dataId, list, fly: false, sprint: false, base.Task.JumpDestination.StopDistance ?? num); movementController.NavigateTo(EMovementType.Quest, dataId, list, fly: false, sprint: false, base.Task.JumpDestination.StopDistance ?? num);
_003Cframework_003EP.RunOnTick(delegate _003Cframework_003EP.RunOnTick(delegate
{ {

View file

@ -87,9 +87,7 @@ internal sealed class MoveExecutor : TaskExecutor<MoveTask>, IToastAware, ITaskE
int num = 1; int num = 1;
List<Vector3> list = new List<Vector3>(num); List<Vector3> list = new List<Vector3>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<Vector3> span = CollectionsMarshal.AsSpan(list); CollectionsMarshal.AsSpan(list)[0] = _destination;
int index = 0;
span[index] = _destination;
movementController.NavigateTo(EMovementType.Quest, dataId, list, base.Task.Fly, base.Task.Sprint ?? (!_mountDuringMovement.HasValue), base.Task.StopDistance, base.Task.IgnoreDistanceToObject ? new float?(float.MaxValue) : ((float?)null), base.Task.Land); movementController.NavigateTo(EMovementType.Quest, dataId, list, base.Task.Fly, base.Task.Sprint ?? (!_mountDuringMovement.HasValue), base.Task.StopDistance, base.Task.IgnoreDistanceToObject ? new float?(float.MaxValue) : ((float?)null), base.Task.Land);
}; };
} }

View file

@ -18,6 +18,10 @@ internal static class MoveTo
{ {
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{ {
if (step.DataIds.Count > 0)
{
return Array.Empty<ITask>();
}
if (step.Position.HasValue) if (step.Position.HasValue)
{ {
return CreateMoveTasks(step, step.Position.Value); return CreateMoveTasks(step, step.Position.Value);
@ -26,6 +30,10 @@ internal static class MoveTo
{ {
return new global::_003C_003Ez__ReadOnlySingleElementList<ITask>(new WaitForNearDataId(step.DataId.Value, step.StopDistance.Value)); return new global::_003C_003Ez__ReadOnlySingleElementList<ITask>(new WaitForNearDataId(step.DataId.Value, step.StopDistance.Value));
} }
if (step != null && step.DataId.HasValue && !step.Position.HasValue)
{
return CreateMoveToObjectTasks(step);
}
EAetheryteLocation valueOrDefault = default(EAetheryteLocation); EAetheryteLocation valueOrDefault = default(EAetheryteLocation);
bool flag; bool flag;
if (step != null) if (step != null)
@ -38,13 +46,13 @@ internal static class MoveTo
{ {
valueOrDefault = aetheryte.GetValueOrDefault(); valueOrDefault = aetheryte.GetValueOrDefault();
flag = true; flag = true;
goto IL_00a3; goto IL_00e2;
} }
} }
} }
flag = false; flag = false;
goto IL_00a3; goto IL_00e2;
IL_00a3: IL_00e2:
if (flag) if (flag)
{ {
return CreateMoveTasks(step, aetheryteData.Locations[valueOrDefault]); return CreateMoveTasks(step, aetheryteData.Locations[valueOrDefault]);
@ -61,6 +69,16 @@ internal static class MoveTo
return Array.Empty<ITask>(); return Array.Empty<ITask>();
} }
private IEnumerable<ITask> CreateMoveToObjectTasks(QuestStep step)
{
yield return new WaitCondition.Task(() => clientState.TerritoryType == step.TerritoryId, "Wait(territory: " + territoryData.GetNameAndId(step.TerritoryId) + ")");
if (!step.DisableNavmesh)
{
yield return new WaitNavmesh.Task();
}
yield return new MoveToObject(step);
}
private IEnumerable<ITask> CreateMoveTasks(QuestStep step, Vector3 destination) private IEnumerable<ITask> CreateMoveTasks(QuestStep step, Vector3 destination)
{ {
IGameObject gameObject = objectTable[0]; IGameObject gameObject = objectTable[0];

View file

@ -0,0 +1,16 @@
using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Movement;
internal sealed record MoveToObject(QuestStep Step) : ITask
{
public bool ShouldRedoOnInterrupt()
{
return true;
}
public override string ToString()
{
return $"MoveToObject({Step.DataId})";
}
}

View file

@ -0,0 +1,127 @@
using System;
using System.Numerics;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using Microsoft.Extensions.Logging;
using Questionable.Functions;
using Questionable.Model;
namespace Questionable.Controller.Steps.Movement;
internal sealed class MoveToObjectExecutor(MovementController movementController, GameFunctions gameFunctions, IClientState clientState, IObjectTable objectTable, ILogger<MoveToObjectExecutor> logger) : TaskExecutor<MoveToObject>()
{
private DateTime _startedLookingAt = DateTime.MaxValue;
private Vector3 _destination;
private bool _moving;
protected override bool Start()
{
if (!base.Task.Step.DataId.HasValue)
{
logger.LogWarning("MoveToObject task has no DataId");
return false;
}
if (clientState.TerritoryType != base.Task.Step.TerritoryId)
{
logger.LogInformation("Not in correct territory yet, waiting");
return true;
}
_startedLookingAt = DateTime.Now;
return TryStartMovement();
}
private IGameObject? FindDistantObjectByDataId(uint dataId)
{
IGameObject gameObject = objectTable[0];
if (gameObject == null)
{
return null;
}
float num = base.Task.Step.CalculateActualStopDistance();
IGameObject result = null;
float num2 = float.MaxValue;
foreach (IGameObject item in objectTable)
{
ObjectKind objectKind = item.ObjectKind;
bool flag = ((objectKind == ObjectKind.Player || objectKind - 8 <= ObjectKind.BattleNpc || objectKind == ObjectKind.Housing) ? true : false);
if (!flag && item.BaseId == dataId)
{
float num3 = Vector3.Distance(gameObject.Position, item.Position);
if (!(num3 <= num) && num3 < num2)
{
result = item;
num2 = num3;
}
}
}
return result;
}
private bool TryStartMovement()
{
IGameObject gameObject = FindDistantObjectByDataId(base.Task.Step.DataId.Value);
if (gameObject == null)
{
gameObject = gameFunctions.FindObjectByDataId(base.Task.Step.DataId.Value, null, warnIfMissing: false);
if (gameObject == null)
{
logger.LogInformation("Object {DataId} not found yet, waiting", base.Task.Step.DataId);
return true;
}
IGameObject gameObject2 = objectTable[0];
float num = base.Task.Step.CalculateActualStopDistance();
if (gameObject2 != null && Vector3.Distance(gameObject2.Position, gameObject.Position) <= num)
{
logger.LogInformation("Only nearby objects with DataId {DataId} remain, skipping", base.Task.Step.DataId);
return true;
}
}
_destination = gameObject.Position;
logger.LogInformation("Moving to object {DataId} at {Position}", base.Task.Step.DataId, _destination);
movementController.NavigateTo(EMovementType.Quest, base.Task.Step.DataId, _destination, fly: false, base.Task.Step.Sprint ?? true, base.Task.Step.CalculateActualStopDistance());
_moving = true;
return true;
}
public override ETaskResult Update()
{
if (clientState.TerritoryType != base.Task.Step.TerritoryId)
{
return ETaskResult.StillRunning;
}
if (!_moving)
{
if (_startedLookingAt != DateTime.MaxValue && DateTime.Now > _startedLookingAt.AddSeconds(3.0))
{
logger.LogInformation("Object {DataId} not found after timeout, skipping step", base.Task.Step.DataId);
return ETaskResult.TaskComplete;
}
if (!TryStartMovement())
{
return ETaskResult.TaskComplete;
}
if (!_moving)
{
return ETaskResult.StillRunning;
}
}
if (movementController.IsPathfinding || movementController.IsPathRunning)
{
return ETaskResult.StillRunning;
}
DateTime movementStartedAt = movementController.MovementStartedAt;
if (movementStartedAt == DateTime.MaxValue || movementStartedAt.AddSeconds(2.0) >= DateTime.Now)
{
return ETaskResult.StillRunning;
}
return ETaskResult.TaskComplete;
}
public override bool ShouldInterruptOnDamage()
{
return false;
}
}

View file

@ -119,14 +119,10 @@ internal static class AethernetShortcut
List<Vector3> list = new List<Vector3>(num); List<Vector3> list = new List<Vector3>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<Vector3> span = CollectionsMarshal.AsSpan(list); Span<Vector3> span = CollectionsMarshal.AsSpan(list);
int num2 = 0; span[0] = new Vector3(0f, 8.442986f, 9f);
span[num2] = new Vector3(0f, 8.442986f, 9f); span[1] = new Vector3(9f, 8.442986f, 0f);
num2++; span[2] = new Vector3(-9f, 8.442986f, 0f);
span[num2] = new Vector3(9f, 8.442986f, 0f); span[3] = new Vector3(0f, 8.442986f, -9f);
num2++;
span[num2] = new Vector3(-9f, 8.442986f, 0f);
num2++;
span[num2] = new Vector3(0f, 8.442986f, -9f);
Vector3 to = list.MinBy((Vector3 x) => Vector3.Distance(playerPosition, x)); Vector3 to = list.MinBy((Vector3 x) => Vector3.Distance(playerPosition, x));
_moving = true; _moving = true;
movementController.NavigateTo(EMovementType.Quest, (uint)base.Task.From, to, fly: false, sprint: true, 0.25f); movementController.NavigateTo(EMovementType.Quest, (uint)base.Task.From, to, fly: false, sprint: true, 0.25f);

View file

@ -257,14 +257,10 @@ internal static class AetheryteShortcut
List<Vector3> list = new List<Vector3>(num); List<Vector3> list = new List<Vector3>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<Vector3> span = CollectionsMarshal.AsSpan(list); Span<Vector3> span = CollectionsMarshal.AsSpan(list);
int num2 = 0; span[0] = new Vector3(0f, 8.8f, 15.5f);
span[num2] = new Vector3(0f, 8.8f, 15.5f); span[1] = new Vector3(0f, 8.8f, -15.5f);
num2++; span[2] = new Vector3(15.5f, 8.8f, 0f);
span[num2] = new Vector3(0f, 8.8f, -15.5f); span[3] = new Vector3(-15.5f, 8.8f, 0f);
num2++;
span[num2] = new Vector3(15.5f, 8.8f, 0f);
num2++;
span[num2] = new Vector3(-15.5f, 8.8f, 0f);
dictionary.Add(EAetheryteLocation.SolutionNine, list); dictionary.Add(EAetheryteLocation.SolutionNine, list);
AetherytesToMoveFrom = dictionary; AetherytesToMoveFrom = dictionary;
} }

View file

@ -43,19 +43,15 @@ internal sealed class TaskCreator
int num = 1; int num = 1;
List<ITask> list = new List<ITask>(num); List<ITask> list = new List<ITask>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<ITask> span = CollectionsMarshal.AsSpan(list); CollectionsMarshal.AsSpan(list)[0] = new WaitAtEnd.WaitNextStepOrSequence();
int index = 0;
span[index] = new WaitAtEnd.WaitNextStepOrSequence();
list2 = list; list2 = list;
} }
else if (step == null) else if (step == null)
{ {
int index = 1; int num = 1;
List<ITask> list3 = new List<ITask>(index); List<ITask> list3 = new List<ITask>(num);
CollectionsMarshal.SetCount(list3, index); CollectionsMarshal.SetCount(list3, num);
Span<ITask> span2 = CollectionsMarshal.AsSpan(list3); CollectionsMarshal.AsSpan(list3)[0] = new WaitAtEnd.WaitNextStepOrSequence();
int num = 0;
span2[num] = new WaitAtEnd.WaitNextStepOrSequence();
list2 = list3; list2 = list3;
} }
else else

View file

@ -515,9 +515,7 @@ internal sealed class CombatController : IDisposable
int num4 = 1; int num4 = 1;
List<Vector3> list = new List<Vector3>(num4); List<Vector3> list = new List<Vector3>(num4);
CollectionsMarshal.SetCount(list, num4); CollectionsMarshal.SetCount(list, num4);
Span<Vector3> span = CollectionsMarshal.AsSpan(list); CollectionsMarshal.AsSpan(list)[0] = gameObject.Position;
int index = 0;
span[index] = gameObject.Position;
movementController.NavigateTo(EMovementType.Combat, null, list, fly: false, sprint: false, num3 + num - 0.25f, float.MaxValue); movementController.NavigateTo(EMovementType.Combat, null, list, fly: false, sprint: false, num3 + num - 0.25f, float.MaxValue);
} }
else else

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Runtime.CompilerServices;
using Dalamud.Game.Command; using Dalamud.Game.Command;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Questionable.Functions; using Questionable.Functions;
@ -36,8 +37,12 @@ internal sealed class CommandHandler : IDisposable
private readonly FateController _fateController; private readonly FateController _fateController;
private readonly SeasonalDutyController _seasonalDutyController;
private readonly FateSelectionWindow _fateSelectionWindow; private readonly FateSelectionWindow _fateSelectionWindow;
private readonly SeasonalDutySelectionWindow _seasonalDutySelectionWindow;
private readonly ITargetManager _targetManager; private readonly ITargetManager _targetManager;
private readonly IClientState _clientState; private readonly IClientState _clientState;
@ -46,13 +51,14 @@ internal sealed class CommandHandler : IDisposable
private readonly ChangelogWindow _changelogWindow; private readonly ChangelogWindow _changelogWindow;
public CommandHandler(ICommandManager commandManager, IChatGui chatGui, QuestController questController, MovementController movementController, FateController fateController, QuestRegistry questRegistry, ConfigWindow configWindow, DebugOverlay debugOverlay, OneTimeSetupWindow oneTimeSetupWindow, QuestWindow questWindow, QuestSelectionWindow questSelectionWindow, QuestSequenceWindow questSequenceWindow, JournalProgressWindow journalProgressWindow, PriorityWindow priorityWindow, FateSelectionWindow fateSelectionWindow, ITargetManager targetManager, QuestFunctions questFunctions, GameFunctions gameFunctions, AetheryteFunctions aetheryteFunctions, IDataManager dataManager, IClientState clientState, IObjectTable objectTable, Configuration configuration, ChangelogWindow changelogWindow) public CommandHandler(ICommandManager commandManager, IChatGui chatGui, QuestController questController, MovementController movementController, FateController fateController, SeasonalDutyController seasonalDutyController, QuestRegistry questRegistry, ConfigWindow configWindow, DebugOverlay debugOverlay, OneTimeSetupWindow oneTimeSetupWindow, QuestWindow questWindow, QuestSelectionWindow questSelectionWindow, QuestSequenceWindow questSequenceWindow, JournalProgressWindow journalProgressWindow, PriorityWindow priorityWindow, FateSelectionWindow fateSelectionWindow, SeasonalDutySelectionWindow seasonalDutySelectionWindow, ITargetManager targetManager, QuestFunctions questFunctions, GameFunctions gameFunctions, AetheryteFunctions aetheryteFunctions, IDataManager dataManager, IClientState clientState, IObjectTable objectTable, Configuration configuration, ChangelogWindow changelogWindow)
{ {
_commandManager = commandManager; _commandManager = commandManager;
_chatGui = chatGui; _chatGui = chatGui;
_questController = questController; _questController = questController;
_movementController = movementController; _movementController = movementController;
_fateController = fateController; _fateController = fateController;
_seasonalDutyController = seasonalDutyController;
_configWindow = configWindow; _configWindow = configWindow;
_oneTimeSetupWindow = oneTimeSetupWindow; _oneTimeSetupWindow = oneTimeSetupWindow;
_questWindow = questWindow; _questWindow = questWindow;
@ -61,6 +67,7 @@ internal sealed class CommandHandler : IDisposable
_journalProgressWindow = journalProgressWindow; _journalProgressWindow = journalProgressWindow;
_priorityWindow = priorityWindow; _priorityWindow = priorityWindow;
_fateSelectionWindow = fateSelectionWindow; _fateSelectionWindow = fateSelectionWindow;
_seasonalDutySelectionWindow = seasonalDutySelectionWindow;
_targetManager = targetManager; _targetManager = targetManager;
_clientState = clientState; _clientState = clientState;
_configuration = configuration; _configuration = configuration;
@ -69,15 +76,15 @@ internal sealed class CommandHandler : IDisposable
ICommandManager commandManager2 = _commandManager; ICommandManager commandManager2 = _commandManager;
CommandInfo commandInfo = new CommandInfo(ProcessCommand); CommandInfo commandInfo = new CommandInfo(ProcessCommand);
string separator = Environment.NewLine + "\t"; string separator = Environment.NewLine + "\t";
_003C_003Ey__InlineArray7<string> buffer = default(_003C_003Ey__InlineArray7<string>); InlineArray7<string> buffer = default(InlineArray7<string>);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray7<string>, string>(ref buffer, 0) = "Opens the Questing window"; global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<InlineArray7<string>, string>(ref buffer, 0) = "Opens the Questing window";
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray7<string>, string>(ref buffer, 1) = "/qst help - displays simplified commands"; global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<InlineArray7<string>, string>(ref buffer, 1) = "/qst help - displays simplified commands";
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray7<string>, string>(ref buffer, 2) = "/qst help-all - displays all available commands"; global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<InlineArray7<string>, string>(ref buffer, 2) = "/qst help-all - displays all available commands";
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray7<string>, string>(ref buffer, 3) = "/qst config - opens the configuration window"; global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<InlineArray7<string>, string>(ref buffer, 3) = "/qst config - opens the configuration window";
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray7<string>, string>(ref buffer, 4) = "/qst changelog - opens the changelog window"; global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<InlineArray7<string>, string>(ref buffer, 4) = "/qst changelog - opens the changelog window";
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray7<string>, string>(ref buffer, 5) = "/qst start - starts doing quests"; global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<InlineArray7<string>, string>(ref buffer, 5) = "/qst start - starts doing quests";
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray7<string>, string>(ref buffer, 6) = "/qst stop - stops doing quests"; global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<InlineArray7<string>, string>(ref buffer, 6) = "/qst stop - stops doing quests";
commandInfo.HelpMessage = string.Join(separator, global::_003CPrivateImplementationDetails_003E.InlineArrayAsReadOnlySpan<_003C_003Ey__InlineArray7<string>, string>(in buffer, 7)); commandInfo.HelpMessage = string.Join(separator, global::_003CPrivateImplementationDetails_003E.InlineArrayAsReadOnlySpan<InlineArray7<string>, string>(in buffer, 7));
commandManager2.AddHandler("/qst", commandInfo); commandManager2.AddHandler("/qst", commandInfo);
} }
@ -99,7 +106,7 @@ internal sealed class CommandHandler : IDisposable
_chatGui.Print("/qst start - starts doing quests", "Questionable", 576); _chatGui.Print("/qst start - starts doing quests", "Questionable", 576);
_chatGui.Print("/qst stop - stops doing quests", "Questionable", 576); _chatGui.Print("/qst stop - stops doing quests", "Questionable", 576);
_chatGui.Print("/qst reload - reload all quest data", "Questionable", 576); _chatGui.Print("/qst reload - reload all quest data", "Questionable", 576);
_chatGui.Print("/qst fate - toggles the FATE farming window", "Questionable", 576); _chatGui.Print("/qst fate / duty - toggles the farming window", "Questionable", 576);
break; break;
case "ha": case "ha":
case "help-all": case "help-all":
@ -116,7 +123,7 @@ internal sealed class CommandHandler : IDisposable
_chatGui.Print("/qst zone - shows all quests starting in the current zone (only includes quests with a known quest path, and currently visible unaccepted quests)", "Questionable", 576); _chatGui.Print("/qst zone - shows all quests starting in the current zone (only includes quests with a known quest path, and currently visible unaccepted quests)", "Questionable", 576);
_chatGui.Print("/qst journal - toggles the Journal Progress window", "Questionable", 576); _chatGui.Print("/qst journal - toggles the Journal Progress window", "Questionable", 576);
_chatGui.Print("/qst priority - toggles the Priority window", "Questionable", 576); _chatGui.Print("/qst priority - toggles the Priority window", "Questionable", 576);
_chatGui.Print("/qst fate - toggles the FATE farming window", "Questionable", 576); _chatGui.Print("/qst fate / duty - toggles the farming window", "Questionable", 576);
_chatGui.Print("/qst handle-interrupt - makes Questionable handle queued interrupts immediately (useful if you manually start combat)", "Questionable", 576); _chatGui.Print("/qst handle-interrupt - makes Questionable handle queued interrupts immediately (useful if you manually start combat)", "Questionable", 576);
break; break;
case "c": case "c":
@ -135,6 +142,7 @@ internal sealed class CommandHandler : IDisposable
_movementController.Stop(); _movementController.Stop();
_questController.Stop("Stop command"); _questController.Stop("Stop command");
_fateController.Stop("Stop command"); _fateController.Stop("Stop command");
_seasonalDutyController.Stop("Stop command");
break; break;
case "reload": case "reload":
_questWindow.Reload(); _questWindow.Reload();
@ -158,6 +166,10 @@ internal sealed class CommandHandler : IDisposable
case "fate": case "fate":
_fateSelectionWindow.ToggleOrUncollapse(); _fateSelectionWindow.ToggleOrUncollapse();
break; break;
case "d":
case "duty":
_seasonalDutySelectionWindow.ToggleOrUncollapse();
break;
case "handle-interrupt": case "handle-interrupt":
_questController.InterruptQueueWithCombat(); _questController.InterruptQueueWithCombat();
break; break;

View file

@ -200,9 +200,7 @@ internal sealed class ContextMenuController : IDisposable
int num = 1; int num = 1;
List<GatheredItem> list = new List<GatheredItem>(num); List<GatheredItem> list = new List<GatheredItem>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<GatheredItem> span = CollectionsMarshal.AsSpan(list); CollectionsMarshal.AsSpan(list)[0] = new GatheredItem
int index = 0;
span[index] = new GatheredItem
{ {
ItemId = itemId, ItemId = itemId,
ItemCount = quantity, ItemCount = quantity,

View file

@ -179,9 +179,7 @@ internal abstract class MiniTaskController<T> : IDisposable
int num = 1; int num = 1;
List<ITask> list2 = new List<ITask>(num); List<ITask> list2 = new List<ITask>(num);
CollectionsMarshal.SetCount(list2, num); CollectionsMarshal.SetCount(list2, num);
Span<ITask> span = CollectionsMarshal.AsSpan(list2); CollectionsMarshal.AsSpan(list2)[0] = new WaitAtEnd.WaitDelay();
int index = 0;
span[index] = new WaitAtEnd.WaitDelay();
taskQueue.InterruptWith(list2); taskQueue.InterruptWith(list2);
} }
LogTasksAfterInterruption(); LogTasksAfterInterruption();
@ -196,9 +194,7 @@ internal abstract class MiniTaskController<T> : IDisposable
int num = 1; int num = 1;
List<ITask> list = new List<ITask>(num); List<ITask> list = new List<ITask>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<ITask> span = CollectionsMarshal.AsSpan(list); CollectionsMarshal.AsSpan(list)[0] = new WaitAtEnd.WaitDelay();
int index = 0;
span[index] = new WaitAtEnd.WaitDelay();
taskQueue.InterruptWith(list); taskQueue.InterruptWith(list);
LogTasksAfterInterruption(); LogTasksAfterInterruption();
} }

View file

@ -431,9 +431,7 @@ internal sealed class MovementController : IDisposable
int num = 1; int num = 1;
List<Vector3> list = new List<Vector3>(num); List<Vector3> list = new List<Vector3>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<Vector3> span = CollectionsMarshal.AsSpan(list); CollectionsMarshal.AsSpan(list)[0] = destination.Position;
int index = 0;
span[index] = destination.Position;
NavigateTo(EMovementType.None, dataId, list, fly: false, sprint: false, destination.StopDistance, destination.VerticalStopDistance); NavigateTo(EMovementType.None, dataId, list, fly: false, sprint: false, destination.StopDistance, destination.VerticalStopDistance);
} }

View file

@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.InteropServices;
using Dalamud.Plugin.Services;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps;
using Questionable.Controller.Steps.Common;
using Questionable.Controller.Steps.Interactions;
using Questionable.Controller.Steps.Movement;
using Questionable.Controller.Steps.Shared;
using Questionable.Model.Questing;
namespace Questionable.Controller;
internal sealed class SeasonalDutyController : MiniTaskController<SeasonalDutyController>
{
private readonly MovementController _movementController;
private readonly IClientState _clientState;
private readonly ILogger<SeasonalDutyController> _logger;
private SeasonalDutyDefinition? _currentDuty;
private int _completedCycles;
private int? _cycleLimit;
private DateTime _startTime;
public bool IsRunning => _currentDuty != null;
public SeasonalDutyDefinition? CurrentDuty => _currentDuty;
public int CompletedCycles => _completedCycles;
public int? CycleLimit => _cycleLimit;
public TimeSpan ElapsedTime
{
get
{
if (!IsRunning)
{
return TimeSpan.Zero;
}
return DateTime.UtcNow - _startTime;
}
}
public SeasonalDutyController(IChatGui chatGui, ICondition condition, IServiceProvider serviceProvider, InterruptHandler interruptHandler, IDataManager dataManager, ILogger<SeasonalDutyController> logger, MovementController movementController, IClientState clientState)
: base(chatGui, condition, serviceProvider, interruptHandler, dataManager, logger)
{
_movementController = movementController;
_clientState = clientState;
_logger = logger;
}
public void Start(SeasonalDutyDefinition duty, int? cycleLimit = null)
{
_currentDuty = duty;
_completedCycles = 0;
_cycleLimit = cycleLimit;
_startTime = DateTime.UtcNow;
_logger.LogInformation("Starting seasonal duty farming: {DutyName} (limit: {Limit})", duty.Name, cycleLimit?.ToString(CultureInfo.InvariantCulture) ?? "unlimited");
EnqueueDutyCycle();
}
public void Update()
{
if (_currentDuty == null || _movementController.IsPathfinding || _movementController.IsPathRunning)
{
return;
}
if (_taskQueue.AllTasksComplete)
{
_completedCycles++;
if (_cycleLimit.HasValue && _completedCycles >= _cycleLimit.Value)
{
_logger.LogInformation("Seasonal duty cycle limit reached ({Cycles}/{Limit})", _completedCycles, _cycleLimit.Value);
Stop("Cycle limit reached");
return;
}
EnqueueDutyCycle();
}
UpdateCurrentTask();
}
private void EnqueueDutyCycle()
{
if (_currentDuty != null)
{
SeasonalDutyDefinition currentDuty = _currentDuty;
_logger.LogInformation("Enqueuing seasonal duty cycle for {DutyName}", currentDuty.Name);
if (_clientState.TerritoryType != currentDuty.TerritoryId && _clientState.TerritoryType != currentDuty.DutyTerritoryId)
{
_taskQueue.Enqueue(new AetheryteShortcut.Task(null, null, currentDuty.Aetheryte, currentDuty.TerritoryId));
}
if (currentDuty.AethernetShortcut != null)
{
_taskQueue.Enqueue(new AethernetShortcut.Task(currentDuty.AethernetShortcut.From, currentDuty.AethernetShortcut.To));
}
_taskQueue.Enqueue(new MoveTask(currentDuty.TerritoryId, currentDuty.NpcPosition, null, 3f));
_taskQueue.Enqueue(new Mount.UnmountTask());
_taskQueue.Enqueue(new Interact.Task(currentDuty.NpcDataId, null, EInteractionType.Interact, SkipMarkerCheck: true));
_taskQueue.Enqueue(new WaitCondition.Task(() => _clientState.TerritoryType == _currentDuty.DutyTerritoryId, $"Wait(territory: {currentDuty.DutyTerritoryId})"));
_taskQueue.Enqueue(new ClearObjectsWithAction.ClearTask(currentDuty.DataIds, currentDuty.Action, currentDuty.DutyTerritoryId, currentDuty.StopDistance, currentDuty.WaypointPositions));
_taskQueue.Enqueue(new WaitCondition.Task(() => _clientState.TerritoryType != _currentDuty.DutyTerritoryId, "Wait(duty end)"));
_taskQueue.Enqueue(new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(3L)));
}
}
public override void Stop(string label)
{
if (_currentDuty != null)
{
_logger.LogInformation("Stopping seasonal duty farming: {Label} (completed {Cycles} cycles)", label, _completedCycles);
_currentDuty = null;
_completedCycles = 0;
_cycleLimit = null;
_taskQueue.Reset();
}
}
public override IList<string> GetRemainingTaskNames()
{
ITask task = _taskQueue.CurrentTaskExecutor?.CurrentTask;
if (task != null)
{
string text = task.ToString() ?? "?";
IList<string> remainingTaskNames = base.GetRemainingTaskNames();
int num = 1 + remainingTaskNames.Count;
List<string> list = new List<string>(num);
CollectionsMarshal.SetCount(list, num);
Span<string> span = CollectionsMarshal.AsSpan(list);
int num2 = 0;
span[num2] = text;
num2++;
{
foreach (string item in remainingTaskNames)
{
span[num2] = item;
num2++;
}
return list;
}
}
return base.GetRemainingTaskNames();
}
}

View file

@ -0,0 +1,148 @@
#define RELEASE
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json;
using Dalamud.Plugin;
using Microsoft.Extensions.Logging;
using Questionable.Model.Questing;
using Questionable.SeasonalDutyPaths;
using Questionable.Windows.QuestComponents;
namespace Questionable.Controller;
internal sealed class SeasonalDutyDefinitionRegistry
{
private readonly IDalamudPluginInterface _pluginInterface;
private readonly ILogger<SeasonalDutyDefinitionRegistry> _logger;
private readonly Dictionary<ushort, SeasonalDutyDefinition> _definitions = new Dictionary<ushort, SeasonalDutyDefinition>();
public IReadOnlyDictionary<ushort, SeasonalDutyDefinition> Definitions => _definitions;
public SeasonalDutyDefinitionRegistry(IDalamudPluginInterface pluginInterface, ILogger<SeasonalDutyDefinitionRegistry> logger)
{
_pluginInterface = pluginInterface;
_logger = logger;
Reload();
}
public void Reload()
{
_definitions.Clear();
LoadFromAssembly();
try
{
LoadFromDirectory(new DirectoryInfo(Path.Combine(_pluginInterface.ConfigDirectory.FullName, "SeasonalDutyDefinitions")));
}
catch (Exception exception)
{
_logger.LogError(exception, "Failed to load seasonal duty definitions from user directory (some may have been successfully loaded)");
}
RemoveExpiredDefinitions();
_logger.LogInformation("Loaded {Count} seasonal duty definitions in total", _definitions.Count);
}
[Conditional("RELEASE")]
private void LoadFromAssembly()
{
_logger.LogInformation("Loading seasonal duty definitions from assembly");
IReadOnlyDictionary<ushort, SeasonalDutyDefinition> definitions = AssemblySeasonalDutyDefinitionLoader.GetDefinitions();
_logger.LogInformation("AssemblySeasonalDutyDefinitionLoader returned {Count} definitions", definitions.Count);
foreach (var (key, value) in definitions)
{
_definitions[key] = value;
}
_logger.LogInformation("Loaded {Count} seasonal duty definitions from assembly", _definitions.Count);
}
[Conditional("DEBUG")]
private void LoadFromProjectDirectory()
{
DirectoryInfo directoryInfo = _pluginInterface.AssemblyLocation.Directory?.Parent?.Parent;
if (directoryInfo == null)
{
return;
}
DirectoryInfo directoryInfo2 = new DirectoryInfo(Path.Combine(directoryInfo.FullName, "SeasonalDutyPaths"));
if (directoryInfo2.Exists)
{
try
{
LoadFromDirectory(directoryInfo2);
}
catch (Exception exception)
{
_definitions.Clear();
_logger.LogError(exception, "Failed to load seasonal duty definitions from project directory");
}
}
}
private void LoadFromDirectory(DirectoryInfo directory)
{
if (!directory.Exists)
{
_logger.LogInformation("Not loading seasonal duty definitions from {DirectoryName} (doesn't exist)", directory);
return;
}
FileInfo[] files = directory.GetFiles("*.json");
foreach (FileInfo fileInfo in files)
{
try
{
ushort? num = ExtractIdFromName(fileInfo.Name);
if (!num.HasValue)
{
continue;
}
using FileStream utf8Json = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read);
SeasonalDutyDefinition seasonalDutyDefinition = JsonSerializer.Deserialize<SeasonalDutyDefinition>(utf8Json);
if (seasonalDutyDefinition != null)
{
_definitions[num.Value] = seasonalDutyDefinition;
}
}
catch (Exception exception)
{
_logger.LogError(exception, "Unable to load seasonal duty definition file {FileName}", fileInfo.FullName);
}
}
}
private void RemoveExpiredDefinitions()
{
foreach (ushort item in (from kvp in _definitions.Where<KeyValuePair<ushort, SeasonalDutyDefinition>>(delegate(KeyValuePair<ushort, SeasonalDutyDefinition> kvp)
{
DateTime? eventExpiry = kvp.Value.EventExpiry;
if (eventExpiry.HasValue)
{
DateTime valueOrDefault = eventExpiry.GetValueOrDefault();
return EventInfoComponent.NormalizeExpiry(valueOrDefault) < DateTime.UtcNow;
}
return false;
})
select kvp.Key).ToList())
{
_logger.LogInformation("Removing expired seasonal duty definition {Id} '{Name}'", item, _definitions[item].Name);
_definitions.Remove(item);
}
}
private static ushort? ExtractIdFromName(string fileName)
{
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
if (!fileNameWithoutExtension.Contains('_', StringComparison.Ordinal))
{
return null;
}
if (ushort.TryParse(fileNameWithoutExtension.Split('_', 2)[0], out var result))
{
return result;
}
return null;
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -846,20 +846,16 @@ internal sealed class QuestionableIpc : IDisposable
int num = 1; int num = 1;
List<ElementId> list15 = new List<ElementId>(num); List<ElementId> list15 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list15, num); CollectionsMarshal.SetCount(list15, num);
Span<ElementId> span15 = CollectionsMarshal.AsSpan(list15); CollectionsMarshal.AsSpan(list15)[0] = new AethernetId(1);
int index = 0;
span15[index] = new AethernetId(1);
source = list15; source = list15;
break; break;
} }
case "aethernet_gridania": case "aethernet_gridania":
{ {
int index = 1; int num = 1;
List<ElementId> list14 = new List<ElementId>(index); List<ElementId> list14 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list14, index); CollectionsMarshal.SetCount(list14, num);
Span<ElementId> span14 = CollectionsMarshal.AsSpan(list14); CollectionsMarshal.AsSpan(list14)[0] = new AethernetId(2);
int num = 0;
span14[num] = new AethernetId(2);
source = list14; source = list14;
break; break;
} }
@ -868,20 +864,16 @@ internal sealed class QuestionableIpc : IDisposable
int num = 1; int num = 1;
List<ElementId> list13 = new List<ElementId>(num); List<ElementId> list13 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list13, num); CollectionsMarshal.SetCount(list13, num);
Span<ElementId> span13 = CollectionsMarshal.AsSpan(list13); CollectionsMarshal.AsSpan(list13)[0] = new AethernetId(3);
int index = 0;
span13[index] = new AethernetId(3);
source = list13; source = list13;
break; break;
} }
case "aethernet_goldsaucer": case "aethernet_goldsaucer":
{ {
int index = 1; int num = 1;
List<ElementId> list12 = new List<ElementId>(index); List<ElementId> list12 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list12, index); CollectionsMarshal.SetCount(list12, num);
Span<ElementId> span12 = CollectionsMarshal.AsSpan(list12); CollectionsMarshal.AsSpan(list12)[0] = new AethernetId(4);
int num = 0;
span12[num] = new AethernetId(4);
source = list12; source = list12;
break; break;
} }
@ -890,20 +882,16 @@ internal sealed class QuestionableIpc : IDisposable
int num = 1; int num = 1;
List<ElementId> list11 = new List<ElementId>(num); List<ElementId> list11 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list11, num); CollectionsMarshal.SetCount(list11, num);
Span<ElementId> span11 = CollectionsMarshal.AsSpan(list11); CollectionsMarshal.AsSpan(list11)[0] = new AethernetId(5);
int index = 0;
span11[index] = new AethernetId(5);
source = list11; source = list11;
break; break;
} }
case "aethernet_idyllshire": case "aethernet_idyllshire":
{ {
int index = 1; int num = 1;
List<ElementId> list10 = new List<ElementId>(index); List<ElementId> list10 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list10, index); CollectionsMarshal.SetCount(list10, num);
Span<ElementId> span10 = CollectionsMarshal.AsSpan(list10); CollectionsMarshal.AsSpan(list10)[0] = new AethernetId(6);
int num = 0;
span10[num] = new AethernetId(6);
source = list10; source = list10;
break; break;
} }
@ -912,20 +900,16 @@ internal sealed class QuestionableIpc : IDisposable
int num = 1; int num = 1;
List<ElementId> list9 = new List<ElementId>(num); List<ElementId> list9 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list9, num); CollectionsMarshal.SetCount(list9, num);
Span<ElementId> span9 = CollectionsMarshal.AsSpan(list9); CollectionsMarshal.AsSpan(list9)[0] = new AethernetId(7);
int index = 0;
span9[index] = new AethernetId(7);
source = list9; source = list9;
break; break;
} }
case "aethernet_kugane": case "aethernet_kugane":
{ {
int index = 1; int num = 1;
List<ElementId> list8 = new List<ElementId>(index); List<ElementId> list8 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list8, index); CollectionsMarshal.SetCount(list8, num);
Span<ElementId> span8 = CollectionsMarshal.AsSpan(list8); CollectionsMarshal.AsSpan(list8)[0] = new AethernetId(8);
int num = 0;
span8[num] = new AethernetId(8);
source = list8; source = list8;
break; break;
} }
@ -934,20 +918,16 @@ internal sealed class QuestionableIpc : IDisposable
int num = 1; int num = 1;
List<ElementId> list7 = new List<ElementId>(num); List<ElementId> list7 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list7, num); CollectionsMarshal.SetCount(list7, num);
Span<ElementId> span7 = CollectionsMarshal.AsSpan(list7); CollectionsMarshal.AsSpan(list7)[0] = new AethernetId(9);
int index = 0;
span7[index] = new AethernetId(9);
source = list7; source = list7;
break; break;
} }
case "aethernet_the_crystarium": case "aethernet_the_crystarium":
{ {
int index = 1; int num = 1;
List<ElementId> list6 = new List<ElementId>(index); List<ElementId> list6 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list6, index); CollectionsMarshal.SetCount(list6, num);
Span<ElementId> span6 = CollectionsMarshal.AsSpan(list6); CollectionsMarshal.AsSpan(list6)[0] = new AethernetId(10);
int num = 0;
span6[num] = new AethernetId(10);
source = list6; source = list6;
break; break;
} }
@ -956,20 +936,16 @@ internal sealed class QuestionableIpc : IDisposable
int num = 1; int num = 1;
List<ElementId> list5 = new List<ElementId>(num); List<ElementId> list5 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list5, num); CollectionsMarshal.SetCount(list5, num);
Span<ElementId> span5 = CollectionsMarshal.AsSpan(list5); CollectionsMarshal.AsSpan(list5)[0] = new AethernetId(11);
int index = 0;
span5[index] = new AethernetId(11);
source = list5; source = list5;
break; break;
} }
case "aethernet_old_sharlayan": case "aethernet_old_sharlayan":
{ {
int index = 1; int num = 1;
List<ElementId> list4 = new List<ElementId>(index); List<ElementId> list4 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list4, index); CollectionsMarshal.SetCount(list4, num);
Span<ElementId> span4 = CollectionsMarshal.AsSpan(list4); CollectionsMarshal.AsSpan(list4)[0] = new AethernetId(12);
int num = 0;
span4[num] = new AethernetId(12);
source = list4; source = list4;
break; break;
} }
@ -978,20 +954,16 @@ internal sealed class QuestionableIpc : IDisposable
int num = 1; int num = 1;
List<ElementId> list3 = new List<ElementId>(num); List<ElementId> list3 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list3, num); CollectionsMarshal.SetCount(list3, num);
Span<ElementId> span3 = CollectionsMarshal.AsSpan(list3); CollectionsMarshal.AsSpan(list3)[0] = new AethernetId(13);
int index = 0;
span3[index] = new AethernetId(13);
source = list3; source = list3;
break; break;
} }
case "aethernet_tuliyollal": case "aethernet_tuliyollal":
{ {
int index = 1; int num = 1;
List<ElementId> list2 = new List<ElementId>(index); List<ElementId> list2 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list2, index); CollectionsMarshal.SetCount(list2, num);
Span<ElementId> span2 = CollectionsMarshal.AsSpan(list2); CollectionsMarshal.AsSpan(list2)[0] = new AethernetId(14);
int num = 0;
span2[num] = new AethernetId(14);
source = list2; source = list2;
break; break;
} }
@ -1000,9 +972,7 @@ internal sealed class QuestionableIpc : IDisposable
int num = 1; int num = 1;
List<ElementId> list = new List<ElementId>(num); List<ElementId> list = new List<ElementId>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<ElementId> span = CollectionsMarshal.AsSpan(list); CollectionsMarshal.AsSpan(list)[0] = new AethernetId(15);
int index = 0;
span[index] = new AethernetId(15);
source = list; source = list;
break; break;
} }

View file

@ -77,9 +77,7 @@ internal sealed class AlliedSocietyQuestFunctions
int num = 1; int num = 1;
List<NpcData> list = new List<NpcData>(num); List<NpcData> list = new List<NpcData>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<NpcData> span = CollectionsMarshal.AsSpan(list); CollectionsMarshal.AsSpan(list)[0] = npcData;
int index = 0;
span[index] = npcData;
questsByAlliedSociety[item] = list; questsByAlliedSociety[item] = list;
} }
} }

View file

@ -1,5 +1,6 @@
using System; using System;
using System.CodeDom.Compiler; using System.CodeDom.Compiler;
using System.Runtime.CompilerServices;
using Lumina.Excel; using Lumina.Excel;
using Lumina.Excel.Sheets; using Lumina.Excel.Sheets;
using Lumina.Text.ReadOnly; using Lumina.Text.ReadOnly;
@ -184,10 +185,10 @@ public readonly struct QuestEx : IExcelRow<QuestEx>
{ {
ExcelModule module = _003Cpage_003EP.Module; ExcelModule module = _003Cpage_003EP.Module;
uint rowId = _003Cpage_003EP.ReadUInt32(_003Coffset_003EP + 2712); uint rowId = _003Cpage_003EP.ReadUInt32(_003Coffset_003EP + 2712);
global::_003C_003Ey__InlineArray2<Type> buffer = default(global::_003C_003Ey__InlineArray2<Type>); InlineArray2<Type> buffer = default(InlineArray2<Type>);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<global::_003C_003Ey__InlineArray2<Type>, Type>(ref buffer, 0) = typeof(EObjName); global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<InlineArray2<Type>, Type>(ref buffer, 0) = typeof(EObjName);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<global::_003C_003Ey__InlineArray2<Type>, Type>(ref buffer, 1) = typeof(ENpcResident); global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<InlineArray2<Type>, Type>(ref buffer, 1) = typeof(ENpcResident);
return RowRef.GetFirstValidRowOrUntyped(module, rowId, global::_003CPrivateImplementationDetails_003E.InlineArrayAsReadOnlySpan<global::_003C_003Ey__InlineArray2<Type>, Type>(in buffer, 2), -965341264, _003Cpage_003EP.Language); return RowRef.GetFirstValidRowOrUntyped(module, rowId, global::_003CPrivateImplementationDetails_003E.InlineArrayAsReadOnlySpan<InlineArray2<Type>, Type>(in buffer, 2), -965341264, _003Cpage_003EP.Language);
} }
} }
@ -199,10 +200,10 @@ public readonly struct QuestEx : IExcelRow<QuestEx>
{ {
ExcelModule module = _003Cpage_003EP.Module; ExcelModule module = _003Cpage_003EP.Module;
uint rowId = _003Cpage_003EP.ReadUInt32(_003Coffset_003EP + 2720); uint rowId = _003Cpage_003EP.ReadUInt32(_003Coffset_003EP + 2720);
global::_003C_003Ey__InlineArray2<Type> buffer = default(global::_003C_003Ey__InlineArray2<Type>); InlineArray2<Type> buffer = default(InlineArray2<Type>);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<global::_003C_003Ey__InlineArray2<Type>, Type>(ref buffer, 0) = typeof(EObjName); global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<InlineArray2<Type>, Type>(ref buffer, 0) = typeof(EObjName);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<global::_003C_003Ey__InlineArray2<Type>, Type>(ref buffer, 1) = typeof(ENpcResident); global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<InlineArray2<Type>, Type>(ref buffer, 1) = typeof(ENpcResident);
return RowRef.GetFirstValidRowOrUntyped(module, rowId, global::_003CPrivateImplementationDetails_003E.InlineArrayAsReadOnlySpan<global::_003C_003Ey__InlineArray2<Type>, Type>(in buffer, 2), -965341264, _003Cpage_003EP.Language); return RowRef.GetFirstValidRowOrUntyped(module, rowId, global::_003CPrivateImplementationDetails_003E.InlineArrayAsReadOnlySpan<InlineArray2<Type>, Type>(in buffer, 2), -965341264, _003Cpage_003EP.Language);
} }
} }

View file

@ -480,16 +480,12 @@ internal sealed class GeneralConfigComponent : ConfigComponent
int num = 1; int num = 1;
List<(uint, string)> list = new List<(uint, string)>(num); List<(uint, string)> list = new List<(uint, string)>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<(uint, string)> span = CollectionsMarshal.AsSpan(list); CollectionsMarshal.AsSpan(list)[0] = (0u, "Mount Roulette");
int index = 0;
span[index] = (0u, "Mount Roulette");
DefaultMounts = list; DefaultMounts = list;
index = 1; num = 1;
List<(EClassJob, string)> list2 = new List<(EClassJob, string)>(index); List<(EClassJob, string)> list2 = new List<(EClassJob, string)>(num);
CollectionsMarshal.SetCount(list2, index); CollectionsMarshal.SetCount(list2, num);
Span<(EClassJob, string)> span2 = CollectionsMarshal.AsSpan(list2); CollectionsMarshal.AsSpan(list2)[0] = (EClassJob.Adventurer, "Auto (highest level/item level)");
num = 0;
span2[num] = (EClassJob.Adventurer, "Auto (highest level/item level)");
DefaultClassJobs = list2; DefaultClassJobs = list2;
} }
} }

View file

@ -898,16 +898,11 @@ internal sealed class SinglePlayerDutyConfigComponent : ConfigComponent
List<(EClassJob, string)> list = new List<(EClassJob, string)>(num); List<(EClassJob, string)> list = new List<(EClassJob, string)>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<(EClassJob, string)> span = CollectionsMarshal.AsSpan(list); Span<(EClassJob, string)> span = CollectionsMarshal.AsSpan(list);
int num2 = 0; span[0] = (EClassJob.Paladin, "Tank Role Quests");
span[num2] = (EClassJob.Paladin, "Tank Role Quests"); span[1] = (EClassJob.WhiteMage, "Healer Role Quests");
num2++; span[2] = (EClassJob.Lancer, "Melee Role Quests");
span[num2] = (EClassJob.WhiteMage, "Healer Role Quests"); span[3] = (EClassJob.Bard, "Physical Ranged Role Quests");
num2++; span[4] = (EClassJob.BlackMage, "Magical Ranged Role Quests");
span[num2] = (EClassJob.Lancer, "Melee Role Quests");
num2++;
span[num2] = (EClassJob.Bard, "Physical Ranged Role Quests");
num2++;
span[num2] = (EClassJob.BlackMage, "Magical Ranged Role Quests");
RoleQuestCategories = list; RoleQuestCategories = list;
} }
} }

View file

@ -59,7 +59,7 @@ internal sealed class ActiveQuestComponent
public event EventHandler? Reload; public event EventHandler? Reload;
[GeneratedRegex("\\s\\s+", RegexOptions.IgnoreCase, "en-US")] [GeneratedRegex("\\s\\s+", RegexOptions.IgnoreCase, "en-US")]
[GeneratedCode("System.Text.RegularExpressions.Generator", "10.0.14.7603")] [GeneratedCode("System.Text.RegularExpressions.Generator", "10.0.14.15411")]
private static Regex MultipleWhitespaceRegex() private static Regex MultipleWhitespaceRegex()
{ {
return _003CRegexGenerator_g_003EFBB8301322196CF81C64F1652C2FA6E1D6BF3907141F781E9D97ABED51BF056C4__MultipleWhitespaceRegex_0.Instance; return _003CRegexGenerator_g_003EFBB8301322196CF81C64F1652C2FA6E1D6BF3907141F781E9D97ABED51BF056C4__MultipleWhitespaceRegex_0.Instance;

View file

@ -615,19 +615,15 @@ internal sealed class PresetBuilderComponent
int num = 1; int num = 1;
List<ElementId> list15 = new List<ElementId>(num); List<ElementId> list15 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list15, num); CollectionsMarshal.SetCount(list15, num);
Span<ElementId> span15 = CollectionsMarshal.AsSpan(list15); CollectionsMarshal.AsSpan(list15)[0] = new AethernetId(1);
int index = 0;
span15[index] = new AethernetId(1);
return list15; return list15;
} }
case "Gridania": case "Gridania":
{ {
int index = 1; int num = 1;
List<ElementId> list14 = new List<ElementId>(index); List<ElementId> list14 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list14, index); CollectionsMarshal.SetCount(list14, num);
Span<ElementId> span14 = CollectionsMarshal.AsSpan(list14); CollectionsMarshal.AsSpan(list14)[0] = new AethernetId(2);
int num = 0;
span14[num] = new AethernetId(2);
return list14; return list14;
} }
case "Uldah": case "Uldah":
@ -635,19 +631,15 @@ internal sealed class PresetBuilderComponent
int num = 1; int num = 1;
List<ElementId> list13 = new List<ElementId>(num); List<ElementId> list13 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list13, num); CollectionsMarshal.SetCount(list13, num);
Span<ElementId> span13 = CollectionsMarshal.AsSpan(list13); CollectionsMarshal.AsSpan(list13)[0] = new AethernetId(3);
int index = 0;
span13[index] = new AethernetId(3);
return list13; return list13;
} }
case "GoldSaucer": case "GoldSaucer":
{ {
int index = 1; int num = 1;
List<ElementId> list12 = new List<ElementId>(index); List<ElementId> list12 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list12, index); CollectionsMarshal.SetCount(list12, num);
Span<ElementId> span12 = CollectionsMarshal.AsSpan(list12); CollectionsMarshal.AsSpan(list12)[0] = new AethernetId(4);
int num = 0;
span12[num] = new AethernetId(4);
return list12; return list12;
} }
case "Ishgard": case "Ishgard":
@ -655,19 +647,15 @@ internal sealed class PresetBuilderComponent
int num = 1; int num = 1;
List<ElementId> list11 = new List<ElementId>(num); List<ElementId> list11 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list11, num); CollectionsMarshal.SetCount(list11, num);
Span<ElementId> span11 = CollectionsMarshal.AsSpan(list11); CollectionsMarshal.AsSpan(list11)[0] = new AethernetId(5);
int index = 0;
span11[index] = new AethernetId(5);
return list11; return list11;
} }
case "Idyllshire": case "Idyllshire":
{ {
int index = 1; int num = 1;
List<ElementId> list10 = new List<ElementId>(index); List<ElementId> list10 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list10, index); CollectionsMarshal.SetCount(list10, num);
Span<ElementId> span10 = CollectionsMarshal.AsSpan(list10); CollectionsMarshal.AsSpan(list10)[0] = new AethernetId(6);
int num = 0;
span10[num] = new AethernetId(6);
return list10; return list10;
} }
case "Rhalgr's Reach": case "Rhalgr's Reach":
@ -675,19 +663,15 @@ internal sealed class PresetBuilderComponent
int num = 1; int num = 1;
List<ElementId> list9 = new List<ElementId>(num); List<ElementId> list9 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list9, num); CollectionsMarshal.SetCount(list9, num);
Span<ElementId> span9 = CollectionsMarshal.AsSpan(list9); CollectionsMarshal.AsSpan(list9)[0] = new AethernetId(7);
int index = 0;
span9[index] = new AethernetId(7);
return list9; return list9;
} }
case "Kugane": case "Kugane":
{ {
int index = 1; int num = 1;
List<ElementId> list8 = new List<ElementId>(index); List<ElementId> list8 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list8, index); CollectionsMarshal.SetCount(list8, num);
Span<ElementId> span8 = CollectionsMarshal.AsSpan(list8); CollectionsMarshal.AsSpan(list8)[0] = new AethernetId(8);
int num = 0;
span8[num] = new AethernetId(8);
return list8; return list8;
} }
case "Doman Enclave": case "Doman Enclave":
@ -695,19 +679,15 @@ internal sealed class PresetBuilderComponent
int num = 1; int num = 1;
List<ElementId> list7 = new List<ElementId>(num); List<ElementId> list7 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list7, num); CollectionsMarshal.SetCount(list7, num);
Span<ElementId> span7 = CollectionsMarshal.AsSpan(list7); CollectionsMarshal.AsSpan(list7)[0] = new AethernetId(9);
int index = 0;
span7[index] = new AethernetId(9);
return list7; return list7;
} }
case "The Crystarium": case "The Crystarium":
{ {
int index = 1; int num = 1;
List<ElementId> list6 = new List<ElementId>(index); List<ElementId> list6 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list6, index); CollectionsMarshal.SetCount(list6, num);
Span<ElementId> span6 = CollectionsMarshal.AsSpan(list6); CollectionsMarshal.AsSpan(list6)[0] = new AethernetId(10);
int num = 0;
span6[num] = new AethernetId(10);
return list6; return list6;
} }
case "Eulmore": case "Eulmore":
@ -715,19 +695,15 @@ internal sealed class PresetBuilderComponent
int num = 1; int num = 1;
List<ElementId> list5 = new List<ElementId>(num); List<ElementId> list5 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list5, num); CollectionsMarshal.SetCount(list5, num);
Span<ElementId> span5 = CollectionsMarshal.AsSpan(list5); CollectionsMarshal.AsSpan(list5)[0] = new AethernetId(11);
int index = 0;
span5[index] = new AethernetId(11);
return list5; return list5;
} }
case "Old Sharlayan": case "Old Sharlayan":
{ {
int index = 1; int num = 1;
List<ElementId> list4 = new List<ElementId>(index); List<ElementId> list4 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list4, index); CollectionsMarshal.SetCount(list4, num);
Span<ElementId> span4 = CollectionsMarshal.AsSpan(list4); CollectionsMarshal.AsSpan(list4)[0] = new AethernetId(12);
int num = 0;
span4[num] = new AethernetId(12);
return list4; return list4;
} }
case "Radz-at-Han": case "Radz-at-Han":
@ -735,19 +711,15 @@ internal sealed class PresetBuilderComponent
int num = 1; int num = 1;
List<ElementId> list3 = new List<ElementId>(num); List<ElementId> list3 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list3, num); CollectionsMarshal.SetCount(list3, num);
Span<ElementId> span3 = CollectionsMarshal.AsSpan(list3); CollectionsMarshal.AsSpan(list3)[0] = new AethernetId(13);
int index = 0;
span3[index] = new AethernetId(13);
return list3; return list3;
} }
case "Tuliyollal": case "Tuliyollal":
{ {
int index = 1; int num = 1;
List<ElementId> list2 = new List<ElementId>(index); List<ElementId> list2 = new List<ElementId>(num);
CollectionsMarshal.SetCount(list2, index); CollectionsMarshal.SetCount(list2, num);
Span<ElementId> span2 = CollectionsMarshal.AsSpan(list2); CollectionsMarshal.AsSpan(list2)[0] = new AethernetId(14);
int num = 0;
span2[num] = new AethernetId(14);
return list2; return list2;
} }
case "Solution Nine": case "Solution Nine":
@ -755,9 +727,7 @@ internal sealed class PresetBuilderComponent
int num = 1; int num = 1;
List<ElementId> list = new List<ElementId>(num); List<ElementId> list = new List<ElementId>(num);
CollectionsMarshal.SetCount(list, num); CollectionsMarshal.SetCount(list, num);
Span<ElementId> span = CollectionsMarshal.AsSpan(list); CollectionsMarshal.AsSpan(list)[0] = new AethernetId(15);
int index = 0;
span[index] = new AethernetId(15);
return list; return list;
} }
default: default:

View file

@ -0,0 +1,424 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using LLib.ImGui;
using Questionable.Controller;
using Questionable.Data;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
using Questionable.Windows.QuestComponents;
namespace Questionable.Windows;
internal sealed class SeasonalDutySelectionWindow : LWindow
{
private readonly SeasonalDutyController _seasonalDutyController;
private readonly SeasonalDutyDefinitionRegistry _seasonalDutyDefinitionRegistry;
private readonly QuestController _questController;
private readonly FateController _fateController;
private readonly MovementController _movementController;
private readonly QuestFunctions _questFunctions;
private readonly QuestData _questData;
private readonly TerritoryData _territoryData;
private int _cycleLimit;
public SeasonalDutySelectionWindow(SeasonalDutyController seasonalDutyController, SeasonalDutyDefinitionRegistry seasonalDutyDefinitionRegistry, QuestController questController, FateController fateController, MovementController movementController, QuestFunctions questFunctions, QuestData questData, TerritoryData territoryData)
: base("Seasonal Duty Farming###QuestionableSeasonalDutyFarming")
{
_seasonalDutyController = seasonalDutyController;
_seasonalDutyDefinitionRegistry = seasonalDutyDefinitionRegistry;
_questController = questController;
_fateController = fateController;
_movementController = movementController;
_questFunctions = questFunctions;
_questData = questData;
_territoryData = territoryData;
base.Size = new Vector2(600f, 400f);
base.SizeCondition = ImGuiCond.FirstUseEver;
base.SizeConstraints = new WindowSizeConstraints
{
MinimumSize = new Vector2(500f, 300f),
MaximumSize = new Vector2(900f, 700f)
};
}
public override void DrawContent()
{
if (_seasonalDutyController.IsRunning)
{
DrawRunningHeader();
}
else
{
DrawControlsStrip();
}
if (_seasonalDutyDefinitionRegistry.Definitions.Count == 0)
{
DrawEmptyState();
}
else
{
DrawDutyTable();
}
}
private void DrawRunningHeader()
{
Vector2 cursorScreenPos = ImGui.GetCursorScreenPos();
float x = ImGui.GetContentRegionAvail().X;
float num = ImGui.GetTextLineHeightWithSpacing() * 3f + ImGui.GetStyle().ItemSpacing.Y * 3f;
ImDrawListPtr windowDrawList = ImGui.GetWindowDrawList();
windowDrawList.AddRectFilledMultiColor(cursorScreenPos, cursorScreenPos + new Vector2(x, num), ImGui.ColorConvertFloat4ToU32(new Vector4(0.18f, 0.26f, 0.2f, 0.85f)), ImGui.ColorConvertFloat4ToU32(new Vector4(0.14f, 0.22f, 0.16f, 0.85f)), ImGui.ColorConvertFloat4ToU32(new Vector4(0.14f, 0.22f, 0.16f, 0.85f)), ImGui.ColorConvertFloat4ToU32(new Vector4(0.18f, 0.26f, 0.2f, 0.85f)));
windowDrawList.AddLine(cursorScreenPos + new Vector2(0f, num), cursorScreenPos + new Vector2(x, num), ImGui.ColorConvertFloat4ToU32(new Vector4(0.3f, 0.8f, 0.4f, 0.5f)), 2f);
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ImGui.GetStyle().ItemSpacing.Y);
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 8f);
float num2 = (float)ImGui.GetTime();
float num3 = 0.85f + MathF.Sin(num2 * 3f) * 0.15f;
Vector4 col = new Vector4(0f, 1f * num3, 0.3f * num3, 1f);
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGui.TextColored(in col, FontAwesomeIcon.Leaf.ToIconString());
}
ImGui.SameLine();
ImGui.TextColored(ImGuiColors.ParsedGreen, _seasonalDutyController.CurrentDuty.Name);
ImGui.SameLine(x - 35f);
using (ImRaii.PushColor(ImGuiCol.Button, Vector4.Zero))
{
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, new Vector4(0.8f, 0.3f, 0.3f, 0.4f)))
{
using (ImRaii.PushColor(ImGuiCol.ButtonActive, new Vector4(0.9f, 0.2f, 0.2f, 0.6f)))
{
if (ImGuiComponents.IconButton(FontAwesomeIcon.Stop))
{
_seasonalDutyController.Stop("UI stop");
_movementController.Stop();
}
}
}
}
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 8f);
string value = (_seasonalDutyController.CycleLimit.HasValue ? $"Cycle {_seasonalDutyController.CompletedCycles + 1} / {_seasonalDutyController.CycleLimit}" : $"Cycle {_seasonalDutyController.CompletedCycles + 1}");
string value2 = FormatElapsed(_seasonalDutyController.ElapsedTime);
IList<string> remainingTaskNames = _seasonalDutyController.GetRemainingTaskNames();
string text = ((remainingTaskNames.Count > 0) ? remainingTaskNames.First() : "");
if (text.Length > 0)
{
Vector4 col2 = ImGuiColors.DalamudGrey3;
ImU8String text2 = new ImU8String(10, 3);
text2.AppendFormatted(value);
text2.AppendLiteral(" · ");
text2.AppendFormatted(value2);
text2.AppendLiteral(" · ");
text2.AppendFormatted(text);
ImGui.TextColored(in col2, text2);
}
else
{
Vector4 col2 = ImGuiColors.DalamudGrey3;
ImU8String text3 = new ImU8String(5, 2);
text3.AppendFormatted(value);
text3.AppendLiteral(" · ");
text3.AppendFormatted(value2);
ImGui.TextColored(in col2, text3);
}
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 8f);
using (ImRaii.Disabled(disabled: true))
{
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Cycles:");
ImGui.SameLine();
ImGui.SetNextItemWidth(100f);
ImGui.InputInt("##CycleLimitRunning", ref _cycleLimit, 1, 5);
}
ImGui.SetCursorPosY(cursorScreenPos.Y + num - ImGui.GetCursorScreenPos().Y + ImGui.GetCursorPosY() + 4f);
ImGui.Spacing();
}
private void DrawControlsStrip()
{
using (ImRaii.Disabled(_seasonalDutyController.IsRunning))
{
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Cycles:");
ImGui.SameLine();
ImGui.SetNextItemWidth(100f);
ImGui.InputInt("##CycleLimit", ref _cycleLimit, 1, 5);
if (_cycleLimit < 0)
{
_cycleLimit = 0;
}
ImGui.SameLine();
ImGui.TextColored(ImGuiColors.DalamudGrey, (_cycleLimit == 0) ? "(unlimited)" : "");
}
ImGui.Spacing();
}
private static void DrawEmptyState()
{
float y = ImGui.GetContentRegionAvail().Y;
float x = ImGui.GetContentRegionAvail().X;
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + y * 0.25f);
Vector4 col = new Vector4(0.3f, 0.65f, 0.4f, 0.7f);
Vector2 cursorScreenPos = ImGui.GetCursorScreenPos();
ImGui.PushFont(UiBuilder.IconFont);
string text = FontAwesomeIcon.Search.ToIconString();
Vector2 vector = ImGui.CalcTextSize(text);
vector *= 2.5f;
ImDrawListPtr windowDrawList = ImGui.GetWindowDrawList();
Vector2 vector2 = new Vector2(x * 0.5f, 0f) + cursorScreenPos + new Vector2(0f, vector.Y * 0.5f);
for (int num = 3; num > 0; num--)
{
Vector4 col2 = new Vector4(col.X, col.Y, col.Z, col.W * 0.1f * (float)num);
ImGui.SetCursorScreenPos(vector2 - vector * 0.5f - new Vector2(num * 2, num * 2));
ImGui.TextColored(in col2, text);
}
ImGui.SetCursorScreenPos(vector2 - vector * 0.5f);
ImGui.TextColored(in col, text);
ImGui.PopFont();
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + vector.Y * 0.5f + 20f);
string text2 = "No Seasonal Duty Definitions";
Vector2 vector3 = ImGui.CalcTextSize(text2);
Vector2 vector4 = new Vector2((x - vector3.X) * 0.5f, 0f) + ImGui.GetCursorScreenPos();
windowDrawList.AddText(vector4 + new Vector2(1f, 1f), ImGui.ColorConvertFloat4ToU32(new Vector4(0f, 0f, 0f, 0.5f)), text2);
ImGui.SetCursorPosX((x - vector3.X) * 0.5f);
ImGui.TextColored(new Vector4(0.95f, 0.95f, 1f, 1f), text2);
ImGui.Spacing();
ImGui.Spacing();
string text3 = "Add definitions to the SeasonalDutyPaths folder to get started.";
ImGui.SetCursorPosX((x - ImGui.CalcTextSize(text3).X) * 0.5f);
ImGui.TextColored(new Vector4(0.7f, 0.7f, 0.8f, 1f), text3);
}
private void DrawDutyTable()
{
List<SeasonalDutyDefinition> list = _seasonalDutyDefinitionRegistry.Definitions.Values.OrderBy(delegate(SeasonalDutyDefinition d)
{
DateTime? eventExpiry = d.EventExpiry;
if (eventExpiry.HasValue)
{
DateTime valueOrDefault = eventExpiry.GetValueOrDefault();
if (EventInfoComponent.NormalizeExpiry(valueOrDefault) - DateTime.UtcNow > TimeSpan.Zero)
{
return 0;
}
}
return 1;
}).ThenBy(delegate(SeasonalDutyDefinition d)
{
DateTime? eventExpiry = d.EventExpiry;
if (eventExpiry.HasValue)
{
DateTime valueOrDefault = eventExpiry.GetValueOrDefault();
TimeSpan timeSpan = EventInfoComponent.NormalizeExpiry(valueOrDefault) - DateTime.UtcNow;
if (timeSpan > TimeSpan.Zero)
{
return timeSpan.TotalSeconds;
}
}
return double.MaxValue;
}).ThenBy<SeasonalDutyDefinition, string>((SeasonalDutyDefinition d) => d.Name, StringComparer.OrdinalIgnoreCase)
.ToList();
using ImRaii.IEndObject endObject = ImRaii.Table("SeasonalDutyTable", 4, ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInnerH | ImGuiTableFlags.ScrollY);
if (!endObject)
{
return;
}
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.None, 3f);
ImGui.TableSetupColumn("Zone", ImGuiTableColumnFlags.None, 2f);
ImGui.TableSetupColumn("Expiry", ImGuiTableColumnFlags.WidthFixed, 80f);
ImGui.TableSetupColumn("##Actions", ImGuiTableColumnFlags.WidthFixed, 30f);
ImGui.TableHeadersRow();
foreach (SeasonalDutyDefinition item in list)
{
ImGui.TableNextRow();
bool flag = _seasonalDutyController.IsRunning && _seasonalDutyController.CurrentDuty == item;
if (flag)
{
float num = (float)ImGui.GetTime();
float w = 0.1f + MathF.Sin(num * 2.5f) * 0.05f;
ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg1, ImGui.GetColorU32(new Vector4(0f, 0.85f, 0.3f, w)));
}
ImGui.TableNextColumn();
DrawDutyRowName(item, flag);
ImGui.TableNextColumn();
ImGui.TextUnformatted(_territoryData.GetName(item.TerritoryId) ?? item.Name);
ImGui.TableNextColumn();
DrawDutyRowExpiry(item);
ImGui.TableNextColumn();
bool disabled = _seasonalDutyController.IsRunning || _fateController.IsRunning || _questController.AutomationType != QuestController.EAutomationType.Manual;
DrawDutyRowActions(item, disabled);
}
}
private void DrawDutyRowName(SeasonalDutyDefinition duty, bool isActive)
{
if (isActive)
{
float num = (float)ImGui.GetTime();
float num2 = 0.85f + MathF.Sin(num * 3f) * 0.15f;
ImGui.TextColored(new Vector4(0f, 1f * num2, 0.3f * num2, 1f), duty.Name);
}
else
{
ImGui.TextUnformatted(duty.Name);
}
if (!ImGui.IsItemHovered())
{
return;
}
using (ImRaii.Tooltip())
{
ImGui.TextColored(ImGuiColors.DalamudWhite, duty.Name);
ImGui.Separator();
ImGui.Spacing();
string nameAndId = _territoryData.GetNameAndId(duty.TerritoryId);
Vector4 col = ImGuiColors.DalamudGrey;
ImU8String text = new ImU8String(6, 1);
text.AppendLiteral("Zone: ");
text.AppendFormatted(nameAndId);
ImGui.TextColored(in col, text);
DateTime? eventExpiry = duty.EventExpiry;
if (eventExpiry.HasValue)
{
DateTime valueOrDefault = eventExpiry.GetValueOrDefault();
TimeSpan timeSpan = EventInfoComponent.NormalizeExpiry(valueOrDefault) - DateTime.UtcNow;
if (timeSpan > TimeSpan.Zero)
{
string text2 = EventInfoComponent.FormatRemainingFull(timeSpan);
ImGui.TextColored(ImGuiColors.DalamudOrange, text2);
}
}
}
}
private static void DrawDutyRowExpiry(SeasonalDutyDefinition duty)
{
DateTime? eventExpiry = duty.EventExpiry;
if (eventExpiry.HasValue)
{
DateTime valueOrDefault = eventExpiry.GetValueOrDefault();
TimeSpan timeSpan = EventInfoComponent.NormalizeExpiry(valueOrDefault) - DateTime.UtcNow;
if (timeSpan > TimeSpan.Zero)
{
string text = EventInfoComponent.FormatRemainingDays(timeSpan);
ImGui.TextColored((timeSpan.TotalDays < 3.0) ? ImGuiColors.DalamudOrange : ImGuiColors.DalamudGrey, text);
}
else
{
ImGui.TextColored(ImGuiColors.DalamudGrey3, "Expired");
}
}
else
{
ImGui.TextColored(ImGuiColors.DalamudGrey3, "--");
}
}
private void DrawDutyRowActions(SeasonalDutyDefinition duty, bool disabled)
{
bool flag = duty.RequiredQuestId.HasValue && !_questFunctions.IsQuestComplete(new QuestId(duty.RequiredQuestId.Value));
ImU8String id = new ImU8String(5, 1);
id.AppendLiteral("duty_");
id.AppendFormatted(duty.Name);
using (ImRaii.PushId(id))
{
if (flag)
{
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGui.TextColored(ImGuiColors.DalamudRed, FontAwesomeIcon.Times.ToIconString());
}
if (ImGui.IsItemHovered())
{
IQuestInfo questInfo;
string value = (_questData.TryGetQuestInfo(new QuestId(duty.RequiredQuestId.Value), out questInfo) ? questInfo.Name : duty.RequiredQuestId.Value.ToString(CultureInfo.InvariantCulture));
ImU8String tooltip = new ImU8String(33, 1);
tooltip.AppendLiteral("Requires \"");
tooltip.AppendFormatted(value);
tooltip.AppendLiteral("\" to be completed first");
ImGui.SetTooltip(tooltip);
}
return;
}
using (ImRaii.Disabled(disabled))
{
if (ImGuiComponents.IconButton(FontAwesomeIcon.Play))
{
_questController.Stop("Seasonal duty farming start");
_fateController.Stop("Seasonal duty farming start");
_movementController.Stop();
int? cycleLimit = ((_cycleLimit > 0) ? new int?(_cycleLimit) : ((int?)null));
_seasonalDutyController.Start(duty, cycleLimit);
}
}
if (disabled && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
{
if (_seasonalDutyController.IsRunning)
{
ImGui.SetTooltip("Already farming a seasonal duty");
}
else if (_fateController.IsRunning)
{
ImGui.SetTooltip("Stop FATE farming first");
}
else
{
ImGui.SetTooltip("Stop quest automation first");
}
}
}
}
private static string FormatElapsed(TimeSpan elapsed)
{
IFormatProvider invariantCulture;
if (elapsed.TotalHours >= 1.0)
{
invariantCulture = CultureInfo.InvariantCulture;
IFormatProvider provider = invariantCulture;
DefaultInterpolatedStringHandler handler = new DefaultInterpolatedStringHandler(5, 3, invariantCulture);
handler.AppendFormatted((int)elapsed.TotalHours);
handler.AppendLiteral("h ");
handler.AppendFormatted(elapsed.Minutes, "D2");
handler.AppendLiteral("m ");
handler.AppendFormatted(elapsed.Seconds, "D2");
handler.AppendLiteral("s");
return string.Create(provider, ref handler);
}
if (elapsed.TotalMinutes >= 1.0)
{
invariantCulture = CultureInfo.InvariantCulture;
IFormatProvider provider2 = invariantCulture;
DefaultInterpolatedStringHandler handler2 = new DefaultInterpolatedStringHandler(3, 2, invariantCulture);
handler2.AppendFormatted((int)elapsed.TotalMinutes);
handler2.AppendLiteral("m ");
handler2.AppendFormatted(elapsed.Seconds, "D2");
handler2.AppendLiteral("s");
return string.Create(provider2, ref handler2);
}
invariantCulture = CultureInfo.InvariantCulture;
IFormatProvider provider3 = invariantCulture;
DefaultInterpolatedStringHandler handler3 = new DefaultInterpolatedStringHandler(1, 1, invariantCulture);
handler3.AppendFormatted(elapsed.Seconds);
handler3.AppendLiteral("s");
return string.Create(provider3, ref handler3);
}
}

View file

@ -74,6 +74,9 @@
<Reference Include="GatheringPaths"> <Reference Include="GatheringPaths">
<HintPath>..\..\GatheringPaths.dll</HintPath> <HintPath>..\..\GatheringPaths.dll</HintPath>
</Reference> </Reference>
<Reference Include="SeasonalDutyPaths">
<HintPath>..\..\SeasonalDutyPaths.dll</HintPath>
</Reference>
<Reference Include="JsonPointer.Net"> <Reference Include="JsonPointer.Net">
<HintPath>..\..\JsonPointer.Net.dll</HintPath> <HintPath>..\..\JsonPointer.Net.dll</HintPath>
</Reference> </Reference>

View file

@ -35,17 +35,20 @@ internal sealed class DalamudInitializer : IDisposable
private readonly FateController _fateController; private readonly FateController _fateController;
private readonly SeasonalDutyController _seasonalDutyController;
private readonly PartyWatchdog _partyWatchdog; private readonly PartyWatchdog _partyWatchdog;
private readonly ILogger<DalamudInitializer> _logger; private readonly ILogger<DalamudInitializer> _logger;
public DalamudInitializer(IDalamudPluginInterface pluginInterface, IFramework framework, QuestController questController, MovementController movementController, FateController fateController, WindowSystem windowSystem, OneTimeSetupWindow oneTimeSetupWindow, QuestWindow questWindow, DebugOverlay debugOverlay, ConfigWindow configWindow, QuestSelectionWindow questSelectionWindow, QuestSequenceWindow questSequenceWindow, QuestValidationWindow questValidationWindow, JournalProgressWindow journalProgressWindow, PriorityWindow priorityWindow, FateSelectionWindow fateSelectionWindow, ChangelogWindow changelogWindow, IToastGui toastGui, Configuration configuration, PartyWatchdog partyWatchdog, ILogger<DalamudInitializer> logger) public DalamudInitializer(IDalamudPluginInterface pluginInterface, IFramework framework, QuestController questController, MovementController movementController, FateController fateController, SeasonalDutyController seasonalDutyController, WindowSystem windowSystem, OneTimeSetupWindow oneTimeSetupWindow, QuestWindow questWindow, DebugOverlay debugOverlay, ConfigWindow configWindow, QuestSelectionWindow questSelectionWindow, QuestSequenceWindow questSequenceWindow, QuestValidationWindow questValidationWindow, JournalProgressWindow journalProgressWindow, PriorityWindow priorityWindow, FateSelectionWindow fateSelectionWindow, SeasonalDutySelectionWindow seasonalDutySelectionWindow, ChangelogWindow changelogWindow, IToastGui toastGui, Configuration configuration, PartyWatchdog partyWatchdog, ILogger<DalamudInitializer> logger)
{ {
_pluginInterface = pluginInterface; _pluginInterface = pluginInterface;
_framework = framework; _framework = framework;
_questController = questController; _questController = questController;
_movementController = movementController; _movementController = movementController;
_fateController = fateController; _fateController = fateController;
_seasonalDutyController = seasonalDutyController;
_windowSystem = windowSystem; _windowSystem = windowSystem;
_oneTimeSetupWindow = oneTimeSetupWindow; _oneTimeSetupWindow = oneTimeSetupWindow;
_questWindow = questWindow; _questWindow = questWindow;
@ -64,6 +67,7 @@ internal sealed class DalamudInitializer : IDisposable
_windowSystem.AddWindow(journalProgressWindow); _windowSystem.AddWindow(journalProgressWindow);
_windowSystem.AddWindow(priorityWindow); _windowSystem.AddWindow(priorityWindow);
_windowSystem.AddWindow(fateSelectionWindow); _windowSystem.AddWindow(fateSelectionWindow);
_windowSystem.AddWindow(seasonalDutySelectionWindow);
_windowSystem.AddWindow(changelogWindow); _windowSystem.AddWindow(changelogWindow);
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw; _pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
_pluginInterface.UiBuilder.OpenMainUi += ToggleQuestWindow; _pluginInterface.UiBuilder.OpenMainUi += ToggleQuestWindow;
@ -79,6 +83,7 @@ internal sealed class DalamudInitializer : IDisposable
_partyWatchdog.Update(); _partyWatchdog.Update();
_questController.Update(); _questController.Update();
_fateController.Update(); _fateController.Update();
_seasonalDutyController.Update();
try try
{ {
_movementController.Update(); _movementController.Update();
@ -87,6 +92,7 @@ internal sealed class DalamudInitializer : IDisposable
{ {
_questController.Stop("Pathfinding failed"); _questController.Stop("Pathfinding failed");
_fateController.Stop("Pathfinding failed"); _fateController.Stop("Pathfinding failed");
_seasonalDutyController.Stop("Pathfinding failed");
} }
} }
@ -98,6 +104,8 @@ internal sealed class DalamudInitializer : IDisposable
private void OnErrorToast(ref SeString message, ref bool isHandled) private void OnErrorToast(ref SeString message, ref bool isHandled)
{ {
_logger.LogTrace("Error Toast: {Message}", message); _logger.LogTrace("Error Toast: {Message}", message);
_fateController.OnErrorToast(ref message, ref isHandled);
_seasonalDutyController.OnErrorToast(ref message, ref isHandled);
} }
private void OnQuestToast(ref SeString message, ref QuestToastOptions options, ref bool isHandled) private void OnQuestToast(ref SeString message, ref QuestToastOptions options, ref bool isHandled)

View file

@ -148,6 +148,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin, IDisposable
serviceCollection.AddTaskFactoryAndExecutor<WaitAtStart.WaitDelay, WaitAtStart.Factory, WaitAtStart.WaitDelayExecutor>(); serviceCollection.AddTaskFactoryAndExecutor<WaitAtStart.WaitDelay, WaitAtStart.Factory, WaitAtStart.WaitDelayExecutor>();
serviceCollection.AddTaskFactoryAndExecutor<MoveTask, MoveTo.Factory, MoveExecutor>(); serviceCollection.AddTaskFactoryAndExecutor<MoveTask, MoveTo.Factory, MoveExecutor>();
serviceCollection.AddTaskExecutor<WaitForNearDataId, WaitForNearDataIdExecutor>(); serviceCollection.AddTaskExecutor<WaitForNearDataId, WaitForNearDataIdExecutor>();
serviceCollection.AddTaskExecutor<MoveToObject, MoveToObjectExecutor>();
serviceCollection.AddTaskExecutor<LandTask, LandExecutor>(); serviceCollection.AddTaskExecutor<LandTask, LandExecutor>();
serviceCollection.AddTaskFactoryAndExecutor<SendNotification.Task, SendNotification.Factory, SendNotification.Executor>(); serviceCollection.AddTaskFactoryAndExecutor<SendNotification.Task, SendNotification.Factory, SendNotification.Executor>();
serviceCollection.AddTaskFactoryAndExecutor<NextQuest.SetQuestTask, NextQuest.Factory, NextQuest.NextQuestExecutor>(); serviceCollection.AddTaskFactoryAndExecutor<NextQuest.SetQuestTask, NextQuest.Factory, NextQuest.NextQuestExecutor>();
@ -165,6 +166,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin, IDisposable
serviceCollection.AddTaskFactory<Emote.Factory>(); serviceCollection.AddTaskFactory<Emote.Factory>();
serviceCollection.AddTaskExecutor<Emote.UseOnObject, Emote.UseOnObjectExecutor>(); serviceCollection.AddTaskExecutor<Emote.UseOnObject, Emote.UseOnObjectExecutor>();
serviceCollection.AddTaskExecutor<Emote.UseOnSelf, Emote.UseOnSelfExecutor>(); serviceCollection.AddTaskExecutor<Emote.UseOnSelf, Emote.UseOnSelfExecutor>();
serviceCollection.AddTaskFactoryAndExecutor<ClearObjectsWithAction.ClearTask, ClearObjectsWithAction.Factory, ClearObjectsWithAction.ClearTaskExecutor>();
serviceCollection.AddTaskFactoryAndExecutor<Questionable.Controller.Steps.Interactions.Action.UseOnObject, Questionable.Controller.Steps.Interactions.Action.Factory, Questionable.Controller.Steps.Interactions.Action.UseOnObjectExecutor>(); serviceCollection.AddTaskFactoryAndExecutor<Questionable.Controller.Steps.Interactions.Action.UseOnObject, Questionable.Controller.Steps.Interactions.Action.Factory, Questionable.Controller.Steps.Interactions.Action.UseOnObjectExecutor>();
serviceCollection.AddTaskExecutor<Questionable.Controller.Steps.Interactions.Action.UseMudraOnObject, Questionable.Controller.Steps.Interactions.Action.UseMudraOnObjectExecutor>(); serviceCollection.AddTaskExecutor<Questionable.Controller.Steps.Interactions.Action.UseMudraOnObject, Questionable.Controller.Steps.Interactions.Action.UseMudraOnObjectExecutor>();
serviceCollection.AddTaskExecutor<Questionable.Controller.Steps.Interactions.Action.TriggerStatusIfMissing, Questionable.Controller.Steps.Interactions.Action.TriggerStatusIfMissingExecutor>(); serviceCollection.AddTaskExecutor<Questionable.Controller.Steps.Interactions.Action.TriggerStatusIfMissing, Questionable.Controller.Steps.Interactions.Action.TriggerStatusIfMissingExecutor>();
@ -221,6 +223,8 @@ public sealed class QuestionablePlugin : IDalamudPlugin, IDisposable
serviceCollection.AddSingleton<CombatController>(); serviceCollection.AddSingleton<CombatController>();
serviceCollection.AddSingleton<GatheringController>(); serviceCollection.AddSingleton<GatheringController>();
serviceCollection.AddSingleton<FateController>(); serviceCollection.AddSingleton<FateController>();
serviceCollection.AddSingleton<SeasonalDutyDefinitionRegistry>();
serviceCollection.AddSingleton<SeasonalDutyController>();
serviceCollection.AddSingleton<ContextMenuController>(); serviceCollection.AddSingleton<ContextMenuController>();
serviceCollection.AddSingleton<ShopController>(); serviceCollection.AddSingleton<ShopController>();
serviceCollection.AddSingleton<InterruptHandler>(); serviceCollection.AddSingleton<InterruptHandler>();
@ -273,6 +277,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin, IDisposable
serviceCollection.AddSingleton<JournalProgressWindow>(); serviceCollection.AddSingleton<JournalProgressWindow>();
serviceCollection.AddSingleton<PriorityWindow>(); serviceCollection.AddSingleton<PriorityWindow>();
serviceCollection.AddSingleton<FateSelectionWindow>(); serviceCollection.AddSingleton<FateSelectionWindow>();
serviceCollection.AddSingleton<SeasonalDutySelectionWindow>();
serviceCollection.AddSingleton<GeneralConfigComponent>(); serviceCollection.AddSingleton<GeneralConfigComponent>();
serviceCollection.AddSingleton<PluginConfigComponent>(); serviceCollection.AddSingleton<PluginConfigComponent>();
serviceCollection.AddSingleton<DutyConfigComponent>(); serviceCollection.AddSingleton<DutyConfigComponent>();

View file

@ -3,7 +3,7 @@ using System.Runtime.CompilerServices;
namespace System.Text.RegularExpressions.Generated; namespace System.Text.RegularExpressions.Generated;
[GeneratedCode("System.Text.RegularExpressions.Generator", "10.0.14.7603")] [GeneratedCode("System.Text.RegularExpressions.Generator", "10.0.14.15411")]
[SkipLocalsInit] [SkipLocalsInit]
internal sealed class _003CRegexGenerator_g_003EFBB8301322196CF81C64F1652C2FA6E1D6BF3907141F781E9D97ABED51BF056C4__MultipleWhitespaceRegex_0 : Regex internal sealed class _003CRegexGenerator_g_003EFBB8301322196CF81C64F1652C2FA6E1D6BF3907141F781E9D97ABED51BF056C4__MultipleWhitespaceRegex_0 : Regex
{ {

View file

@ -3,7 +3,7 @@ using System.CodeDom.Compiler;
namespace System.Text.RegularExpressions.Generated; namespace System.Text.RegularExpressions.Generated;
[GeneratedCode("System.Text.RegularExpressions.Generator", "10.0.14.7603")] [GeneratedCode("System.Text.RegularExpressions.Generator", "10.0.14.15411")]
internal static class _003CRegexGenerator_g_003EFBB8301322196CF81C64F1652C2FA6E1D6BF3907141F781E9D97ABED51BF056C4__Utilities internal static class _003CRegexGenerator_g_003EFBB8301322196CF81C64F1652C2FA6E1D6BF3907141F781E9D97ABED51BF056C4__Utilities
{ {
internal static readonly TimeSpan s_defaultTimeout = ((AppContext.GetData("REGEX_DEFAULT_MATCH_TIMEOUT") is TimeSpan timeSpan) ? timeSpan : Regex.InfiniteMatchTimeout); internal static readonly TimeSpan s_defaultTimeout = ((AppContext.GetData("REGEX_DEFAULT_MATCH_TIMEOUT") is TimeSpan timeSpan) ? timeSpan : Regex.InfiniteMatchTimeout);

View file

@ -0,0 +1,104 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/WigglyMuffin/Questionable/raw/refs/heads/main/SeasonalDutyPaths/seasonaldutydefinition-v1.json",
"title": "Seasonal Duty Definition V1",
"description": "A seasonal duty farming definition",
"type": "object",
"properties": {
"$schema": {
"type": "string"
},
"Name": {
"description": "Display name of the seasonal duty",
"type": "string"
},
"TerritoryId": {
"description": "Territory ID where the NPC is located",
"type": "integer",
"minimum": 1
},
"Aetheryte": {
"description": "Nearest aetheryte for teleporting",
"$ref": "https://github.com/WigglyMuffin/Questionable/raw/refs/heads/main/Questionable.Model/common-aetheryte.json"
},
"AethernetShortcut": {
"description": "Aethernet shortcut to use after teleporting",
"type": "array",
"items": {
"type": "string"
},
"minItems": 2,
"maxItems": 2
},
"NpcDataId": {
"description": "Data ID of the NPC to interact with",
"type": "integer",
"minimum": 1
},
"NpcPosition": {
"description": "Position of the NPC",
"$ref": "https://github.com/WigglyMuffin/Questionable/raw/refs/heads/main/Questionable.Model/common-vector3.json"
},
"DialogueChoices": {
"description": "Dialogue choices when interacting with the NPC to enter the duty",
"type": "array",
"items": {
"type": "object"
}
},
"DutyTerritoryId": {
"description": "Territory ID of the duty instance",
"type": "integer",
"minimum": 1
},
"DataIds": {
"description": "Data IDs of objects to interact with inside the duty",
"type": "array",
"items": {
"type": "integer",
"minimum": 1
},
"minItems": 1
},
"WaypointPositions": {
"description": "Waypoint positions to navigate through inside the duty",
"type": "array",
"items": {
"$ref": "https://github.com/WigglyMuffin/Questionable/raw/refs/heads/main/Questionable.Model/common-vector3.json"
}
},
"Action": {
"description": "Action to perform on each object",
"type": "string"
},
"StopDistance": {
"description": "Distance at which to stop and perform the action",
"type": "number",
"minimum": 0
},
"RequiredQuestId": {
"description": "Quest ID that must be completed before this duty can be farmed",
"type": "integer",
"minimum": 1
},
"EventExpiry": {
"description": "Date and time (in UTC) when the seasonal event ends",
"format": "date-time",
"type": [
"string",
"null"
]
}
},
"required": [
"$schema",
"Name",
"TerritoryId",
"NpcDataId",
"NpcPosition",
"DutyTerritoryId",
"DataIds",
"Action"
],
"additionalProperties": false
}

View file

@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Numerics;
using System.Runtime.InteropServices;
using Questionable.Model.Common;
using Questionable.Model.Questing;
namespace Questionable.SeasonalDutyPaths;
public static class AssemblySeasonalDutyDefinitionLoader
{
private static Dictionary<ushort, SeasonalDutyDefinition>? _definitions;
public static Stream SeasonalDutyDefinitionSchema => typeof(AssemblySeasonalDutyDefinitionLoader).Assembly.GetManifestResourceStream("Questionable.SeasonalDutyPaths.SeasonalDutyDefinitionSchema");
public static IReadOnlyDictionary<ushort, SeasonalDutyDefinition> GetDefinitions()
{
if (_definitions == null)
{
_definitions = new Dictionary<ushort, SeasonalDutyDefinition>();
LoadDefinitions();
}
return _definitions ?? throw new InvalidOperationException("seasonal duty definition data is not initialized");
}
private static void AddDefinition(ushort id, SeasonalDutyDefinition definition)
{
_definitions[id] = definition;
}
private static void LoadDefinitions()
{
LoadDefinition0();
}
private static void LoadDefinition0()
{
SeasonalDutyDefinition obj = new SeasonalDutyDefinition
{
Name = "Hatching-tide (2026)",
TerritoryId = 133,
Aetheryte = EAetheryteLocation.Gridania,
AethernetShortcut = new AethernetShortcut
{
From = EAetheryteLocation.Gridania,
To = EAetheryteLocation.GridaniaAmphitheatre
},
NpcDataId = 1056067u,
NpcPosition = new Vector3(-54.551025f, 6.851239f, -110.948364f)
};
int num = 1;
List<DialogueChoice> list = new List<DialogueChoice>(num);
CollectionsMarshal.SetCount(list, num);
CollectionsMarshal.AsSpan(list)[0] = new DialogueChoice
{
Type = EDialogChoiceType.List,
ExcelSheet = "custom/009/FesEst2026Entrance_00960",
Prompt = new ExcelRef("TEXT_FESEST2026ENTRANCE_00960_Q1_000_000"),
Answer = new ExcelRef("TEXT_FESEST2026ENTRANCE_00960_A1_000_001"),
PromptIsRegularExpression = true
};
obj.DialogueChoices = list;
obj.DutyTerritoryId = 1336;
num = 8;
List<uint> list2 = new List<uint>(num);
CollectionsMarshal.SetCount(list2, num);
Span<uint> span = CollectionsMarshal.AsSpan(list2);
span[0] = 2015072u;
span[1] = 2015073u;
span[2] = 2015074u;
span[3] = 2015075u;
span[4] = 2015076u;
span[5] = 2015077u;
span[6] = 2015078u;
span[7] = 2015079u;
obj.DataIds = list2;
num = 9;
List<Vector3> list3 = new List<Vector3>(num);
CollectionsMarshal.SetCount(list3, num);
Span<Vector3> span2 = CollectionsMarshal.AsSpan(list3);
span2[0] = new Vector3(-700.0023f, -0.15729797f, -721.67926f);
span2[1] = new Vector3(-700.1704f, -0.0301615f, 77.54147f);
span2[2] = new Vector3(-699.9926f, -0.021064043f, 877.55304f);
span2[3] = new Vector3(99.99213f, -0.100738764f, -721.9226f);
span2[4] = new Vector3(99.92716f, 4.7683716E-07f, 77.599335f);
span2[5] = new Vector3(100.40486f, 0.20596194f, 877.50397f);
span2[6] = new Vector3(899.93384f, 0.03615737f, -722.4109f);
span2[7] = new Vector3(900.052f, 0.23717809f, 77.897415f);
span2[8] = new Vector3(900.26733f, -0.30200624f, 877.5144f);
obj.WaypointPositions = list3;
obj.Action = EAction.PruningPirouette;
obj.RequiredQuestId = (ushort)5425;
obj.EventExpiry = new DateTime(2026, 4, 6, 14, 59, 0, DateTimeKind.Utc);
AddDefinition(1, obj);
}
}

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>SeasonalDutyPaths</AssemblyName>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<TargetFramework>netcoreapp1.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<LangVersion>12.0</LangVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<None Remove="Questionable.SeasonalDutyPaths.SeasonalDutyDefinitionSchema" />
<EmbeddedResource Include="Questionable.SeasonalDutyPaths.SeasonalDutyDefinitionSchema" LogicalName="Questionable.SeasonalDutyPaths.SeasonalDutyDefinitionSchema" />
</ItemGroup>
<ItemGroup>
<Reference Include="Questionable.Model">
<HintPath>..\..\Questionable.Model.dll</HintPath>
</Reference>
</ItemGroup>
</Project>