Add dubious free fantasia handling

This commit is contained in:
Liza 2025-03-03 23:07:55 +01:00
parent caf336c078
commit b46d0868bf
Signed by: liza
GPG Key ID: 2C41B84815CF6445
14 changed files with 289 additions and 48 deletions

View File

@ -152,6 +152,10 @@ internal static class SkipConditionsExtensions
emptyAetheryte.RequiredQuestVariablesNotMet)
.AsSyntaxNodeOrToken(),
Assignment(nameof(skipAetheryteCondition.NearPosition), skipAetheryteCondition.NearPosition,
emptyAetheryte.NearPosition).AsSyntaxNodeOrToken()))));
emptyAetheryte.NearPosition)
.AsSyntaxNodeOrToken(),
Assignment(nameof(skipAetheryteCondition.ExtraCondition), skipAetheryteCondition.ExtraCondition,
emptyAetheryte.ExtraCondition)
.AsSyntaxNodeOrToken()))));
}
}

View File

@ -26,6 +26,33 @@
{
"Sequence": 1,
"Steps": [
{
"DataId": 1052475,
"Position": {
"X": -22.354492,
"Y": 10.13581,
"Z": -241.41296
},
"TerritoryId": 133,
"InteractionType": "Interact",
"AetheryteShortcut": "Gridania",
"AethernetShortcut": [
"[Gridania] Aetheryte Plaza",
"[Gridania] Mih Khetto's Amphitheatre"
],
"SkipConditions": {
"AetheryteShortcutIf": {
"ExtraCondition": "SkipFreeFantasia",
"InSameTerritory": true,
"InTerritory": [
133
]
},
"StepIf": {
"ExtraCondition": "SkipFreeFantasia"
}
}
},
{
"DataId": 1051889,
"Position": {
@ -35,10 +62,16 @@
},
"TerritoryId": 131,
"InteractionType": "Interact",
"AetheryteShortcut": "Ul'dah",
"AethernetShortcut": [
"[Ul'dah] Adventurers' Guild",
"[Ul'dah] Aetheryte Plaza",
"[Ul'dah] Goldsmiths' Guild"
]
],
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
}
]
},

View File

@ -303,7 +303,8 @@
"RisingStonesSolar",
"RoguesGuild",
"NotRoguesGuild",
"DockStorehouse"
"DockStorehouse",
"SkipFreeFantasia"
]
}
},
@ -370,6 +371,12 @@
"TerritoryId"
],
"additionalProperties": false
},
"ExtraCondition": {
"type": "string",
"enum": [
"SkipFreeFantasia"
]
}
},
"additionalProperties": false

View File

@ -13,5 +13,6 @@ public sealed class SkipConditionConverter() : EnumConverter<EExtraSkipCondition
{ EExtraSkipCondition.RoguesGuild, "RoguesGuild"},
{ EExtraSkipCondition.NotRoguesGuild, "NotRoguesGuild"},
{ EExtraSkipCondition.DockStorehouse, "DockStorehouse"},
{ EExtraSkipCondition.SkipFreeFantasia, "SkipFreeFantasia" },
};
}

View File

@ -21,4 +21,6 @@ public enum EExtraSkipCondition
/// Location for NIN quests in Eastern La Noscea; located far underneath the actual zone.
/// </summary>
DockStorehouse,
SkipFreeFantasia,
}

View File

@ -21,4 +21,5 @@ public sealed class SkipAetheryteCondition
public EAetheryteLocation? AetheryteUnlocked { get; set; }
public bool RequiredQuestVariablesNotMet { get; set; }
public NearPositionCondition? NearPosition { get; set; }
public EExtraSkipCondition? ExtraCondition { get; set; }
}

View File

@ -34,6 +34,9 @@ internal sealed class Configuration : IPluginConfiguration
public bool UseEscToCancelQuesting { get; set; } = true;
public bool ShowIncompleteSeasonalEvents { get; set; } = true;
public bool ConfigureTextAdvance { get; set; } = true;
// TODO Temporary setting for 7.1
public bool PickUpFreeFantasia { get; set; } = true;
}
internal sealed class DutyConfiguration

View File

@ -58,7 +58,7 @@ internal static class Interact
yield return new Task(step.DataId.Value, quest, step.InteractionType,
step.TargetTerritoryId != null || quest.Id is SatisfactionSupplyNpcId ||
step.SkipConditions is { StepIf.Never: true } || step.InteractionType == EInteractionType.PurchaseItem,
step.SkipConditions is { StepIf.Never: true } || step.InteractionType == EInteractionType.PurchaseItem || step.DataId == 1052475,
step.PickUpItemId, step.SkipConditions?.StepIf, step.CompletionQuestVariablesFlags);
}
}

View File

@ -58,7 +58,8 @@ internal static class AetheryteShortcut
IClientState clientState,
IChatGui chatGui,
ICondition condition,
AetheryteData aetheryteData) : TaskExecutor<Task>
AetheryteData aetheryteData,
ExtraConditionUtils extraConditionUtils) : TaskExecutor<Task>
{
private bool _teleported;
private DateTime _continueAt;
@ -148,6 +149,13 @@ internal static class AetheryteShortcut
return true;
}
}
if (skipConditions.ExtraCondition != null && skipConditions.ExtraCondition != EExtraSkipCondition.None &&
extraConditionUtils.MatchesExtraCondition(skipConditions.ExtraCondition.Value))
{
logger.LogInformation("Skipping step, extra condition {} matches", skipConditions.ExtraCondition);
return true;
}
}
if (Task.ExpectedTerritoryId == territoryType)

View File

@ -0,0 +1,75 @@
using System;
using System.Numerics;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using Microsoft.Extensions.Logging;
using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Shared;
internal sealed class ExtraConditionUtils
{
private readonly Configuration _configuration;
private readonly IClientState _clientState;
private readonly ILogger<ExtraConditionUtils> _logger;
public ExtraConditionUtils(
Configuration configuration,
IClientState clientState,
ILogger<ExtraConditionUtils> logger)
{
_configuration = configuration;
_clientState = clientState;
_logger = logger;
}
public bool MatchesExtraCondition(EExtraSkipCondition skipCondition)
{
var position = _clientState.LocalPlayer?.Position;
return position != null &&
_clientState.TerritoryType != 0 &&
MatchesExtraCondition(skipCondition, position.Value, _clientState.TerritoryType);
}
public bool MatchesExtraCondition(EExtraSkipCondition skipCondition, Vector3 position, ushort territoryType)
{
return skipCondition switch
{
EExtraSkipCondition.WakingSandsMainArea => territoryType == 212 && position.X < 24,
EExtraSkipCondition.WakingSandsSolar => territoryType == 212 && position.X >= 24,
EExtraSkipCondition.RisingStonesSolar => territoryType == 351 && position.Z <= -28,
EExtraSkipCondition.RoguesGuild => territoryType == 129 && position.Y <= -115,
EExtraSkipCondition.NotRoguesGuild => territoryType == 129 && position.Y > -115,
EExtraSkipCondition.DockStorehouse => territoryType == 137 && position.Y <= -20,
EExtraSkipCondition.SkipFreeFantasia => ShouldSkipFreeFantasia(),
_ => throw new ArgumentOutOfRangeException(nameof(skipCondition), skipCondition, null)
};
}
private unsafe bool ShouldSkipFreeFantasia()
{
if (!_configuration.General.PickUpFreeFantasia)
{
_logger.LogInformation("Skipping fantasia step, as free fantasia is disabled in the configuration");
return true;
}
bool foundFestival = false;
for (int i = 0; i < GameMain.Instance()->ActiveFestivals.Length; ++i)
{
if (GameMain.Instance()->ActiveFestivals[i].Id == 160)
{
foundFestival = true;
break;
}
}
if (!foundFestival)
{
_logger.LogInformation("Skipping fantasia step, as free fantasia moogle is not available");
return true;
}
return false;
}
}

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Game.ClientState.Conditions;
@ -56,9 +55,10 @@ internal static class SkipCondition
GameFunctions gameFunctions,
QuestFunctions questFunctions,
IClientState clientState,
ICondition condition) : TaskExecutor<SkipTask>
ICondition condition,
ExtraConditionUtils extraConditionUtils) : TaskExecutor<SkipTask>
{
protected override unsafe bool Start()
protected override bool Start()
{
var skipConditions = Task.SkipConditions;
var step = Task.Step;
@ -66,6 +66,60 @@ internal static class SkipCondition
logger.LogInformation("Checking skip conditions; {ConfiguredConditions}", string.Join(",", skipConditions));
if (CheckFlyingCondition(step, skipConditions))
return true;
if (CheckUnlockedMountCondition(skipConditions))
return true;
if (CheckDivingCondition(skipConditions))
return true;
if (CheckTerritoryCondition(skipConditions))
return true;
if (CheckQuestConditions(skipConditions))
return true;
if (CheckTargetableCondition(step, skipConditions))
return true;
if (CheckNameplateCondition(step, skipConditions))
return true;
if (CheckItemCondition(step, skipConditions))
return true;
if (CheckAetheryteCondition(step, skipConditions))
return true;
if (CheckAethernetCondition(step))
return true;
if (CheckQuestWorkConditions(elementId, step))
return true;
if (CheckJobCondition(step))
return true;
if (CheckPositionCondition(skipConditions))
return true;
if (skipConditions.ExtraCondition != null && skipConditions.ExtraCondition != EExtraSkipCondition.None &&
extraConditionUtils.MatchesExtraCondition(skipConditions.ExtraCondition.Value))
{
logger.LogInformation("Skipping step, extra condition {} matches", skipConditions.ExtraCondition);
return true;
}
if (CheckPickUpTurnInQuestIds(step))
return true;
return false;
}
private bool CheckFlyingCondition(QuestStep step, SkipStepConditions skipConditions)
{
if (skipConditions.Flying == ELockedSkipCondition.Unlocked &&
gameFunctions.IsFlyingUnlocked(step.TerritoryId))
{
@ -80,6 +134,11 @@ internal static class SkipCondition
return true;
}
return false;
}
private unsafe bool CheckUnlockedMountCondition(SkipStepConditions skipConditions)
{
if (skipConditions.Chocobo == ELockedSkipCondition.Unlocked &&
PlayerState.Instance()->IsMountUnlocked(1))
{
@ -87,18 +146,11 @@ internal static class SkipCondition
return true;
}
if (skipConditions.Diving == true && condition[ConditionFlag.Diving])
{
logger.LogInformation("Skipping step, as you're currently diving underwater");
return true;
}
if (skipConditions.Diving == false && !condition[ConditionFlag.Diving])
{
logger.LogInformation("Skipping step, as you're not currently diving underwater");
return true;
}
return false;
}
private bool CheckTerritoryCondition(SkipStepConditions skipConditions)
{
if (skipConditions.InTerritory.Count > 0 &&
skipConditions.InTerritory.Contains(clientState.TerritoryType))
{
@ -113,6 +165,28 @@ internal static class SkipCondition
return true;
}
return false;
}
private bool CheckDivingCondition(SkipStepConditions skipConditions)
{
if (skipConditions.Diving == true && condition[ConditionFlag.Diving])
{
logger.LogInformation("Skipping step, as you're currently diving underwater");
return true;
}
if (skipConditions.Diving == false && !condition[ConditionFlag.Diving])
{
logger.LogInformation("Skipping step, as you're not currently diving underwater");
return true;
}
return false;
}
private bool CheckQuestConditions(SkipStepConditions skipConditions)
{
if (skipConditions.QuestsCompleted.Count > 0 &&
skipConditions.QuestsCompleted.All(questFunctions.IsQuestComplete))
{
@ -127,6 +201,11 @@ internal static class SkipCondition
return true;
}
return false;
}
private bool CheckTargetableCondition(QuestStep step, SkipStepConditions skipConditions)
{
if (skipConditions.NotTargetable &&
step is { DataId: not null })
{
@ -146,6 +225,11 @@ internal static class SkipCondition
}
}
return false;
}
private unsafe bool CheckNameplateCondition(QuestStep step, SkipStepConditions skipConditions)
{
if (skipConditions.NotNamePlateIconId.Count > 0 &&
step is { DataId: not null })
{
@ -162,6 +246,11 @@ internal static class SkipCondition
}
}
return false;
}
private unsafe bool CheckItemCondition(QuestStep step, SkipStepConditions skipConditions)
{
if (skipConditions.Item is { NotInInventory: true } && step is { ItemId: not null })
{
InventoryManager* inventoryManager = InventoryManager.Instance();
@ -174,6 +263,11 @@ internal static class SkipCondition
}
}
return false;
}
private bool CheckAetheryteCondition(QuestStep step, SkipStepConditions skipConditions)
{
if (step is
{
DataId: not null,
@ -199,6 +293,11 @@ internal static class SkipCondition
return true;
}
return false;
}
private bool CheckAethernetCondition(QuestStep step)
{
if (step is { DataId: not null, InteractionType: EInteractionType.AttuneAetherCurrent } &&
gameFunctions.IsAetherCurrentUnlocked(step.DataId.Value))
{
@ -206,6 +305,11 @@ internal static class SkipCondition
return true;
}
return false;
}
private bool CheckQuestWorkConditions(ElementId elementId, QuestStep step)
{
QuestProgressInfo? questWork = questFunctions.GetQuestProgressInfo(elementId);
if (questWork != null)
{
@ -249,6 +353,11 @@ internal static class SkipCondition
}
}
return false;
}
private bool CheckJobCondition(QuestStep step)
{
if (step is { RequiredCurrentJob.Count: > 0 })
{
List<EClassJob> expectedJobs =
@ -263,6 +372,11 @@ internal static class SkipCondition
}
}
return false;
}
private bool CheckPositionCondition(SkipStepConditions skipConditions)
{
if (skipConditions.NearPosition is { } nearPosition &&
clientState.TerritoryType == nearPosition.TerritoryId)
{
@ -274,19 +388,11 @@ internal static class SkipCondition
}
}
if (skipConditions.ExtraCondition != null && skipConditions.ExtraCondition != EExtraSkipCondition.None)
{
var position = clientState.LocalPlayer?.Position;
if (position != null &&
clientState.TerritoryType != 0 &&
MatchesExtraCondition(skipConditions.ExtraCondition.Value, position.Value,
clientState.TerritoryType))
{
logger.LogInformation("Skipping step, extra condition {} matches", skipConditions.ExtraCondition);
return true;
}
}
return false;
}
private bool CheckPickUpTurnInQuestIds(QuestStep step)
{
if (step.PickUpQuestId != null && questFunctions.IsQuestAcceptedOrComplete(step.PickUpQuestId))
{
logger.LogInformation("Skipping step, as we have already picked up the relevant quest");
@ -302,20 +408,6 @@ internal static class SkipCondition
return false;
}
private static bool MatchesExtraCondition(EExtraSkipCondition condition, Vector3 position, ushort territoryType)
{
return condition switch
{
EExtraSkipCondition.WakingSandsMainArea => territoryType == 212 && position.X < 24,
EExtraSkipCondition.WakingSandsSolar => territoryType == 212 && position.X >= 24,
EExtraSkipCondition.RisingStonesSolar => territoryType == 351 && position.Z <= -28,
EExtraSkipCondition.RoguesGuild => territoryType == 129 && position.Y <= -115,
EExtraSkipCondition.NotRoguesGuild => territoryType == 129 && position.Y > -115,
EExtraSkipCondition.DockStorehouse => territoryType == 137 && position.Y <= -20,
_ => throw new ArgumentOutOfRangeException(nameof(condition), condition, null)
};
}
public override ETaskResult Update() => ETaskResult.SkipRemainingTasksForStep;
public override bool ShouldInterruptOnDamage() => false;

View File

@ -247,6 +247,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
serviceCollection.AddTaskExecutor<WaitAtEnd.EndAutomation, WaitAtEnd.EndAutomationExecutor>();
serviceCollection.AddSingleton<TaskCreator>();
serviceCollection.AddSingleton<ExtraConditionUtils>();
}
private static void AddControllers(ServiceCollection serviceCollection)

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
@ -110,5 +111,18 @@ internal sealed class GeneralConfigComponent : ConfigComponent
Configuration.General.ConfigureTextAdvance = configureTextAdvance;
Save();
}
ImGui.Separator();
ImGui.TextColored(ImGuiColors.DalamudYellow, "Patch 7.1 exclusive content");
using (_ = ImRaii.PushIndent())
{
bool pickUpFreeFantasia = Configuration.General.PickUpFreeFantasia;
if (ImGui.Checkbox("Try to pick up free limited-time fantasia during 'Little Ladies' Day' quests",
ref pickUpFreeFantasia))
{
Configuration.General.PickUpFreeFantasia = pickUpFreeFantasia;
Save();
}
}
}
}

View File

@ -22,7 +22,7 @@ internal sealed class EventInfoComponent
[SuppressMessage("ReSharper", "CollectionNeverUpdated.Local")]
private readonly List<EventQuest> _eventQuests =
[
new("Valentione's Day", [new(5251)], AtDailyReset(new(2025, 2, 17))),
new("Valentione's Day", [new(5237), new(5238)], AtDailyReset(new(2025, 3, 17))),
];
private readonly QuestData _questData;