mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 10:17:22 +01:00
Merge pull request #2421 from Haselnussbomber/unlockstate-service
Add IUnlockState service
This commit is contained in:
commit
596af24e95
4 changed files with 1564 additions and 0 deletions
95
Dalamud/Game/UnlockState/ItemActionType.cs
Normal file
95
Dalamud/Game/UnlockState/ItemActionType.cs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
using Lumina.Excel.Sheets;
|
||||
|
||||
namespace Dalamud.Game.UnlockState;
|
||||
|
||||
/// <summary>
|
||||
/// Enum for <see cref="ItemAction.Type"/>.
|
||||
/// </summary>
|
||||
internal enum ItemActionType : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// No item action.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks a companion (minion).
|
||||
/// </summary>
|
||||
Companion = 853,
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks a chocobo companion barding.
|
||||
/// </summary>
|
||||
BuddyEquip = 1013,
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks a mount.
|
||||
/// </summary>
|
||||
Mount = 1322,
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks recipes from a crafting recipe book.
|
||||
/// </summary>
|
||||
SecretRecipeBook = 2136,
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks various types of content (e.g. Riding Maps, Blue Mage Totems, Emotes, Hairstyles).
|
||||
/// </summary>
|
||||
UnlockLink = 2633,
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks a Triple Triad Card.
|
||||
/// </summary>
|
||||
TripleTriadCard = 3357,
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks gathering nodes of a Folklore Tome.
|
||||
/// </summary>
|
||||
FolkloreTome = 4107,
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks an Orchestrion Roll.
|
||||
/// </summary>
|
||||
OrchestrionRoll = 25183,
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks portrait designs.
|
||||
/// </summary>
|
||||
FramersKit = 29459,
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks Bozjan Field Notes.
|
||||
/// </summary>
|
||||
/// <remarks> These are server-side but are cached client-side. </remarks>
|
||||
FieldNotes = 19743,
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks an Ornament (fashion accessory).
|
||||
/// </summary>
|
||||
Ornament = 20086,
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks Glasses.
|
||||
/// </summary>
|
||||
Glasses = 37312,
|
||||
|
||||
/// <summary>
|
||||
/// Company Seal Vouchers, which convert the item into Company Seals when used.
|
||||
/// </summary>
|
||||
CompanySealVouchers = 41120,
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks Occult Records in Occult Crescent.
|
||||
/// </summary>
|
||||
OccultRecords = 43141,
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks Phantom Jobs in Occult Crescent.
|
||||
/// </summary>
|
||||
SoulShards = 43142,
|
||||
|
||||
/// <summary>
|
||||
/// Star Contributor Certificate, which grants the Star Contributor status in Cosmic Exploration.
|
||||
/// </summary>
|
||||
StarContributorCertificate = 45189,
|
||||
}
|
||||
283
Dalamud/Game/UnlockState/RecipeData.cs
Normal file
283
Dalamud/Game/UnlockState/RecipeData.cs
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
using System.Linq;
|
||||
|
||||
using CommunityToolkit.HighPerformance;
|
||||
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game.Gui;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||
using FFXIVClientStructs.Interop;
|
||||
|
||||
using Lumina.Excel.Sheets;
|
||||
|
||||
namespace Dalamud.Game.UnlockState;
|
||||
|
||||
/// <summary>
|
||||
/// Represents recipe-related data for all crafting classes.
|
||||
/// </summary>
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
internal unsafe class RecipeData : IInternalDisposableService
|
||||
{
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly DataManager dataManager = Service<DataManager>.Get();
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly ClientState.ClientState clientState = Service<ClientState.ClientState>.Get();
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly GameGui gameGui = Service<GameGui>.Get();
|
||||
|
||||
private readonly ushort[] craftTypeLevels;
|
||||
private readonly byte[] unlockedNoteBookDivisionsCount;
|
||||
private readonly byte[] unlockedSecretNoteBookDivisionsCount;
|
||||
private readonly ushort[,] noteBookDivisionIds;
|
||||
private byte[]? cachedUnlockedSecretRecipeBooks;
|
||||
private byte[]? cachedUnlockLinks;
|
||||
private byte[]? cachedCompletedQuests;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RecipeData"/> class.
|
||||
/// </summary>
|
||||
[ServiceManager.ServiceConstructor]
|
||||
public RecipeData()
|
||||
{
|
||||
var numCraftTypes = this.dataManager.GetExcelSheet<CraftType>().Count();
|
||||
var numSecretNotBookDivisions = this.dataManager.GetExcelSheet<NotebookDivision>().Count(row => row.RowId is >= 1000 and < 2000);
|
||||
|
||||
this.unlockedNoteBookDivisionsCount = new byte[numCraftTypes];
|
||||
this.unlockedSecretNoteBookDivisionsCount = new byte[numCraftTypes];
|
||||
this.noteBookDivisionIds = new ushort[numCraftTypes, numSecretNotBookDivisions];
|
||||
|
||||
this.craftTypeLevels = new ushort[numCraftTypes];
|
||||
|
||||
this.clientState.Login += this.Update;
|
||||
this.clientState.Logout += this.OnLogout;
|
||||
this.clientState.LevelChanged += this.OnlevelChanged;
|
||||
this.gameGui.AgentUpdate += this.OnAgentUpdate;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IInternalDisposableService.DisposeService()
|
||||
{
|
||||
this.clientState.Login -= this.Update;
|
||||
this.clientState.Logout -= this.OnLogout;
|
||||
this.clientState.LevelChanged -= this.OnlevelChanged;
|
||||
this.gameGui.AgentUpdate -= this.OnAgentUpdate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified Recipe is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The Recipe row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
public bool IsRecipeUnlocked(Recipe row)
|
||||
{
|
||||
// E8 ?? ?? ?? ?? 48 63 76 (2025.09.04)
|
||||
var division = row.RecipeNotebookList.RowId != 0 && row.RecipeNotebookList.IsValid
|
||||
? (row.RecipeNotebookList.RowId - 1000) / 8 + 1000
|
||||
: ((uint)row.RecipeLevelTable.Value.ClassJobLevel - 1) / 5;
|
||||
|
||||
// E8 ?? ?? ?? ?? 33 ED 84 C0 75 (2025.09.04)
|
||||
foreach (var craftTypeRow in this.dataManager.GetExcelSheet<CraftType>())
|
||||
{
|
||||
var craftType = (byte)craftTypeRow.RowId;
|
||||
|
||||
if (division < this.unlockedNoteBookDivisionsCount[craftType])
|
||||
return true;
|
||||
|
||||
if (this.unlockedNoteBookDivisionsCount[craftType] == 0)
|
||||
continue;
|
||||
|
||||
if (division is 5000 or 5001)
|
||||
return true;
|
||||
|
||||
if (division < 1000)
|
||||
continue;
|
||||
|
||||
if (this.unlockedSecretNoteBookDivisionsCount[craftType] == 0)
|
||||
continue;
|
||||
|
||||
if (this.noteBookDivisionIds.GetRowSpan(craftType).Contains((ushort)division))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnLogout(int type, int code)
|
||||
{
|
||||
this.cachedUnlockedSecretRecipeBooks = null;
|
||||
this.cachedUnlockLinks = null;
|
||||
this.cachedCompletedQuests = null;
|
||||
}
|
||||
|
||||
private void OnlevelChanged(uint classJobId, uint level)
|
||||
{
|
||||
if (this.dataManager.GetExcelSheet<ClassJob>().TryGetRow(classJobId, out var classJobRow) &&
|
||||
classJobRow.ClassJobCategory.RowId == 33) // Crafter
|
||||
{
|
||||
this.Update();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAgentUpdate(AgentUpdateFlag agentUpdateFlag)
|
||||
{
|
||||
if (agentUpdateFlag.HasFlag(AgentUpdateFlag.UnlocksUpdate))
|
||||
this.Update();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// based on Client::Game::UI::RecipeNote.InitializeStructs
|
||||
|
||||
if (!this.clientState.IsLoggedIn || !this.NeedsUpdate())
|
||||
return;
|
||||
|
||||
Array.Clear(this.unlockedNoteBookDivisionsCount, 0, this.unlockedNoteBookDivisionsCount.Length);
|
||||
Array.Clear(this.unlockedSecretNoteBookDivisionsCount, 0, this.unlockedSecretNoteBookDivisionsCount.Length);
|
||||
Array.Clear(this.noteBookDivisionIds, 0, this.noteBookDivisionIds.Length);
|
||||
|
||||
foreach (var craftTypeRow in this.dataManager.GetExcelSheet<CraftType>())
|
||||
{
|
||||
var craftType = (byte)craftTypeRow.RowId;
|
||||
var craftTypeLevel = RecipeNote.Instance()->GetCraftTypeLevel(craftType);
|
||||
if (craftTypeLevel == 0)
|
||||
continue;
|
||||
|
||||
var noteBookDivisionIndex = -1;
|
||||
|
||||
foreach (var noteBookDivisionRow in this.dataManager.GetExcelSheet<NotebookDivision>())
|
||||
{
|
||||
if (noteBookDivisionRow.RowId < 1000)
|
||||
{
|
||||
if (craftTypeLevel >= noteBookDivisionRow.CraftOpeningLevel)
|
||||
this.unlockedNoteBookDivisionsCount[craftType]++;
|
||||
}
|
||||
else if (noteBookDivisionRow.RowId < 2000)
|
||||
{
|
||||
noteBookDivisionIndex++;
|
||||
|
||||
// For future Lumina.Excel update, replace with:
|
||||
// if (!notebookDivisionRow.AllowedCraftTypes[craftType])
|
||||
// continue;
|
||||
|
||||
switch (craftTypeRow.RowId)
|
||||
{
|
||||
case 0 when !noteBookDivisionRow.CRPCraft: continue;
|
||||
case 1 when !noteBookDivisionRow.BSMCraft: continue;
|
||||
case 2 when !noteBookDivisionRow.ARMCraft: continue;
|
||||
case 3 when !noteBookDivisionRow.GSMCraft: continue;
|
||||
case 4 when !noteBookDivisionRow.LTWCraft: continue;
|
||||
case 5 when !noteBookDivisionRow.WVRCraft: continue;
|
||||
case 6 when !noteBookDivisionRow.ALCCraft: continue;
|
||||
case 7 when !noteBookDivisionRow.CULCraft: continue;
|
||||
}
|
||||
|
||||
if (noteBookDivisionRow.GatheringOpeningLevel != byte.MaxValue)
|
||||
continue;
|
||||
|
||||
// For future Lumina.Excel update, replace with:
|
||||
// if (notebookDivisionRow.RequiresSecretRecipeBookGroupUnlock)
|
||||
if (noteBookDivisionRow.Unknown1)
|
||||
{
|
||||
var secretRecipeBookUnlocked = false;
|
||||
|
||||
// For future Lumina.Excel update, iterate over notebookDivisionRow.SecretRecipeBookGroups
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
// For future Lumina.Excel update, replace with:
|
||||
// if (secretRecipeBookGroup.RowId == 0 || !secretRecipeBookGroup.IsValid)
|
||||
// continue;
|
||||
var secretRecipeBookGroupRowId = i switch
|
||||
{
|
||||
0 => noteBookDivisionRow.Unknown2,
|
||||
1 => noteBookDivisionRow.Unknown2,
|
||||
_ => default,
|
||||
};
|
||||
|
||||
if (secretRecipeBookGroupRowId == 0)
|
||||
continue;
|
||||
|
||||
if (!this.dataManager.GetExcelSheet<SecretRecipeBookGroup>().TryGetRow(secretRecipeBookGroupRowId, out var secretRecipeBookGroupRow))
|
||||
continue;
|
||||
|
||||
// For future Lumina.Excel update, replace with:
|
||||
// var bitIndex = secretRecipeBookGroup.Value.UnlockBitIndex[craftType];
|
||||
|
||||
var bitIndex = craftType switch
|
||||
{
|
||||
0 => secretRecipeBookGroupRow.Unknown0,
|
||||
1 => secretRecipeBookGroupRow.Unknown1,
|
||||
2 => secretRecipeBookGroupRow.Unknown2,
|
||||
3 => secretRecipeBookGroupRow.Unknown3,
|
||||
4 => secretRecipeBookGroupRow.Unknown4,
|
||||
5 => secretRecipeBookGroupRow.Unknown5,
|
||||
6 => secretRecipeBookGroupRow.Unknown6,
|
||||
7 => secretRecipeBookGroupRow.Unknown7,
|
||||
_ => default,
|
||||
};
|
||||
|
||||
if (PlayerState.Instance()->UnlockedSecretRecipeBooksBitArray.Get(bitIndex))
|
||||
{
|
||||
secretRecipeBookUnlocked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (noteBookDivisionRow.CraftOpeningLevel > craftTypeLevel && !secretRecipeBookUnlocked)
|
||||
continue;
|
||||
}
|
||||
else if (craftTypeLevel < noteBookDivisionRow.CraftOpeningLevel)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (noteBookDivisionRow.QuestUnlock.RowId != 0 && !UIState.Instance()->IsUnlockLinkUnlockedOrQuestCompleted(noteBookDivisionRow.QuestUnlock.RowId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
this.unlockedSecretNoteBookDivisionsCount[craftType]++;
|
||||
this.noteBookDivisionIds[craftType, noteBookDivisionIndex] = (ushort)noteBookDivisionRow.RowId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool NeedsUpdate()
|
||||
{
|
||||
var changed = false;
|
||||
|
||||
foreach (var craftTypeRow in this.dataManager.GetExcelSheet<CraftType>())
|
||||
{
|
||||
var craftType = (byte)craftTypeRow.RowId;
|
||||
var craftTypeLevel = RecipeNote.Instance()->GetCraftTypeLevel(craftType);
|
||||
|
||||
if (this.craftTypeLevels[craftType] != craftTypeLevel)
|
||||
{
|
||||
this.craftTypeLevels[craftType] = craftTypeLevel;
|
||||
changed |= true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.cachedUnlockedSecretRecipeBooks == null || !PlayerState.Instance()->UnlockedSecretRecipeBooks.SequenceEqual(this.cachedUnlockedSecretRecipeBooks))
|
||||
{
|
||||
this.cachedUnlockedSecretRecipeBooks = PlayerState.Instance()->UnlockedSecretRecipeBooks.ToArray();
|
||||
changed |= true;
|
||||
}
|
||||
|
||||
if (this.cachedUnlockLinks == null || !UIState.Instance()->UnlockLinks.SequenceEqual(this.cachedUnlockLinks))
|
||||
{
|
||||
this.cachedUnlockLinks = UIState.Instance()->UnlockLinks.ToArray();
|
||||
changed |= true;
|
||||
}
|
||||
|
||||
if (this.cachedCompletedQuests == null || !QuestManager.Instance()->CompletedQuests.SequenceEqual(this.cachedCompletedQuests))
|
||||
{
|
||||
this.cachedCompletedQuests = QuestManager.Instance()->CompletedQuests.ToArray();
|
||||
changed |= true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
858
Dalamud/Game/UnlockState/UnlockState.cs
Normal file
858
Dalamud/Game/UnlockState/UnlockState.cs
Normal file
|
|
@ -0,0 +1,858 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.IoC.Internal;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Plugin.Services;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.InstanceContent;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||
using FFXIVClientStructs.FFXIV.Component.Exd;
|
||||
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.Sheets;
|
||||
|
||||
using ActionSheet = Lumina.Excel.Sheets.Action;
|
||||
using InstanceContentSheet = Lumina.Excel.Sheets.InstanceContent;
|
||||
using PublicContentSheet = Lumina.Excel.Sheets.PublicContent;
|
||||
|
||||
namespace Dalamud.Game.UnlockState;
|
||||
|
||||
#pragma warning disable UnlockState
|
||||
|
||||
/// <summary>
|
||||
/// This class provides unlock state of various content in the game.
|
||||
/// </summary>
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
|
||||
{
|
||||
private static readonly ModuleLog Log = new(nameof(UnlockState));
|
||||
|
||||
private readonly ConcurrentDictionary<Type, HashSet<uint>> cachedUnlockedRowIds = [];
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly DataManager dataManager = Service<DataManager>.Get();
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly ClientState.ClientState clientState = Service<ClientState.ClientState>.Get();
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly GameGui gameGui = Service<GameGui>.Get();
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly RecipeData recipeData = Service<RecipeData>.Get();
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private UnlockState()
|
||||
{
|
||||
this.clientState.Login += this.OnLogin;
|
||||
this.clientState.Logout += this.OnLogout;
|
||||
this.gameGui.AgentUpdate += this.OnAgentUpdate;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event IUnlockState.UnlockDelegate Unlock;
|
||||
|
||||
private bool IsLoaded => PlayerState.Instance()->IsLoaded;
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IInternalDisposableService.DisposeService()
|
||||
{
|
||||
this.clientState.Login -= this.OnLogin;
|
||||
this.clientState.Logout -= this.OnLogout;
|
||||
this.gameGui.AgentUpdate -= this.OnAgentUpdate;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsActionUnlocked(ActionSheet row)
|
||||
{
|
||||
return this.IsUnlockLinkUnlocked(row.UnlockLink.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsAetherCurrentUnlocked(AetherCurrent row)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
return PlayerState.Instance()->IsAetherCurrentUnlocked(row.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsAetherCurrentCompFlgSetUnlocked(AetherCurrentCompFlgSet row)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
return PlayerState.Instance()->IsAetherCurrentZoneComplete(row.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsAozActionUnlocked(AozAction row)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
if (row.RowId == 0 || !row.Action.IsValid)
|
||||
return false;
|
||||
|
||||
return UIState.Instance()->IsUnlockLinkUnlockedOrQuestCompleted(row.Action.Value.UnlockLink.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBannerBgUnlocked(BannerBg row)
|
||||
{
|
||||
return row.UnlockCondition.IsValid && this.IsBannerConditionUnlocked(row.UnlockCondition.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBannerConditionUnlocked(BannerCondition row)
|
||||
{
|
||||
if (row.RowId == 0)
|
||||
return false;
|
||||
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
var rowPtr = ExdModule.GetBannerConditionByIndex(row.RowId);
|
||||
if (rowPtr == null)
|
||||
return false;
|
||||
|
||||
return ExdModule.GetBannerConditionUnlockState(rowPtr) == 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBannerDecorationUnlocked(BannerDecoration row)
|
||||
{
|
||||
return row.UnlockCondition.IsValid && this.IsBannerConditionUnlocked(row.UnlockCondition.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBannerFacialUnlocked(BannerFacial row)
|
||||
{
|
||||
return row.UnlockCondition.IsValid && this.IsBannerConditionUnlocked(row.UnlockCondition.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBannerFrameUnlocked(BannerFrame row)
|
||||
{
|
||||
return row.UnlockCondition.IsValid && this.IsBannerConditionUnlocked(row.UnlockCondition.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBannerTimelineUnlocked(BannerTimeline row)
|
||||
{
|
||||
return row.UnlockCondition.IsValid && this.IsBannerConditionUnlocked(row.UnlockCondition.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBuddyActionUnlocked(BuddyAction row)
|
||||
{
|
||||
return this.IsUnlockLinkUnlocked(row.UnlockLink);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBuddyEquipUnlocked(BuddyEquip row)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
return UIState.Instance()->Buddy.CompanionInfo.IsBuddyEquipUnlocked(row.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsCharaMakeCustomizeUnlocked(CharaMakeCustomize row)
|
||||
{
|
||||
return row.IsPurchasable && this.IsUnlockLinkUnlocked(row.UnlockLink);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsChocoboTaxiStandUnlocked(ChocoboTaxiStand row)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
return UIState.Instance()->IsChocoboTaxiStandUnlocked(row.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsCompanionUnlocked(Companion row)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
return UIState.Instance()->IsCompanionUnlocked(row.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsCraftActionUnlocked(CraftAction row)
|
||||
{
|
||||
return this.IsUnlockLinkUnlocked(row.QuestRequirement.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsCSBonusContentTypeUnlocked(CSBonusContentType row)
|
||||
{
|
||||
return this.IsUnlockLinkUnlocked(row.UnlockLink);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsEmoteUnlocked(Emote row)
|
||||
{
|
||||
return this.IsUnlockLinkUnlocked(row.UnlockLink);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsEmjVoiceNpcUnlocked(EmjVoiceNpc row)
|
||||
{
|
||||
return this.IsUnlockLinkUnlocked(row.Unknown26);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsEmjCostumeUnlocked(EmjCostume row)
|
||||
{
|
||||
return this.dataManager.GetExcelSheet<EmjVoiceNpc>().TryGetRow(row.RowId, out var emjVoiceNpcRow)
|
||||
&& this.IsEmjVoiceNpcUnlocked(emjVoiceNpcRow)
|
||||
&& QuestManager.IsQuestComplete(row.Unknown1);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsGeneralActionUnlocked(GeneralAction row)
|
||||
{
|
||||
return this.IsUnlockLinkUnlocked(row.UnlockLink);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsGlassesUnlocked(Glasses row)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
return PlayerState.Instance()->IsGlassesUnlocked((ushort)row.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsHowToUnlocked(HowTo row)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
return UIState.Instance()->IsHowToUnlocked(row.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsInstanceContentUnlocked(InstanceContentSheet row)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
return UIState.IsInstanceContentUnlocked(row.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public unsafe bool IsItemUnlocked(Item row)
|
||||
{
|
||||
if (row.ItemAction.RowId == 0)
|
||||
return false;
|
||||
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
// To avoid the ExdModule.GetItemRowById call, which can return null if the excel page
|
||||
// is not loaded, we're going to imitate the IsItemActionUnlocked call first:
|
||||
switch ((ItemActionType)row.ItemAction.Value.Type)
|
||||
{
|
||||
case ItemActionType.Companion:
|
||||
return UIState.Instance()->IsCompanionUnlocked(row.ItemAction.Value.Data[0]);
|
||||
|
||||
case ItemActionType.BuddyEquip:
|
||||
return UIState.Instance()->Buddy.CompanionInfo.IsBuddyEquipUnlocked(row.ItemAction.Value.Data[0]);
|
||||
|
||||
case ItemActionType.Mount:
|
||||
return PlayerState.Instance()->IsMountUnlocked(row.ItemAction.Value.Data[0]);
|
||||
|
||||
case ItemActionType.SecretRecipeBook:
|
||||
return PlayerState.Instance()->IsSecretRecipeBookUnlocked(row.ItemAction.Value.Data[0]);
|
||||
|
||||
case ItemActionType.UnlockLink:
|
||||
case ItemActionType.OccultRecords:
|
||||
return UIState.Instance()->IsUnlockLinkUnlocked(row.ItemAction.Value.Data[0]);
|
||||
|
||||
case ItemActionType.TripleTriadCard when row.AdditionalData.Is<TripleTriadCard>():
|
||||
return UIState.Instance()->IsTripleTriadCardUnlocked((ushort)row.AdditionalData.RowId);
|
||||
|
||||
case ItemActionType.FolkloreTome:
|
||||
return PlayerState.Instance()->IsFolkloreBookUnlocked(row.ItemAction.Value.Data[0]);
|
||||
|
||||
case ItemActionType.OrchestrionRoll when row.AdditionalData.Is<Orchestrion>():
|
||||
return PlayerState.Instance()->IsOrchestrionRollUnlocked(row.AdditionalData.RowId);
|
||||
|
||||
case ItemActionType.FramersKit:
|
||||
return PlayerState.Instance()->IsFramersKitUnlocked(row.AdditionalData.RowId);
|
||||
|
||||
case ItemActionType.Ornament:
|
||||
return PlayerState.Instance()->IsOrnamentUnlocked(row.ItemAction.Value.Data[0]);
|
||||
|
||||
case ItemActionType.Glasses:
|
||||
return PlayerState.Instance()->IsGlassesUnlocked((ushort)row.AdditionalData.RowId);
|
||||
|
||||
case ItemActionType.SoulShards when PublicContentOccultCrescent.GetState() is var occultCrescentState && occultCrescentState != null:
|
||||
var supportJobId = (byte)row.ItemAction.Value.Data[0];
|
||||
return supportJobId < occultCrescentState->SupportJobLevels.Length && occultCrescentState->SupportJobLevels[supportJobId] != 0;
|
||||
|
||||
case ItemActionType.CompanySealVouchers:
|
||||
return false;
|
||||
}
|
||||
|
||||
var nativeRow = ExdModule.GetItemRowById(row.RowId);
|
||||
return nativeRow != null && UIState.Instance()->IsItemActionUnlocked(nativeRow) == 1;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsMcGuffinUnlocked(McGuffin row)
|
||||
{
|
||||
return PlayerState.Instance()->IsMcGuffinUnlocked(row.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsMJILandmarkUnlocked(MJILandmark row)
|
||||
{
|
||||
return this.IsUnlockLinkUnlocked(row.UnlockLink);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsMKDLoreUnlocked(MKDLore row)
|
||||
{
|
||||
return this.IsUnlockLinkUnlocked(row.Unknown2);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsMountUnlocked(Mount row)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
return PlayerState.Instance()->IsMountUnlocked(row.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsNotebookDivisionUnlocked(NotebookDivision row)
|
||||
{
|
||||
return this.IsUnlockLinkUnlocked(row.QuestUnlock.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsOrchestrionUnlocked(Orchestrion row)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
return PlayerState.Instance()->IsOrchestrionRollUnlocked(row.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsOrnamentUnlocked(Ornament row)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
return PlayerState.Instance()->IsOrnamentUnlocked(row.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsPerformUnlocked(Perform row)
|
||||
{
|
||||
return this.IsUnlockLinkUnlocked((uint)row.UnlockLink);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsPublicContentUnlocked(PublicContentSheet row)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
return UIState.IsPublicContentUnlocked(row.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsRecipeUnlocked(Recipe row)
|
||||
{
|
||||
return this.recipeData.IsRecipeUnlocked(row);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsSecretRecipeBookUnlocked(SecretRecipeBook row)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
return PlayerState.Instance()->IsSecretRecipeBookUnlocked(row.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsTraitUnlocked(Trait row)
|
||||
{
|
||||
return this.IsUnlockLinkUnlocked(row.Quest.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsTripleTriadCardUnlocked(TripleTriadCard row)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
return UIState.Instance()->IsTripleTriadCardUnlocked((ushort)row.RowId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsItemUnlockable(Item row)
|
||||
{
|
||||
if (row.ItemAction.RowId == 0)
|
||||
return false;
|
||||
|
||||
return (ItemActionType)row.ItemAction.Value.Type
|
||||
is ItemActionType.Companion
|
||||
or ItemActionType.BuddyEquip
|
||||
or ItemActionType.Mount
|
||||
or ItemActionType.SecretRecipeBook
|
||||
or ItemActionType.UnlockLink
|
||||
or ItemActionType.TripleTriadCard
|
||||
or ItemActionType.FolkloreTome
|
||||
or ItemActionType.OrchestrionRoll
|
||||
or ItemActionType.FramersKit
|
||||
or ItemActionType.Ornament
|
||||
or ItemActionType.Glasses
|
||||
or ItemActionType.OccultRecords
|
||||
or ItemActionType.SoulShards;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsRowRefUnlocked<T>(RowRef<T> rowRef) where T : struct, IExcelRow<T>
|
||||
{
|
||||
return this.IsRowRefUnlocked((RowRef)rowRef);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsRowRefUnlocked(RowRef rowRef)
|
||||
{
|
||||
if (!this.IsLoaded || rowRef.IsUntyped)
|
||||
return false;
|
||||
|
||||
if (rowRef.TryGetValue<ActionSheet>(out var actionRow))
|
||||
return this.IsActionUnlocked(actionRow);
|
||||
|
||||
if (rowRef.TryGetValue<AetherCurrent>(out var aetherCurrentRow))
|
||||
return this.IsAetherCurrentUnlocked(aetherCurrentRow);
|
||||
|
||||
if (rowRef.TryGetValue<AetherCurrentCompFlgSet>(out var aetherCurrentCompFlgSetRow))
|
||||
return this.IsAetherCurrentCompFlgSetUnlocked(aetherCurrentCompFlgSetRow);
|
||||
|
||||
if (rowRef.TryGetValue<AozAction>(out var aozActionRow))
|
||||
return this.IsAozActionUnlocked(aozActionRow);
|
||||
|
||||
if (rowRef.TryGetValue<BannerBg>(out var bannerBgRow))
|
||||
return this.IsBannerBgUnlocked(bannerBgRow);
|
||||
|
||||
if (rowRef.TryGetValue<BannerCondition>(out var bannerConditionRow))
|
||||
return this.IsBannerConditionUnlocked(bannerConditionRow);
|
||||
|
||||
if (rowRef.TryGetValue<BannerDecoration>(out var bannerDecorationRow))
|
||||
return this.IsBannerDecorationUnlocked(bannerDecorationRow);
|
||||
|
||||
if (rowRef.TryGetValue<BannerFacial>(out var bannerFacialRow))
|
||||
return this.IsBannerFacialUnlocked(bannerFacialRow);
|
||||
|
||||
if (rowRef.TryGetValue<BannerFrame>(out var bannerFrameRow))
|
||||
return this.IsBannerFrameUnlocked(bannerFrameRow);
|
||||
|
||||
if (rowRef.TryGetValue<BannerTimeline>(out var bannerTimelineRow))
|
||||
return this.IsBannerTimelineUnlocked(bannerTimelineRow);
|
||||
|
||||
if (rowRef.TryGetValue<BuddyAction>(out var buddyActionRow))
|
||||
return this.IsBuddyActionUnlocked(buddyActionRow);
|
||||
|
||||
if (rowRef.TryGetValue<BuddyEquip>(out var buddyEquipRow))
|
||||
return this.IsBuddyEquipUnlocked(buddyEquipRow);
|
||||
|
||||
if (rowRef.TryGetValue<CSBonusContentType>(out var csBonusContentTypeRow))
|
||||
return this.IsCSBonusContentTypeUnlocked(csBonusContentTypeRow);
|
||||
|
||||
if (rowRef.TryGetValue<CharaMakeCustomize>(out var charaMakeCustomizeRow))
|
||||
return this.IsCharaMakeCustomizeUnlocked(charaMakeCustomizeRow);
|
||||
|
||||
if (rowRef.TryGetValue<ChocoboTaxiStand>(out var chocoboTaxiStandRow))
|
||||
return this.IsChocoboTaxiStandUnlocked(chocoboTaxiStandRow);
|
||||
|
||||
if (rowRef.TryGetValue<Companion>(out var companionRow))
|
||||
return this.IsCompanionUnlocked(companionRow);
|
||||
|
||||
if (rowRef.TryGetValue<CraftAction>(out var craftActionRow))
|
||||
return this.IsCraftActionUnlocked(craftActionRow);
|
||||
|
||||
if (rowRef.TryGetValue<Emote>(out var emoteRow))
|
||||
return this.IsEmoteUnlocked(emoteRow);
|
||||
|
||||
if (rowRef.TryGetValue<GeneralAction>(out var generalActionRow))
|
||||
return this.IsGeneralActionUnlocked(generalActionRow);
|
||||
|
||||
if (rowRef.TryGetValue<Glasses>(out var glassesRow))
|
||||
return this.IsGlassesUnlocked(glassesRow);
|
||||
|
||||
if (rowRef.TryGetValue<HowTo>(out var howToRow))
|
||||
return this.IsHowToUnlocked(howToRow);
|
||||
|
||||
if (rowRef.TryGetValue<InstanceContentSheet>(out var instanceContentRow))
|
||||
return this.IsInstanceContentUnlocked(instanceContentRow);
|
||||
|
||||
if (rowRef.TryGetValue<Item>(out var itemRow))
|
||||
return this.IsItemUnlocked(itemRow);
|
||||
|
||||
if (rowRef.TryGetValue<MJILandmark>(out var mjiLandmarkRow))
|
||||
return this.IsMJILandmarkUnlocked(mjiLandmarkRow);
|
||||
|
||||
if (rowRef.TryGetValue<MKDLore>(out var mkdLoreRow))
|
||||
return this.IsMKDLoreUnlocked(mkdLoreRow);
|
||||
|
||||
if (rowRef.TryGetValue<McGuffin>(out var mcGuffinRow))
|
||||
return this.IsMcGuffinUnlocked(mcGuffinRow);
|
||||
|
||||
if (rowRef.TryGetValue<Mount>(out var mountRow))
|
||||
return this.IsMountUnlocked(mountRow);
|
||||
|
||||
if (rowRef.TryGetValue<NotebookDivision>(out var notebookDivisionRow))
|
||||
return this.IsNotebookDivisionUnlocked(notebookDivisionRow);
|
||||
|
||||
if (rowRef.TryGetValue<Orchestrion>(out var orchestrionRow))
|
||||
return this.IsOrchestrionUnlocked(orchestrionRow);
|
||||
|
||||
if (rowRef.TryGetValue<Ornament>(out var ornamentRow))
|
||||
return this.IsOrnamentUnlocked(ornamentRow);
|
||||
|
||||
if (rowRef.TryGetValue<Perform>(out var performRow))
|
||||
return this.IsPerformUnlocked(performRow);
|
||||
|
||||
if (rowRef.TryGetValue<PublicContentSheet>(out var publicContentRow))
|
||||
return this.IsPublicContentUnlocked(publicContentRow);
|
||||
|
||||
if (rowRef.TryGetValue<Recipe>(out var recipeRow))
|
||||
return this.IsRecipeUnlocked(recipeRow);
|
||||
|
||||
if (rowRef.TryGetValue<SecretRecipeBook>(out var secretRecipeBookRow))
|
||||
return this.IsSecretRecipeBookUnlocked(secretRecipeBookRow);
|
||||
|
||||
if (rowRef.TryGetValue<Trait>(out var traitRow))
|
||||
return this.IsTraitUnlocked(traitRow);
|
||||
|
||||
if (rowRef.TryGetValue<TripleTriadCard>(out var tripleTriadCardRow))
|
||||
return this.IsTripleTriadCardUnlocked(tripleTriadCardRow);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsUnlockLinkUnlocked(ushort unlockLink)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
if (unlockLink == 0)
|
||||
return false;
|
||||
|
||||
return UIState.Instance()->IsUnlockLinkUnlocked(unlockLink);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsUnlockLinkUnlocked(uint unlockLink)
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return false;
|
||||
|
||||
if (unlockLink == 0)
|
||||
return false;
|
||||
|
||||
return UIState.Instance()->IsUnlockLinkUnlockedOrQuestCompleted(unlockLink);
|
||||
}
|
||||
|
||||
private void OnLogin()
|
||||
{
|
||||
this.Update();
|
||||
}
|
||||
|
||||
private void OnLogout(int type, int code)
|
||||
{
|
||||
this.cachedUnlockedRowIds.Clear();
|
||||
}
|
||||
|
||||
private void OnAgentUpdate(AgentUpdateFlag agentUpdateFlag)
|
||||
{
|
||||
if (agentUpdateFlag.HasFlag(AgentUpdateFlag.UnlocksUpdate))
|
||||
this.Update();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!this.IsLoaded)
|
||||
return;
|
||||
|
||||
this.UpdateUnlocksForSheet<ActionSheet>();
|
||||
this.UpdateUnlocksForSheet<AetherCurrent>();
|
||||
this.UpdateUnlocksForSheet<AetherCurrentCompFlgSet>();
|
||||
this.UpdateUnlocksForSheet<AozAction>();
|
||||
this.UpdateUnlocksForSheet<BannerBg>();
|
||||
this.UpdateUnlocksForSheet<BannerCondition>();
|
||||
this.UpdateUnlocksForSheet<BannerDecoration>();
|
||||
this.UpdateUnlocksForSheet<BannerFacial>();
|
||||
this.UpdateUnlocksForSheet<BannerFrame>();
|
||||
this.UpdateUnlocksForSheet<BannerTimeline>();
|
||||
this.UpdateUnlocksForSheet<BuddyAction>();
|
||||
this.UpdateUnlocksForSheet<BuddyEquip>();
|
||||
this.UpdateUnlocksForSheet<CSBonusContentType>();
|
||||
this.UpdateUnlocksForSheet<CharaMakeCustomize>();
|
||||
this.UpdateUnlocksForSheet<ChocoboTaxi>();
|
||||
this.UpdateUnlocksForSheet<Companion>();
|
||||
this.UpdateUnlocksForSheet<CraftAction>();
|
||||
this.UpdateUnlocksForSheet<EmjVoiceNpc>();
|
||||
this.UpdateUnlocksForSheet<Emote>();
|
||||
this.UpdateUnlocksForSheet<GeneralAction>();
|
||||
this.UpdateUnlocksForSheet<Glasses>();
|
||||
this.UpdateUnlocksForSheet<HowTo>();
|
||||
this.UpdateUnlocksForSheet<InstanceContentSheet>();
|
||||
this.UpdateUnlocksForSheet<Item>();
|
||||
this.UpdateUnlocksForSheet<MJILandmark>();
|
||||
this.UpdateUnlocksForSheet<MKDLore>();
|
||||
this.UpdateUnlocksForSheet<McGuffin>();
|
||||
this.UpdateUnlocksForSheet<Mount>();
|
||||
this.UpdateUnlocksForSheet<NotebookDivision>();
|
||||
this.UpdateUnlocksForSheet<Orchestrion>();
|
||||
this.UpdateUnlocksForSheet<Ornament>();
|
||||
this.UpdateUnlocksForSheet<Perform>();
|
||||
this.UpdateUnlocksForSheet<PublicContentSheet>();
|
||||
this.UpdateUnlocksForSheet<Recipe>();
|
||||
this.UpdateUnlocksForSheet<SecretRecipeBook>();
|
||||
this.UpdateUnlocksForSheet<Trait>();
|
||||
this.UpdateUnlocksForSheet<TripleTriadCard>();
|
||||
|
||||
// Not implemented:
|
||||
// - DescriptionPage: quite complex
|
||||
// - QuestAcceptAdditionCondition: ignored
|
||||
|
||||
// For some other day:
|
||||
// - FishingSpot
|
||||
// - Spearfishing
|
||||
// - Adventure (Sightseeing)
|
||||
// - MinerFolkloreTome
|
||||
// - BotanistFolkloreTome
|
||||
// - FishingFolkloreTome
|
||||
// - VVD or is that unlocked via quest?
|
||||
// - VVDNotebookContents?
|
||||
// - FramersKit (is that just an Item?)
|
||||
// - ... more?
|
||||
|
||||
// Subrow sheets, which are incompatible with the current Unlock event, since RowRef doesn't carry the SubrowId:
|
||||
// - EmjCostume
|
||||
|
||||
// Probably not happening, because it requires fetching data from server:
|
||||
// - Achievements
|
||||
// - Titles
|
||||
// - Bozjan Field Notes
|
||||
// - Support/Phantom Jobs, which require to be in Occult Crescent, because it checks the jobs level for != 0
|
||||
}
|
||||
|
||||
private void UpdateUnlocksForSheet<T>() where T : struct, IExcelRow<T>
|
||||
{
|
||||
var unlockedRowIds = this.cachedUnlockedRowIds.GetOrAdd(typeof(T), _ => []);
|
||||
|
||||
foreach (var row in this.dataManager.GetExcelSheet<T>())
|
||||
{
|
||||
if (unlockedRowIds.Contains(row.RowId))
|
||||
continue;
|
||||
|
||||
var rowRef = LuminaUtils.CreateRef<T>(row.RowId);
|
||||
|
||||
if (!this.IsRowRefUnlocked(rowRef))
|
||||
continue;
|
||||
|
||||
unlockedRowIds.Add(row.RowId);
|
||||
|
||||
Log.Verbose($"Unlock detected: {typeof(T).Name}#{row.RowId}");
|
||||
|
||||
foreach (var action in Delegate.EnumerateInvocationList(this.Unlock))
|
||||
{
|
||||
try
|
||||
{
|
||||
action((RowRef)rowRef);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception during raise of {handler}", action.Method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plugin-scoped version of a <see cref="UnlockState"/> service.
|
||||
/// </summary>
|
||||
[PluginInterface]
|
||||
[ServiceManager.ScopedService]
|
||||
#pragma warning disable SA1015
|
||||
[ResolveVia<IUnlockState>]
|
||||
#pragma warning restore SA1015
|
||||
internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockState
|
||||
{
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly UnlockState unlockStateService = Service<UnlockState>.Get();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnlockStatePluginScoped"/> class.
|
||||
/// </summary>
|
||||
internal UnlockStatePluginScoped()
|
||||
{
|
||||
this.unlockStateService.Unlock += this.UnlockForward;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event IUnlockState.UnlockDelegate? Unlock;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsActionUnlocked(ActionSheet row) => this.unlockStateService.IsActionUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsAetherCurrentCompFlgSetUnlocked(AetherCurrentCompFlgSet row) => this.unlockStateService.IsAetherCurrentCompFlgSetUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsAetherCurrentUnlocked(AetherCurrent row) => this.unlockStateService.IsAetherCurrentUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsAozActionUnlocked(AozAction row) => this.unlockStateService.IsAozActionUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBannerBgUnlocked(BannerBg row) => this.unlockStateService.IsBannerBgUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBannerConditionUnlocked(BannerCondition row) => this.unlockStateService.IsBannerConditionUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBannerDecorationUnlocked(BannerDecoration row) => this.unlockStateService.IsBannerDecorationUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBannerFacialUnlocked(BannerFacial row) => this.unlockStateService.IsBannerFacialUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBannerFrameUnlocked(BannerFrame row) => this.unlockStateService.IsBannerFrameUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBannerTimelineUnlocked(BannerTimeline row) => this.unlockStateService.IsBannerTimelineUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBuddyActionUnlocked(BuddyAction row) => this.unlockStateService.IsBuddyActionUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsBuddyEquipUnlocked(BuddyEquip row) => this.unlockStateService.IsBuddyEquipUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsCharaMakeCustomizeUnlocked(CharaMakeCustomize row) => this.unlockStateService.IsCharaMakeCustomizeUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsChocoboTaxiStandUnlocked(ChocoboTaxiStand row) => this.unlockStateService.IsChocoboTaxiStandUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsCompanionUnlocked(Companion row) => this.unlockStateService.IsCompanionUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsCraftActionUnlocked(CraftAction row) => this.unlockStateService.IsCraftActionUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsCSBonusContentTypeUnlocked(CSBonusContentType row) => this.unlockStateService.IsCSBonusContentTypeUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsEmoteUnlocked(Emote row) => this.unlockStateService.IsEmoteUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsEmjVoiceNpcUnlocked(EmjVoiceNpc row) => this.unlockStateService.IsEmjVoiceNpcUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsEmjCostumeUnlocked(EmjCostume row) => this.unlockStateService.IsEmjCostumeUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsGeneralActionUnlocked(GeneralAction row) => this.unlockStateService.IsGeneralActionUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsGlassesUnlocked(Glasses row) => this.unlockStateService.IsGlassesUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsHowToUnlocked(HowTo row) => this.unlockStateService.IsHowToUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsInstanceContentUnlocked(InstanceContentSheet row) => this.unlockStateService.IsInstanceContentUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsItemUnlockable(Item row) => this.unlockStateService.IsItemUnlockable(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsItemUnlocked(Item row) => this.unlockStateService.IsItemUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsMcGuffinUnlocked(McGuffin row) => this.unlockStateService.IsMcGuffinUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsMJILandmarkUnlocked(MJILandmark row) => this.unlockStateService.IsMJILandmarkUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsMKDLoreUnlocked(MKDLore row) => this.unlockStateService.IsMKDLoreUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsMountUnlocked(Mount row) => this.unlockStateService.IsMountUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsNotebookDivisionUnlocked(NotebookDivision row) => this.unlockStateService.IsNotebookDivisionUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsOrchestrionUnlocked(Orchestrion row) => this.unlockStateService.IsOrchestrionUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsOrnamentUnlocked(Ornament row) => this.unlockStateService.IsOrnamentUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsPerformUnlocked(Perform row) => this.unlockStateService.IsPerformUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsPublicContentUnlocked(PublicContentSheet row) => this.unlockStateService.IsPublicContentUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsRecipeUnlocked(Recipe row) => this.unlockStateService.IsRecipeUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsRowRefUnlocked(RowRef rowRef) => this.unlockStateService.IsRowRefUnlocked(rowRef);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsRowRefUnlocked<T>(RowRef<T> rowRef) where T : struct, IExcelRow<T> => this.unlockStateService.IsRowRefUnlocked(rowRef);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsSecretRecipeBookUnlocked(SecretRecipeBook row) => this.unlockStateService.IsSecretRecipeBookUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsTraitUnlocked(Trait row) => this.unlockStateService.IsTraitUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsTripleTriadCardUnlocked(TripleTriadCard row) => this.unlockStateService.IsTripleTriadCardUnlocked(row);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsUnlockLinkUnlocked(uint unlockLink) => this.unlockStateService.IsUnlockLinkUnlocked(unlockLink);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsUnlockLinkUnlocked(ushort unlockLink) => this.unlockStateService.IsUnlockLinkUnlocked(unlockLink);
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IInternalDisposableService.DisposeService()
|
||||
{
|
||||
this.unlockStateService.Unlock -= this.UnlockForward;
|
||||
}
|
||||
|
||||
private void UnlockForward(RowRef rowRef) => this.Unlock?.Invoke(rowRef);
|
||||
}
|
||||
328
Dalamud/Plugin/Services/IUnlockState.cs
Normal file
328
Dalamud/Plugin/Services/IUnlockState.cs
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.Sheets;
|
||||
|
||||
namespace Dalamud.Plugin.Services;
|
||||
|
||||
#pragma warning disable SA1400 // Access modifier should be declared: Interface members are public by default
|
||||
|
||||
/// <summary>
|
||||
/// Interface for determining unlock state of various content in the game.
|
||||
/// </summary>
|
||||
[Experimental("UnlockState")]
|
||||
public interface IUnlockState
|
||||
{
|
||||
/// <summary>
|
||||
/// A delegate type used for the <see cref="Unlock"/> event.
|
||||
/// </summary>
|
||||
/// <param name="rowRef">A RowRef of the unlocked thing.</param>
|
||||
delegate void UnlockDelegate(RowRef rowRef);
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when something was unlocked.
|
||||
/// </summary>
|
||||
event UnlockDelegate? Unlock;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified Action is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The Action row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsActionUnlocked(Lumina.Excel.Sheets.Action row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified AetherCurrentCompFlgSet is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The AetherCurrentCompFlgSet row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsAetherCurrentCompFlgSetUnlocked(AetherCurrentCompFlgSet row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified AetherCurrent is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The AetherCurrent row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsAetherCurrentUnlocked(AetherCurrent row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified AozAction (Blue Mage Action) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The AozAction row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsAozActionUnlocked(AozAction row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified BannerBg (Portrait Backgrounds) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The BannerBg row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsBannerBgUnlocked(BannerBg row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified BannerCondition is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The BannerCondition row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsBannerConditionUnlocked(BannerCondition row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified BannerDecoration (Portrait Accents) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The BannerDecoration row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsBannerDecorationUnlocked(BannerDecoration row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified BannerFacial (Portrait Expressions) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The BannerFacial row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsBannerFacialUnlocked(BannerFacial row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified BannerFrame (Portrait Frames) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The BannerFrame row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsBannerFrameUnlocked(BannerFrame row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified BannerTimeline (Portrait Poses) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The BannerTimeline row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsBannerTimelineUnlocked(BannerTimeline row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified BuddyAction (Action of the players Chocobo Companion) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The BuddyAction row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsBuddyActionUnlocked(BuddyAction row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified BuddyEquip (Equipment of the players Chocobo Companion) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The BuddyEquip row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsBuddyEquipUnlocked(BuddyEquip row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified CharaMakeCustomize (Hairstyles and Face Paint patterns) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The CharaMakeCustomize row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsCharaMakeCustomizeUnlocked(CharaMakeCustomize row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified ChocoboTaxiStand (Chocobokeeps of the Chocobo Porter service) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The ChocoboTaxiStand row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsChocoboTaxiStandUnlocked(ChocoboTaxiStand row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified Companion (Minions) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The Companion row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsCompanionUnlocked(Companion row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified CraftAction is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The CraftAction row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsCraftActionUnlocked(CraftAction row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified CSBonusContentType is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The CSBonusContentType row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsCSBonusContentTypeUnlocked(CSBonusContentType row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified Emote is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The Emote row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsEmoteUnlocked(Emote row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified EmjVoiceNpc (Doman Mahjong Characters) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The EmjVoiceNpc row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsEmjVoiceNpcUnlocked(EmjVoiceNpc row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified EmjCostume (Doman Mahjong Character Costume) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The EmjCostume row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsEmjCostumeUnlocked(EmjCostume row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified GeneralAction is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The GeneralAction row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsGeneralActionUnlocked(GeneralAction row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified Glasses is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The Glasses row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsGlassesUnlocked(Glasses row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified HowTo is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The HowTo row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsHowToUnlocked(HowTo row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified InstanceContent is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The InstanceContent row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsInstanceContentUnlocked(InstanceContent row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified Item is considered unlockable.
|
||||
/// </summary>
|
||||
/// <param name="row">The Item row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlockable; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsItemUnlockable(Item row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified Item is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The Item row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsItemUnlocked(Item row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified McGuffin is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The McGuffin row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsMcGuffinUnlocked(McGuffin row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified MJILandmark (Island Sanctuary landmark) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The MJILandmark row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsMJILandmarkUnlocked(MJILandmark row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified MKDLore (Occult Record) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The MKDLore row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsMKDLoreUnlocked(MKDLore row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified Mount is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The Mount row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsMountUnlocked(Mount row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified NotebookDivision (Categories in Crafting/Gathering Log) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The NotebookDivision row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsNotebookDivisionUnlocked(NotebookDivision row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified Orchestrion roll is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The Orchestrion row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsOrchestrionUnlocked(Orchestrion row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified Ornament (Fashion Accessories) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The Ornament row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsOrnamentUnlocked(Ornament row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified Perform (Performance Instruments) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The Perform row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsPerformUnlocked(Perform row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified PublicContent is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The PublicContent row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsPublicContentUnlocked(PublicContent row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified Recipe is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The Recipe row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsRecipeUnlocked(Recipe row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the underlying RowRef type is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="rowRef">The RowRef to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsRowRefUnlocked(RowRef rowRef);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the underlying RowRef type is unlocked.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the Excel row.</typeparam>
|
||||
/// <param name="rowRef">The RowRef to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsRowRefUnlocked<T>(RowRef<T> rowRef) where T : struct, IExcelRow<T>;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified SecretRecipeBook (Master Recipe Books) is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The SecretRecipeBook row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsSecretRecipeBookUnlocked(SecretRecipeBook row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified Trait is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The Trait row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsTraitUnlocked(Trait row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified TripleTriadCard is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="row">The TripleTriadCard row to check.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsTripleTriadCardUnlocked(TripleTriadCard row);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified unlock link is unlocked or quest is completed.
|
||||
/// </summary>
|
||||
/// <param name="unlockLink">The unlock link id or quest id (quest ids in this case are over 65536).</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsUnlockLinkUnlocked(uint unlockLink);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified unlock link is unlocked.
|
||||
/// </summary>
|
||||
/// <param name="unlockLink">The unlock link id.</param>
|
||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||
bool IsUnlockLinkUnlocked(ushort unlockLink);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue