Consider alternative Lv1 class quests locked; don't do priority quests unless we can teleport + aren't broke
This commit is contained in:
parent
f9368ae809
commit
052c366ea0
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||
using LLib.GameData;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Questionable.Model;
|
||||
@ -21,6 +22,7 @@ internal sealed class QuestData
|
||||
public static readonly IReadOnlyList<ushort> MeleeRoleQuests = [138, 156, 180];
|
||||
public static readonly IReadOnlyList<ushort> PhysicalRangedRoleQuests = [138, 157, 181];
|
||||
public static readonly IReadOnlyList<ushort> CasterRoleQuests = [139, 158, 182];
|
||||
|
||||
public static readonly IReadOnlyList<IReadOnlyList<ushort>> AllRoleQuestChapters =
|
||||
[
|
||||
TankRoleQuests,
|
||||
@ -59,7 +61,7 @@ internal sealed class QuestData
|
||||
_quests = quests.ToDictionary(x => x.QuestId, x => x);
|
||||
|
||||
// workaround because the game doesn't require completion of the CT questline through normal means
|
||||
QuestInfo aTimeToEveryPurpose = (QuestInfo) _quests[new QuestId(425)];
|
||||
QuestInfo aTimeToEveryPurpose = (QuestInfo)_quests[new QuestId(425)];
|
||||
aTimeToEveryPurpose.AddPreviousQuest(new QuestId(495));
|
||||
}
|
||||
|
||||
@ -180,4 +182,32 @@ internal sealed class QuestData
|
||||
.Where(x => chapterIds.Contains(x.NewGamePlusChapter))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public List<QuestId> GetLockedClassQuests()
|
||||
{
|
||||
EClassJob startingClass;
|
||||
unsafe
|
||||
{
|
||||
var playerState = PlayerState.Instance();
|
||||
if (playerState != null)
|
||||
startingClass = (EClassJob)playerState->FirstClass;
|
||||
else
|
||||
startingClass = EClassJob.Adventurer;
|
||||
}
|
||||
|
||||
if (startingClass == EClassJob.Adventurer)
|
||||
return [];
|
||||
|
||||
return
|
||||
[
|
||||
startingClass == EClassJob.Gladiator ? new(177) : new(253),
|
||||
startingClass == EClassJob.Pugilist ? new(178) : new(533),
|
||||
startingClass == EClassJob.Marauder ? new(179) : new(311),
|
||||
startingClass == EClassJob.Lancer ? new(180) : new(23),
|
||||
startingClass == EClassJob.Archer ? new(181) : new(21),
|
||||
startingClass == EClassJob.Conjurer ? new(182) : new(22),
|
||||
startingClass == EClassJob.Thaumaturge ? new(183) : new(345),
|
||||
startingClass == EClassJob.Arcanist ? new(451) : new(453),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Model.Common;
|
||||
@ -10,13 +13,19 @@ namespace Questionable.Functions;
|
||||
|
||||
internal sealed unsafe class AetheryteFunctions
|
||||
{
|
||||
private const uint TeleportAction = 5;
|
||||
private const uint ReturnAction = 8;
|
||||
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILogger<AetheryteFunctions> _logger;
|
||||
private readonly IDataManager _dataManager;
|
||||
|
||||
public AetheryteFunctions(IServiceProvider serviceProvider, ILogger<AetheryteFunctions> logger)
|
||||
public AetheryteFunctions(IServiceProvider serviceProvider, ILogger<AetheryteFunctions> logger,
|
||||
IDataManager dataManager)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_logger = logger;
|
||||
_dataManager = dataManager;
|
||||
}
|
||||
|
||||
public DateTime ReturnRequestedAt { get; set; } = DateTime.MinValue;
|
||||
@ -39,10 +48,18 @@ internal sealed unsafe class AetheryteFunctions
|
||||
public bool CanTeleport(EAetheryteLocation aetheryteLocation)
|
||||
{
|
||||
if ((ushort)aetheryteLocation == PlayerState.Instance()->HomeAetheryteId &&
|
||||
ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 8) == 0)
|
||||
ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, ReturnAction) == 0)
|
||||
return true;
|
||||
|
||||
return ActionManager.Instance()->GetActionStatus(ActionType.Action, 5) == 0;
|
||||
return ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, TeleportAction) == 0;
|
||||
}
|
||||
|
||||
public bool IsTeleportUnlocked()
|
||||
{
|
||||
ushort unlockLink = _dataManager.GetExcelSheet<GeneralAction>()!
|
||||
.Single(x => x.Action.Row == 5)
|
||||
.UnlockLink;
|
||||
return UIState.Instance()->IsUnlockLinkUnlocked(unlockLink);
|
||||
}
|
||||
|
||||
public bool TeleportAetheryte(uint aetheryteId)
|
||||
@ -51,17 +68,17 @@ internal sealed unsafe class AetheryteFunctions
|
||||
if (IsAetheryteUnlocked(aetheryteId, out var subIndex))
|
||||
{
|
||||
if (aetheryteId == PlayerState.Instance()->HomeAetheryteId &&
|
||||
ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 8) == 0)
|
||||
ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, ReturnAction) == 0)
|
||||
{
|
||||
ReturnRequestedAt = DateTime.Now;
|
||||
if (ActionManager.Instance()->UseAction(ActionType.GeneralAction, 8))
|
||||
if (ActionManager.Instance()->UseAction(ActionType.GeneralAction, ReturnAction))
|
||||
{
|
||||
_logger.LogInformation("Using 'return' for home aetheryte");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ActionManager.Instance()->GetActionStatus(ActionType.Action, 5) == 0)
|
||||
if (ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, TeleportAction) == 0)
|
||||
{
|
||||
// fallback if return isn't available or (more likely) on a different aetheryte
|
||||
_logger.LogInformation("Teleporting to aetheryte {AetheryteId}", aetheryteId);
|
||||
|
@ -241,8 +241,21 @@ internal sealed unsafe class QuestFunctions
|
||||
|
||||
public ElementId? GetNextPriorityQuestThatCanBeAccepted()
|
||||
{
|
||||
// all priority quests assume we're able to teleport to the beginning (and for e.g. class quests, the end)
|
||||
// ideally without having to wait 15m for Return.
|
||||
if (!_aetheryteFunctions.IsTeleportUnlocked())
|
||||
return null;
|
||||
|
||||
// ideally, we'd also be able to afford *some* teleports
|
||||
// this implicitly makes sure we're not starting one of the lv1 class quests if we can't afford to teleport back
|
||||
//
|
||||
// Of course, they can still be accepted manually.
|
||||
InventoryManager* inventoryManager = InventoryManager.Instance();
|
||||
if (inventoryManager->GetItemCountInContainer(1, InventoryType.Currency) < 2000)
|
||||
return null;
|
||||
|
||||
return GetPriorityQuestsThatCanBeAccepted()
|
||||
.FirstOrDefault(x =>
|
||||
.Where(x =>
|
||||
{
|
||||
if (!_questRegistry.TryGetQuest(x, out Quest? quest))
|
||||
return false;
|
||||
@ -251,8 +264,7 @@ internal sealed unsafe class QuestFunctions
|
||||
if (firstStep == null)
|
||||
return false;
|
||||
|
||||
if (firstStep.AetheryteShortcut is { } aetheryteShortcut &&
|
||||
_aetheryteFunctions.IsAetheryteUnlocked(aetheryteShortcut))
|
||||
if (firstStep.AetheryteShortcut != null)
|
||||
return true;
|
||||
|
||||
if (firstStep is
|
||||
@ -260,6 +272,25 @@ internal sealed unsafe class QuestFunctions
|
||||
return true;
|
||||
|
||||
return false;
|
||||
})
|
||||
.FirstOrDefault(x =>
|
||||
{
|
||||
if (!_questRegistry.TryGetQuest(x, out Quest? quest))
|
||||
return false;
|
||||
|
||||
return quest.AllSteps().All(x =>
|
||||
{
|
||||
if (x.Step.AetheryteShortcut is { } aetheryteShortcut &&
|
||||
_aetheryteFunctions.IsAetheryteUnlocked(aetheryteShortcut))
|
||||
return false;
|
||||
|
||||
if (x.Step.AethernetShortcut is { } aethernetShortcut &&
|
||||
(!_aetheryteFunctions.IsAetheryteUnlocked(aethernetShortcut.From) ||
|
||||
!_aetheryteFunctions.IsAetheryteUnlocked(aethernetShortcut.To)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -415,6 +446,9 @@ internal sealed unsafe class QuestFunctions
|
||||
if (questInfo.GrandCompany != GrandCompany.None && questInfo.GrandCompany != GetGrandCompany())
|
||||
return true;
|
||||
|
||||
if (_questData.GetLockedClassQuests().Contains(questId))
|
||||
return true;
|
||||
|
||||
return !HasCompletedPreviousQuests(questInfo, extraCompletedQuest) || !HasCompletedPreviousInstances(questInfo);
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,12 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Plugin.Services;
|
||||
using ImGuiNET;
|
||||
using Questionable.Controller;
|
||||
using Questionable.Data;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Windows;
|
||||
|
Loading…
Reference in New Issue
Block a user