-<Project Sdk="Dalamud.NET.Sdk/10.0.0">
+<Project Sdk="Dalamud.NET.Sdk/11.0.0">
<ItemGroup>
<ProjectReference Include="..\LLib\LLib.csproj" />
<ProjectReference Include="..\Questionable.Model\Questionable.Model.csproj" />
-Subproject commit fde09c705b648f03c287814191a554f0a4b92cc4
+Subproject commit 538329a1e80acbcd09e28bd6dd459c35b5563c0a
{
BattleChara* battleChara = (BattleChara*)gameObject.Address;
if (_combatData.CombatItemUse.Condition == ECombatItemUseCondition.Incapacitated)
- return (battleChara->Flags2 & 128u) != 0;
+ return (battleChara->CombatTagType & 128u) != 0; // FIXME 7.1
if (_combatData.CombatItemUse.Condition == ECombatItemUseCondition.HealthPercent)
return (100f * battleChara->Health / battleChara->MaxHealth) < _combatData.CombatItemUse.Value;
float hitboxOffset = player.HitboxRadius + gameObject.HitboxRadius;
float actualDistance = Vector3.Distance(player.Position, gameObject.Position);
- float maxDistance = player.ClassJob.GameData?.Role is 3 or 4 ? 20f : 2.9f;
+ float maxDistance = player.ClassJob.ValueNullable?.Role is 3 or 4 ? 20f : 2.9f;
if (actualDistance - hitboxOffset >= maxDistance)
{
if (actualDistance - hitboxOffset <= 5)
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.Command;
using Dalamud.Plugin.Services;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using Questionable.Functions;
using Questionable.Model.Questing;
using Questionable.Windows;
-using Questionable.Windows.QuestComponents;
using Quest = Questionable.Model.Quest;
namespace Questionable.Controller;
ushort? mountId = _gameFunctions.GetMountId();
if (mountId != null)
{
- var row = _dataManager.GetExcelSheet<Mount>()!.GetRow(mountId.Value);
+ var row = _dataManager.GetExcelSheet<Mount>().GetRowOrDefault(mountId.Value);
_chatGui.Print(
$"Mount ID: {mountId}, Name: {row?.Singular}, Obtainable: {(row?.Order == -1 ? "No" : "Yes")}",
MessageTag, TagColor);
private void AddContextMenuEntry(IMenuOpenedArgs args, uint itemId, uint npcId, EExtendedClassJob extendedClassJob,
string verb)
{
- EClassJob currentClassJob = (EClassJob)_clientState.LocalPlayer!.ClassJob.Id;
+ EClassJob currentClassJob = (EClassJob)_clientState.LocalPlayer!.ClassJob.RowId;
EClassJob classJob = ClassJobUtils.AsIndividualJobs(extendedClassJob).Single();
if (classJob != currentClassJob && currentClassJob is EClassJob.Miner or EClassJob.Botanist)
return;
using LLib;
using LLib.GameData;
using LLib.GameUI;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Interactions;
using Questionable.Data;
_shopController = shopController;
_logger = logger;
- _returnRegex = _dataManager.GetExcelSheet<Addon>()!.GetRow(196)!.GetRegex(addon => addon.Text, pluginLog)!;
+ _returnRegex = _dataManager.GetExcelSheet<Addon>().GetRow(196).GetRegex(addon => addon.Text, pluginLog)!;
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectString", SelectStringPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "CutSceneSelectString", CutsceneSelectStringPostSetup);
step.InteractionType == EInteractionType.Gather)
{
if (_gatheringData.TryGetGatheringPointId(step.ItemsToGather[0].ItemId,
- (EClassJob?)_clientState.LocalPlayer?.ClassJob.Id ?? EClassJob.Adventurer,
+ (EClassJob?)_clientState.LocalPlayer?.ClassJob.RowId ?? EClassJob.Adventurer,
out GatheringPointId? gatheringPointId) &&
_gatheringPointRegistry.TryGetGatheringPoint(gatheringPointId, out GatheringRoot? root))
{
[NotNullWhen(true)] out string? warpText)
{
var warps = _dataManager.GetExcelSheet<Warp>()!
- .Where(x => x.RowId > 0 && x.TerritoryType.Row == targetTerritoryId);
+ .Where(x => x.RowId > 0 && x.TerritoryType.RowId == targetTerritoryId);
foreach (var entry in warps)
{
- string? excelName = entry.Name?.ToString();
- string? excelQuestion = entry.Question?.ToString();
+ string? excelName = entry.Name.ToString();
+ string? excelQuestion = entry.Question.ToString();
- if (excelQuestion != null && GameFunctions.GameStringEquals(excelQuestion, actualPrompt))
+ if (!string.IsNullOrEmpty(excelQuestion) && GameFunctions.GameStringEquals(excelQuestion, actualPrompt))
{
warpId = entry.RowId;
warpText = excelQuestion;
return true;
}
- else if (excelName != null && GameFunctions.GameStringEquals(excelName, actualPrompt))
+ else if (!string.IsNullOrEmpty(excelName) && GameFunctions.GameStringEquals(excelName, actualPrompt))
{
warpId = entry.RowId;
warpText = excelName;
using FFXIVClientStructs.FFXIV.Client.Game.Event;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using LLib;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps;
using Questionable.Controller.Steps.Gathering;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services;
using LLib;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
-using LLib;
-using LLib.GameData;
-using Lumina.Excel.GeneratedSheets;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps;
-using Questionable.Controller.Steps.Interactions;
using Questionable.Controller.Steps.Shared;
-using Questionable.Data;
using Questionable.External;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
using Quest = Questionable.Model.Quest;
-using Mount = Questionable.Controller.Steps.Common.Mount;
namespace Questionable.Controller;
private EAction PickAction(EAction minerAction, EAction botanistAction)
{
- if ((EClassJob?)clientState.LocalPlayer?.ClassJob.Id == EClassJob.Miner)
+ if ((EClassJob?)clientState.LocalPlayer?.ClassJob.RowId == EClassJob.Miner)
return minerAction;
else
return botanistAction;
private EAction PickAction(EAction minerAction, EAction botanistAction)
{
- if ((EClassJob?)clientState.LocalPlayer?.ClassJob.Id == EClassJob.Miner)
+ if ((EClassJob?)clientState.LocalPlayer?.ClassJob.RowId == EClassJob.Miner)
return minerAction;
else
return botanistAction;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using LLib;
-using Lumina.Excel.GeneratedSheets;
-using Microsoft.Extensions.DependencyInjection;
+using Lumina.Excel.Sheets;
using Microsoft.Extensions.Logging;
using Questionable.Functions;
using Questionable.Model.Questing;
];
private int _attempts;
- private Item _item = null!;
+ private Item? _item;
private List<ushort> _targetSlots = null!;
private DateTime _continueAt = DateTime.MaxValue;
protected override bool Start()
{
- _item = dataManager.GetExcelSheet<Item>()!.GetRow(Task.ItemId) ??
+ _item = dataManager.GetExcelSheet<Item>().GetRowOrDefault(Task.ItemId) ??
throw new ArgumentOutOfRangeException(nameof(Task.ItemId));
_targetSlots = GetEquipSlot(_item) ?? throw new InvalidOperationException("Not a piece of equipment");
var itemSlot = equippedContainer->GetInventorySlot(slot);
if (itemSlot != null && itemSlot->ItemId == Task.ItemId)
{
- logger.LogInformation("Already equipped {Item}, skipping step", _item.Name?.ToString());
+ logger.LogInformation("Already equipped {Item}, skipping step", _item?.Name.ToString());
return;
}
}
throw new TaskException($"Could not equip item {Task.ItemId}.");
}
- private static List<ushort>? GetEquipSlot(Item item)
+ private static List<ushort>? GetEquipSlot(Item? item)
{
- return item.EquipSlotCategory.Row switch
+ if (item == null)
+ return [];
+ return item.Value.EquipSlotCategory.RowId switch
{
- >= 1 and <= 11 => [(ushort)(item.EquipSlotCategory.Row - 1)],
+ >= 1 and <= 11 => [(ushort)(item.Value.EquipSlotCategory.RowId - 1)],
12 => [11, 12], // rings
13 => [0],
17 => [13], // soul crystal
protected override bool Start()
{
- RecommendEquipModule.Instance()->SetupForClassJob((byte)clientState.LocalPlayer!.ClassJob.Id);
+ RecommendEquipModule.Instance()->SetupForClassJob((byte)clientState.LocalPlayer!.ClassJob.RowId);
return true;
}
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Component.GUI;
using LLib.GameData;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using Microsoft.Extensions.Logging;
using Questionable.External;
using Questionable.Model.Questing;
return false;
}
- RecipeLookup? recipeLookup = dataManager.GetExcelSheet<RecipeLookup>()!.GetRow(Task.ItemId);
+ RecipeLookup? recipeLookup = dataManager.GetExcelSheet<RecipeLookup>().GetRowOrDefault(Task.ItemId);
if (recipeLookup == null)
throw new TaskException($"Item {Task.ItemId} is not craftable");
- uint recipeId = (EClassJob)clientState.LocalPlayer!.ClassJob.Id switch
+ uint recipeId = (EClassJob)clientState.LocalPlayer!.ClassJob.RowId switch
{
- EClassJob.Carpenter => recipeLookup.CRP.Row,
- EClassJob.Blacksmith => recipeLookup.BSM.Row,
- EClassJob.Armorer => recipeLookup.ARM.Row,
- EClassJob.Goldsmith => recipeLookup.GSM.Row,
- EClassJob.Leatherworker => recipeLookup.LTW.Row,
- EClassJob.Weaver => recipeLookup.WVR.Row,
- EClassJob.Alchemist => recipeLookup.ALC.Row,
- EClassJob.Culinarian => recipeLookup.CUL.Row,
+ EClassJob.Carpenter => recipeLookup.Value.CRP.RowId,
+ EClassJob.Blacksmith => recipeLookup.Value.BSM.RowId,
+ EClassJob.Armorer => recipeLookup.Value.ARM.RowId,
+ EClassJob.Goldsmith => recipeLookup.Value.GSM.RowId,
+ EClassJob.Leatherworker => recipeLookup.Value.LTW.RowId,
+ EClassJob.Weaver => recipeLookup.Value.WVR.RowId,
+ EClassJob.Alchemist => recipeLookup.Value.ALC.RowId,
+ EClassJob.Culinarian => recipeLookup.Value.CUL.RowId,
_ => 0
};
{
recipeId = new[]
{
- recipeLookup.CRP.Row,
- recipeLookup.BSM.Row,
- recipeLookup.ARM.Row,
- recipeLookup.GSM.Row,
- recipeLookup.LTW.Row,
- recipeLookup.WVR.Row,
- recipeLookup.ALC.Row,
- recipeLookup.WVR.Row
+ recipeLookup.Value.CRP.RowId,
+ recipeLookup.Value.BSM.RowId,
+ recipeLookup.Value.ARM.RowId,
+ recipeLookup.Value.GSM.RowId,
+ recipeLookup.Value.LTW.RowId,
+ recipeLookup.Value.WVR.RowId,
+ recipeLookup.Value.ALC.RowId,
+ recipeLookup.Value.WVR.RowId
}
.FirstOrDefault(x => x != 0);
}
foreach (var itemToGather in step.ItemsToGather)
{
- EClassJob currentClassJob = (EClassJob)clientState.LocalPlayer!.ClassJob.Id;
+ EClassJob currentClassJob = (EClassJob)clientState.LocalPlayer!.ClassJob.RowId;
if (!gatheringData.TryGetGatheringPointId(itemToGather.ItemId, currentClassJob,
out GatheringPointId? gatheringPointId))
throw new TaskException($"No gathering point found for item {itemToGather.ItemId}");
using System;
using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Numerics;
using Dalamud.Game.ClientState.Conditions;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using LLib;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Common;
using Questionable.Data;
{
List<EClassJob> expectedJobs =
step.RequiredCurrentJob.SelectMany(ClassJobUtils.AsIndividualJobs).ToList();
- EClassJob currentJob = (EClassJob)clientState.LocalPlayer!.ClassJob.Id;
+ EClassJob currentJob = (EClassJob)clientState.LocalPlayer!.ClassJob.RowId;
logger.LogInformation("Checking current job {CurrentJob} against {ExpectedJobs}", currentJob,
string.Join(",", expectedJobs));
if (!expectedJobs.Contains(currentJob))
{
protected override unsafe bool StartInternal()
{
- if (clientState.LocalPlayer!.ClassJob.Id == (uint)Task.ClassJob)
+ if (clientState.LocalPlayer!.ClassJob.RowId == (uint)Task.ClassJob)
return false;
var gearsetModule = RaptureGearsetModule.Instance();
using System.Collections.Immutable;
using System.Linq;
using Dalamud.Plugin.Services;
-using Lumina.Excel.GeneratedSheets2;
+using Lumina.Excel.Sheets;
namespace Questionable.Data;
{
_overworldCurrents = dataManager.GetExcelSheet<AetherCurrentCompFlgSet>()!
.Where(x => x.RowId > 0)
- .Where(x => x.Territory.Value != null)
+ .Where(x => x.Territory.IsValid)
.ToImmutableDictionary(
- x => (ushort)x.Territory.Row,
+ x => (ushort)x.Territory.RowId,
x => x.AetherCurrents
- .Where(y => y.Row > 0 && y.Value?.Quest.Row == 0)
- .Select(y => y.Row)
+ .Where(y => y.RowId > 0 && y.Value.Quest.RowId == 0)
+ .Select(y => y.RowId)
.ToImmutableList());
}
using System.Numerics;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using Questionable.Model.Common;
namespace Questionable.Data;
void ConfigureAetheryteWithPlaceName(EAetheryteLocation aetheryteLocation, uint placeNameId, ushort territoryId)
{
ConfigureAetheryte(aetheryteLocation,
- dataManager.GetExcelSheet<PlaceName>()!.GetRow(placeNameId)!.Name.ToDalamudString().TextValue,
+ dataManager.GetExcelSheet<PlaceName>().GetRow(placeNameId).Name.ToDalamudString().TextValue,
territoryId,
(ushort)((int)aetheryteLocation / 100));
}
- foreach (var aetheryte in dataManager.GetExcelSheet<Aetheryte>()!.Where(x => x.RowId > 0))
+ foreach (var aetheryte in dataManager.GetExcelSheet<Aetheryte>().Where(x => x.RowId > 0))
{
- string? aethernetName = aetheryte.AethernetName?.Value?.Name.ToString();
+ string? aethernetName = aetheryte.AethernetName.ValueNullable?.Name.ToString();
if (!string.IsNullOrEmpty(aethernetName))
aethernetNames[(EAetheryteLocation)aetheryte.RowId] = aethernetName;
- if (aetheryte.Territory != null && aetheryte.Territory.Row > 0)
- territoryIds[(EAetheryteLocation)aetheryte.RowId] = (ushort)aetheryte.Territory.Row;
+ if (aetheryte.Territory.RowId > 0)
+ territoryIds[(EAetheryteLocation)aetheryte.RowId] = (ushort)aetheryte.Territory.RowId;
if (aetheryte.AethernetGroup > 0)
aethernetGroups[(EAetheryteLocation)aetheryte.RowId] = aetheryte.AethernetGroup;
TerritoryIds = territoryIds.AsReadOnly();
AethernetGroups = aethernetGroups.AsReadOnly();
- TownTerritoryIds = dataManager.GetExcelSheet<TerritoryType>()!
- .Where(x => x.RowId > 0 && !string.IsNullOrEmpty(x.Name) && x.TerritoryIntendedUse == 0)
+ TownTerritoryIds = dataManager.GetExcelSheet<TerritoryType>()
+ .Where(x => x.RowId > 0 && !string.IsNullOrEmpty(x.Name.ToString()) && x.TerritoryIntendedUse.RowId == 0)
.Select(x => (ushort)x.RowId)
.ToList();
}
using System.Linq;
using Dalamud.Plugin.Services;
using LLib.GameData;
-using Lumina.Excel.GeneratedSheets;
-using Microsoft.Extensions.Logging;
+using Lumina.Excel.Sheets;
using Questionable.Model.Gathering;
namespace Questionable.Data;
public GatheringData(IDataManager dataManager)
{
- Dictionary<uint, uint> gatheringItemToItem = dataManager.GetExcelSheet<GatheringItem>()!
- .Where(x => x.RowId != 0 && x.Item != 0)
- .ToDictionary(x => x.RowId, x => (uint)x.Item);
+ Dictionary<uint, uint> gatheringItemToItem = dataManager.GetExcelSheet<GatheringItem>()
+ .Where(x => x.RowId != 0 && x.Item.RowId != 0)
+ .ToDictionary(x => x.RowId, x => x.Item.RowId);
- foreach (var gatheringPointBase in dataManager.GetExcelSheet<GatheringPointBase>()!)
+ foreach (var gatheringPointBase in dataManager.GetExcelSheet<GatheringPointBase>())
{
- foreach (var gatheringItemId in gatheringPointBase.Item.Where(x => x != 0))
+ foreach (var gatheringItem in gatheringPointBase.Item.Where(x => x.RowId != 0))
{
- if (gatheringItemToItem.TryGetValue((uint)gatheringItemId, out uint itemId))
+ if (gatheringItemToItem.TryGetValue(gatheringItem.RowId, out uint itemId))
{
- if (gatheringPointBase.GatheringType.Row is 0 or 1)
+ if (gatheringPointBase.GatheringType.RowId is 0 or 1)
_minerGatheringPoints[itemId] = new GatheringPointId((ushort)gatheringPointBase.RowId);
- else if (gatheringPointBase.GatheringType.Row is 2 or 3)
+ else if (gatheringPointBase.GatheringType.RowId is 2 or 3)
_botanistGatheringPoints[itemId] = new GatheringPointId((ushort)gatheringPointBase.RowId);
}
}
}
- _itemIdToCollectability = dataManager.GetExcelSheet<SatisfactionSupply>()!
+ _itemIdToCollectability = dataManager.GetSubrowExcelSheet<SatisfactionSupply>()
.Where(x => x.RowId > 0)
+ .SelectMany(x => x)
.Where(x => x.Slot is 2)
.Select(x => new
{
- ItemId = x.Item.Row,
+ ItemId = x.Item.RowId,
Collectability = x.CollectabilityHigh,
})
.Distinct()
.ToDictionary(x => x.ItemId, x => x.Collectability);
- _npcForCustomDeliveries = dataManager.GetExcelSheet<SatisfactionNpc>()!
+ _npcForCustomDeliveries = dataManager.GetExcelSheet<SatisfactionNpc>()
.Where(x => x.RowId > 0)
- .SelectMany(x => dataManager.GetExcelSheet<SatisfactionSupply>()!
- .Where(y => y.RowId == x.SupplyIndex.Last())
+ .SelectMany(x => dataManager.GetSubrowExcelSheet<SatisfactionSupply>()
+ .Where(y => y.RowId == x.SatisfactionNpcParams.Last().SupplyIndex)
+ .SelectMany(y => y)
.Select(y => new
{
- ItemId = y.Item.Row,
- NpcId = x.Npc.Row
+ ItemId = y.Item.RowId,
+ NpcId = x.Npc.RowId
}))
.Where(x => x.ItemId > 0)
.Distinct()
using System.Collections.Generic;
using System.Linq;
using Dalamud.Plugin.Services;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
+using Microsoft.Extensions.Logging;
using Questionable.Model;
using Questionable.Model.Questing;
internal sealed class JournalData
{
- public JournalData(IDataManager dataManager, QuestData questData)
+ public JournalData(IDataManager dataManager, QuestData questData, ILogger<JournalData> logger)
{
- var genres = dataManager.GetExcelSheet<JournalGenre>()!
+ var genres = dataManager.GetExcelSheet<JournalGenre>()
.Where(x => x.RowId > 0 && x.Icon > 0)
.Select(x => new Genre(x, questData.GetAllByJournalGenre(x.RowId)))
.ToList();
+ foreach (var genre in genres)
+ {
+ logger.LogInformation("Genre {GenreId}: {GenreName} has {QuestCount} quests",
+ genre.Id, genre.Name, genre.QuestCount);
+ }
+ logger.LogInformation("Genre count: {GenreCount}", genres.Count);
+ var quest = questData.GetQuestInfo(new QuestId(5193));
+ logger.LogInformation("Q: {N}, {A}, {B}", quest.Name, quest.JournalGenre, quest.IssuerDataId);
- var limsaStart = dataManager.GetExcelSheet<QuestRedo>()!.GetRow(1)!;
- var gridaniaStart = dataManager.GetExcelSheet<QuestRedo>()!.GetRow(2)!;
- var uldahStart = dataManager.GetExcelSheet<QuestRedo>()!.GetRow(3)!;
+ var limsaStart = dataManager.GetExcelSheet<QuestRedo>().GetRow(1);
+ var gridaniaStart = dataManager.GetExcelSheet<QuestRedo>().GetRow(2);
+ var uldahStart = dataManager.GetExcelSheet<QuestRedo>().GetRow(3);
var genreLimsa = new Genre(uint.MaxValue - 3, "Starting in Limsa Lominsa", 1,
- new uint[] { 108, 109 }.Concat(limsaStart.Quest.Select(x => x.Row))
+ new uint[] { 108, 109 }.Concat(limsaStart.QuestRedoParam.Select(x => x.Quest.RowId))
.Where(x => x != 0)
.Select(x => questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF))))
.ToList());
var genreGridania = new Genre(uint.MaxValue - 2, "Starting in Gridania", 1,
- new uint[] { 85, 123, 124 }.Concat(gridaniaStart.Quest.Select(x => x.Row))
+ new uint[] { 85, 123, 124 }.Concat(gridaniaStart.QuestRedoParam.Select(x => x.Quest.RowId))
.Where(x => x != 0)
.Select(x => questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF))))
.ToList());
var genreUldah = new Genre(uint.MaxValue - 1, "Starting in Ul'dah", 1,
- new uint[] { 568, 569, 570 }.Concat(uldahStart.Quest.Select(x => x.Row))
+ new uint[] { 568, 569, 570 }.Concat(uldahStart.QuestRedoParam.Select(x => x.Quest.RowId))
.Where(x => x != 0)
.Select(x => questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF))))
.ToList());
genreLimsa.Quests.Contains(x) || genreGridania.Quests.Contains(x) || genreUldah.Quests.Contains(x));
Genres = genres.AsReadOnly();
- Categories = dataManager.GetExcelSheet<JournalCategory>()!
+ Categories = dataManager.GetExcelSheet<JournalCategory>()
.Where(x => x.RowId > 0)
.Select(x => new Category(x, Genres.Where(y => y.CategoryId == x.RowId).ToList()))
.ToList()
.AsReadOnly();
- Sections = dataManager.GetExcelSheet<JournalSection>()!
+ Sections = dataManager.GetExcelSheet<JournalSection>()
.Select(x => new Section(x, Categories.Where(y => y.SectionId == x.RowId).ToList()))
.ToList();
}
{
Id = journalGenre.RowId;
Name = journalGenre.Name.ToString();
- CategoryId = journalGenre.JournalCategory.Row;
+ CategoryId = journalGenre.JournalCategory.RowId;
Quests = quests;
}
{
public uint Id { get; } = journalCategory.RowId;
public string Name { get; } = journalCategory.Name.ToString();
- public uint SectionId { get; } = journalCategory.JournalSection.Row;
+ public uint SectionId { get; } = journalCategory.JournalSection.RowId;
public IReadOnlyList<Genre> Genres { get; } = genres;
public int QuestCount => Genres.Sum(x => x.QuestCount);
}
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using LLib.GameData;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
+using Questionable.Data.Sheets;
using Questionable.Model;
using Questionable.Model.Questing;
-using Leve = Lumina.Excel.GeneratedSheets2.Leve;
-using Quest = Lumina.Excel.GeneratedSheets2.Quest;
+using Quest = Lumina.Excel.Sheets.Quest;
namespace Questionable.Data;
public static readonly IReadOnlyList<QuestId> CrystalTowerQuests =
[new(1709), new(1200), new(1201), new(1202), new(1203), new(1474), new(494), new(495)];
- public static readonly IReadOnlyList<ushort> TankRoleQuests = [136, 154, 178];
- public static readonly IReadOnlyList<ushort> HealerRoleQuests = [137, 155, 179];
- 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<uint> TankRoleQuests = [136, 154, 178];
+ public static readonly IReadOnlyList<uint> HealerRoleQuests = [137, 155, 179];
+ public static readonly IReadOnlyList<uint> MeleeRoleQuests = [138, 156, 180];
+ public static readonly IReadOnlyList<uint> PhysicalRangedRoleQuests = [138, 157, 181];
+ public static readonly IReadOnlyList<uint> CasterRoleQuests = [139, 158, 182];
- public static readonly IReadOnlyList<IReadOnlyList<ushort>> AllRoleQuestChapters =
+ public static readonly IReadOnlyList<IReadOnlyList<uint>> AllRoleQuestChapters =
[
TankRoleQuests,
HealerRoleQuests,
public QuestData(IDataManager dataManager)
{
- Dictionary<uint, ushort> questChapters =
+ Dictionary<uint, uint> questChapters =
dataManager.GetExcelSheet<QuestChapter>()!
- .Where(x => x.RowId > 0 && x.Quest.Row > 0)
- .ToDictionary(x => x.Quest.Row, x => x.Redo);
+ .Where(x => x.RowId > 0 && x.Quest.RowId > 0)
+ .ToDictionary(x => x.Quest.RowId, x => x.Redo.RowId);
Dictionary<uint, byte> startingCities = new();
for (byte redoChapter = 1; redoChapter <= 3; ++redoChapter)
{
- var questRedo = dataManager.GetExcelSheet<QuestRedo>()!.GetRow(redoChapter)!;
- foreach (var quest in questRedo.Quest.Where(x => x.Row > 0))
- startingCities[quest.Row] = redoChapter;
+ var questRedo = dataManager.GetExcelSheet<QuestRedo>().GetRow(redoChapter);
+ foreach (var quest in questRedo.QuestRedoParam.Where(x => x.Quest.IsValid))
+ startingCities[quest.Quest.RowId] = redoChapter;
}
List<IQuestInfo> quests =
[
- ..dataManager.GetExcelSheet<Quest>()!
+ ..dataManager.GetExcelSheet<QuestEx>()
.Where(x => x.RowId > 0)
- .Where(x => x.IssuerLocation.Row > 0)
+ .Where(x => x.IssuerLocation.RowId > 0)
.Select(x => new QuestInfo(x, questChapters.GetValueOrDefault(x.RowId),
startingCities.GetValueOrDefault(x.RowId)))
.Where(x => x.QuestId.Value != 1428),
- ..dataManager.GetExcelSheet<SatisfactionNpc>()!
- .Where(x => x.RowId > 0)
+ ..dataManager.GetExcelSheet<SatisfactionNpc>()
+ .Where(x => x is { RowId: > 0, Npc.RowId: > 0 })
.Select(x => new SatisfactionSupplyInfo(x)),
- ..dataManager.GetExcelSheet<Leve>()!
+ ..dataManager.GetExcelSheet<Leve>()
.Where(x => x.RowId > 0)
- .Where(x => x.LevelLevemete.Row != 0)
+ .Where(x => x.LevelLevemete.RowId != 0)
.Select(x => new LeveInfo(x)),
];
_quests = quests.ToDictionary(x => x.QuestId, x => x);
public List<QuestInfo> GetClassJobQuests(EClassJob classJob)
{
- List<ushort> chapterIds = classJob switch
+ List<uint> chapterIds = classJob switch
{
EClassJob.Adventurer => throw new ArgumentOutOfRangeException(nameof(classJob)),
return GetQuestsInNewGamePlusChapters(chapterIds);
}
- private List<QuestInfo> GetQuestsInNewGamePlusChapters(List<ushort> chapterIds)
+ private List<QuestInfo> GetQuestsInNewGamePlusChapters(List<uint> chapterIds)
{
return _quests.Values
.Where(x => x is QuestInfo)
--- /dev/null
+using Lumina.Excel;
+using Lumina.Excel.Sheets;
+using Lumina.Text.ReadOnly;
+
+namespace Questionable.Data.Sheets;
+
+// TODO Remove once fixed in dalamud
+[Sheet("Quest", 0x1F8C7430)]
+public readonly unsafe struct QuestEx(ExcelPage page, uint offset, uint row) : IExcelRow<QuestEx>
+{
+ public uint RowId => row;
+
+ public Quest Original { get; } = new(page, offset, row);
+
+ public RowRef IssuerStart => RowRef.GetFirstValidRowOrUntyped(page.Module, page.ReadUInt32(offset + 2456), [typeof(EObjName), typeof(ENpcResident)], 882056187, page.Language);
+ public RowRef<Level> IssuerLocation => new(page.Module, page.ReadUInt32(offset + 2460), page.Language);
+ public RowRef<JournalGenre> JournalGenre => new(page.Module, page.ReadUInt32(offset + 2468), page.Language);
+ public ushort SortKey => page.ReadUInt16(offset + 2502);
+ public readonly RowRef<ExVersion> Expansion => new(page.Module, (uint)page.ReadUInt8(offset + 2504), page.Language);
+ public readonly byte PreviousQuestJoin => page.ReadUInt8(offset + 2508);
+ public readonly RowRef<ClassJobCategory> ClassJobCategory0 => new(page.Module, (uint)page.ReadUInt8(offset + 2505), page.Language);
+ public readonly RowRef<ClassJobCategory> ClassJobCategory1 => new(page.Module, (uint)page.ReadUInt8(offset + 2507), page.Language);
+ public readonly RowRef<Festival> Festival => new(page.Module, (uint)page.ReadUInt8(offset + 2517), page.Language);
+ public readonly byte Unknown7 => page.ReadUInt8(offset + 2509);
+ public readonly byte QuestLockJoin => page.ReadUInt8(offset + 2510);
+ public readonly RowRef<GrandCompany> GrandCompany => new(page.Module, (uint)page.ReadUInt8(offset + 2514), page.Language);
+ public readonly byte InstanceContentJoin => page.ReadUInt8(offset + 2516);
+ public readonly RowRef<BeastTribe> BeastTribe => new(page.Module, (uint)page.ReadUInt8(offset + 2520), page.Language);
+ public bool IsRepeatable => page.ReadPackedBool(offset + 2535, 1);
+
+ public readonly Collection<RowRef<Quest>> PreviousQuest => new(page, offset, offset, &PreviousQuestCtor, 3);
+ private static RowRef<Quest> PreviousQuestCtor(ExcelPage page, uint parentOffset, uint offset, uint i) => new(page.Module, page.ReadUInt32(offset + 2424 + i * 4), page.Language);
+
+ public readonly Collection<RowRef<Quest>> QuestLock => new(page, offset, offset, &QuestLockCtor, 2);
+ private static RowRef<Quest> QuestLockCtor(ExcelPage page, uint parentOffset, uint offset, uint i) => new(page.Module, page.ReadUInt32(offset + 2436 + i * 4), page.Language);
+
+ public readonly Collection<ushort> ClassJobLevel => new(page, offset, offset, &ClassJobLevelCtor, 2);
+ private static ushort ClassJobLevelCtor(ExcelPage page, uint parentOffset, uint offset, uint i) => page.ReadUInt16(offset + 2484 + i * 2);
+
+ public Collection<RowRef<InstanceContent>> InstanceContent => new(page, offset, offset, &InstanceContentCtor, 3);
+ private static RowRef<InstanceContent> InstanceContentCtor(ExcelPage page, uint parentOffset, uint offset, uint i) => new(page.Module, page.ReadUInt32(offset + 2444 + i * 4), page.Language);
+
+ static QuestEx IExcelRow<QuestEx>.Create(ExcelPage page, uint offset, uint row) =>
+ new(page, offset, row);
+}
using System.Linq;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
namespace Questionable.Data;
private readonly ImmutableDictionary<uint, string> _territoryNames;
private readonly ImmutableHashSet<ushort> _territoriesWithMount;
private readonly ImmutableDictionary<ushort, uint> _dutyTerritories;
- private readonly ImmutableDictionary<ushort, string> _instanceNames;
+ private readonly ImmutableDictionary<uint, string> _instanceNames;
private readonly ImmutableDictionary<uint, string> _contentFinderConditionNames;
public TerritoryData(IDataManager dataManager)
{
- _territoryNames = dataManager.GetExcelSheet<TerritoryType>()!
+ _territoryNames = dataManager.GetExcelSheet<TerritoryType>()
.Where(x => x.RowId > 0)
.Select(x =>
new
{
x.RowId,
- Name = x.PlaceName.Value?.Name?.ToString() ?? x.PlaceNameZone?.Value?.Name?.ToString(),
+ Name = x.PlaceName.ValueNullable?.Name.ToString() ?? x.PlaceNameZone.ValueNullable?.Name.ToString(),
})
.Where(x => !string.IsNullOrEmpty(x.Name))
.ToImmutableDictionary(x => x.RowId, x => x.Name!);
- _territoriesWithMount = dataManager.GetExcelSheet<TerritoryType>()!
+ _territoriesWithMount = dataManager.GetExcelSheet<TerritoryType>()
.Where(x => x.RowId > 0 && x.Mount)
.Select(x => (ushort)x.RowId)
.ToImmutableHashSet();
- _dutyTerritories = dataManager.GetExcelSheet<TerritoryType>()!
- .Where(x => x.RowId > 0 && x.ContentFinderCondition.Row != 0)
- .ToImmutableDictionary(x => (ushort)x.RowId, x => x.ContentFinderCondition.Value!.ContentType.Row);
+ _dutyTerritories = dataManager.GetExcelSheet<TerritoryType>()
+ .Where(x => x.RowId > 0 && x.ContentFinderCondition.RowId != 0)
+ .ToImmutableDictionary(x => (ushort)x.RowId, x => x.ContentFinderCondition.Value.ContentType.RowId);
- _instanceNames = dataManager.GetExcelSheet<ContentFinderCondition>()!
- .Where(x => x.RowId > 0 && x.Content != 0 && x.ContentLinkType == 1 && x.ContentType.Row != 6)
- .ToImmutableDictionary(x => x.Content, x => x.Name.ToString());
+ _instanceNames = dataManager.GetExcelSheet<ContentFinderCondition>()
+ .Where(x => x.RowId > 0 && x.Content.RowId != 0 && x.ContentLinkType == 1 && x.ContentType.RowId != 6)
+ .ToImmutableDictionary(x => x.Content.RowId, x => x.Name.ToString());
- _contentFinderConditionNames = dataManager.GetExcelSheet<ContentFinderCondition>()!
- .Where(x => x.RowId > 0 && x.Content != 0 && x.ContentLinkType == 1 && x.ContentType.Row != 6)
+ _contentFinderConditionNames = dataManager.GetExcelSheet<ContentFinderCondition>()
+ .Where(x => x.RowId > 0 && x.Content.RowId != 0 && x.ContentLinkType == 1 && x.ContentType.RowId != 6)
.ToImmutableDictionary(x => x.RowId, x => x.Name.ToString());
}
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;
using Questionable.Model.Questing;
-using Action = Lumina.Excel.GeneratedSheets.Action;
+using Action = Lumina.Excel.Sheets.Action;
namespace Questionable.Functions;
public bool IsTeleportUnlocked()
{
- uint unlockLink = _dataManager.GetExcelSheet<Action>()!
- .GetRow(5)!
- .UnlockLink;
+ uint unlockLink = _dataManager.GetExcelSheet<Action>()
+ .GetRow(5)
+ .UnlockLink
+ .RowId;
return UIState.Instance()->IsUnlockLinkUnlocked(unlockLink);
}
using FFXIVClientStructs.FFXIV.Client.System.Framework;
using FFXIVClientStructs.FFXIV.Client.System.Memory;
using FFXIVClientStructs.FFXIV.Client.System.String;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using Microsoft.Extensions.Logging;
using Questionable.Model.Questing;
_sanitiseString =
(delegate* unmanaged<Utf8String*, int, IntPtr, void>)sigScanner.ScanText(Signatures.SanitiseString);
- _emoteCommands = dataManager.GetExcelSheet<Emote>()!
+ _emoteCommands = dataManager.GetExcelSheet<Emote>()
.Where(x => x.RowId > 0)
- .Where(x => x.TextCommand != null && x.TextCommand.Value != null)
- .Select(x => (x.RowId, Command: x.TextCommand.Value!.Command?.ToString()))
- .Where(x => x.Command != null && x.Command.StartsWith('/'))
+ .Where(x => x.TextCommand.IsValid)
+ .Select(x => (x.RowId, Command: x.TextCommand.Value.Command.ToString()))
+ .Where(x => !string.IsNullOrEmpty(x.Command) && x.Command.StartsWith('/'))
.ToDictionary(x => (EEmote)x.RowId, x => x.Command!)
.AsReadOnly();
}
private static class Signatures
{
- internal const string SendChat = "48 89 5C 24 ?? 57 48 83 EC 20 48 8B FA 48 8B D9 45 84 C9";
- internal const string SanitiseString = "E8 ?? ?? ?? ?? 48 8D 4C 24 ?? 0F B6 F0 E8 ?? ?? ?? ?? 48 8D 4D C0";
+ internal const string SendChat = "48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F2 48 8B F9 45 84 C9";
+ internal const string SanitiseString = "E8 ?? ?? ?? ?? EB 0A 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8D AE";
}
[StructLayout(LayoutKind.Explicit)]
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using LLib;
-using Lumina.Excel.CustomSheets;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using Lumina.Text;
+using Lumina.Text.ReadOnly;
using Microsoft.Extensions.Logging;
using Questionable.Model;
using Quest = Questionable.Model.Quest;
-using GimmickYesNo = Lumina.Excel.GeneratedSheets2.GimmickYesNo;
+using GimmickYesNo = Lumina.Excel.Sheets.GimmickYesNo;
namespace Questionable.Functions;
return new StringOrRegex(seString?.ToDalamudString().ToString());
}
- public SeString? GetRawDialogueText(Quest? currentQuest, string? excelSheetName, string key)
+ public ReadOnlySeString? GetRawDialogueText(Quest? currentQuest, string? excelSheetName, string key)
{
if (currentQuest != null && excelSheetName == null)
{
var questRow =
- _dataManager.GetExcelSheet<Lumina.Excel.GeneratedSheets2.Quest>()!.GetRow((uint)currentQuest.Id.Value +
+ _dataManager.GetExcelSheet<Lumina.Excel.Sheets.Quest>().GetRowOrDefault((uint)currentQuest.Id.Value +
0x10000);
if (questRow == null)
{
return null;
}
- excelSheetName = $"quest/{(currentQuest.Id.Value / 100):000}/{questRow.Id}";
+ excelSheetName = $"quest/{(currentQuest.Id.Value / 100):000}/{questRow.Value.RowId}";
}
ArgumentNullException.ThrowIfNull(excelSheetName);
- var excelSheet = _dataManager.Excel.GetSheet<QuestDialogueText>(excelSheetName);
- if (excelSheet == null)
- {
- _logger.LogError("Unknown excel sheet '{SheetName}'", excelSheetName);
- return null;
- }
-
- return excelSheet.FirstOrDefault(x => x.Key == key)?.Value;
+ var excelSheet = _dataManager.GetExcelSheet<QuestDialogueText>(name: excelSheetName);
+ return excelSheet.Cast<QuestDialogueText?>()
+ .FirstOrDefault(x => x!.Value.Key == key)?.Value;
}
public StringOrRegex GetDialogueTextByRowId(string? excelSheet, uint rowId, bool isRegex)
return new StringOrRegex(seString?.ToDalamudString().ToString());
}
- public SeString? GetRawDialogueTextByRowId(string? excelSheet, uint rowId)
+ public ReadOnlySeString? GetRawDialogueTextByRowId(string? excelSheet, uint rowId)
{
if (excelSheet == "GimmickYesNo")
{
- var questRow = _dataManager.GetExcelSheet<GimmickYesNo>()!.GetRow(rowId);
+ var questRow = _dataManager.GetExcelSheet<GimmickYesNo>().GetRowOrDefault(rowId);
return questRow?.Unknown0;
}
else if (excelSheet == "Warp")
{
- var questRow = _dataManager.GetExcelSheet<Warp>()!.GetRow(rowId);
+ var questRow = _dataManager.GetExcelSheet<Warp>().GetRowOrDefault(rowId);
return questRow?.Name;
}
else if (excelSheet is "Addon")
{
- var questRow = _dataManager.GetExcelSheet<Addon>()!.GetRow(rowId);
+ var questRow = _dataManager.GetExcelSheet<Addon>().GetRowOrDefault(rowId);
return questRow?.Text;
}
else if (excelSheet is "EventPathMove")
{
- var questRow = _dataManager.GetExcelSheet<EventPathMove>()!.GetRow(rowId);
- return questRow?.Unknown10;
+ var questRow = _dataManager.GetExcelSheet<EventPathMove>().GetRowOrDefault(rowId);
+ return questRow?.Unknown0;
}
else if (excelSheet is "GilShop")
{
- var questRow = _dataManager.GetExcelSheet<GilShop>()!.GetRow(rowId);
+ var questRow = _dataManager.GetExcelSheet<GilShop>().GetRowOrDefault(rowId);
return questRow?.Name;
}
else if (excelSheet is "ContentTalk" or null)
{
- var questRow = _dataManager.GetExcelSheet<ContentTalk>()!.GetRow(rowId);
+ var questRow = _dataManager.GetExcelSheet<ContentTalk>().GetRowOrDefault(rowId);
return questRow?.Text;
}
else
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Component.GUI;
using LLib.GameUI;
+using Lumina.Excel.Sheets;
using Microsoft.Extensions.Logging;
using Questionable.Model;
-using Questionable.Model.Common;
using Questionable.Model.Questing;
-using Action = Lumina.Excel.GeneratedSheets2.Action;
+using Action = Lumina.Excel.Sheets.Action;
using BattleChara = FFXIVClientStructs.FFXIV.Client.Game.Character.BattleChara;
-using ContentFinderCondition = Lumina.Excel.GeneratedSheets.ContentFinderCondition;
+using ContentFinderCondition = Lumina.Excel.Sheets.ContentFinderCondition;
using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
using Quest = Questionable.Model.Quest;
-using TerritoryType = Lumina.Excel.GeneratedSheets.TerritoryType;
namespace Questionable.Functions;
internal sealed unsafe class GameFunctions
{
private readonly ReadOnlyDictionary<ushort, byte> _territoryToAetherCurrentCompFlgSet;
- private readonly ReadOnlyDictionary<uint, ushort> _contentFinderConditionToContentId;
+ private readonly ReadOnlyDictionary<uint, uint> _contentFinderConditionToContentId;
private readonly QuestFunctions _questFunctions;
private readonly IDataManager _dataManager;
_configuration = configuration;
_logger = logger;
- _territoryToAetherCurrentCompFlgSet = dataManager.GetExcelSheet<TerritoryType>()!
+ _territoryToAetherCurrentCompFlgSet = dataManager.GetExcelSheet<TerritoryType>()
.Where(x => x.RowId > 0)
- .Where(x => x.Unknown32 > 0)
- .ToDictionary(x => (ushort)x.RowId, x => x.Unknown32)
+ .Where(x => x.Unknown3 > 0)
+ .ToDictionary(x => (ushort)x.RowId, x => x.Unknown4)
.AsReadOnly();
- _contentFinderConditionToContentId = dataManager.GetExcelSheet<ContentFinderCondition>()!
- .Where(x => x.RowId > 0 && x.Content > 0)
- .ToDictionary(x => x.RowId, x => x.Content)
+ _territoryToAetherCurrentCompFlgSet = new Dictionary<ushort, byte>().AsReadOnly();
+ _contentFinderConditionToContentId = dataManager.GetExcelSheet<ContentFinderCondition>()
+ .Where(x => x.RowId > 0 && x.Content.RowId > 0)
+ .ToDictionary(x => x.RowId, x => x.Content.RowId)
.AsReadOnly();
}
public bool UseAction(IGameObject gameObject, EAction action, bool checkCanUse = true)
{
- var actionRow = _dataManager.GetExcelSheet<Action>()!.GetRow((uint)action)!;
+ var actionRow = _dataManager.GetExcelSheet<Action>().GetRow((uint)action);
if (checkCanUse && !ActionManager.CanUseActionOnTarget((uint)action, (GameObject*)gameObject.Address))
{
_logger.LogWarning("Can not use action {Action} on target {Target}", action, gameObject);
public void OpenDutyFinder(uint contentFinderConditionId)
{
- if (_contentFinderConditionToContentId.TryGetValue(contentFinderConditionId, out ushort contentId))
+ if (_contentFinderConditionToContentId.TryGetValue(contentFinderConditionId, out uint contentId))
{
if (UIState.IsInstanceContentUnlocked(contentId))
AgentContentsFinder.Instance()->OpenRegularDuty(contentFinderConditionId);
using FFXIVClientStructs.FFXIV.Component.GUI;
using LLib.GameData;
using LLib.GameUI;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using Questionable.Controller;
using Questionable.Controller.Steps.Interactions;
using Questionable.Data;
..QuestData.CrystalTowerQuests
];
- EClassJob classJob = (EClassJob?)_clientState.LocalPlayer?.ClassJob.Id ?? EClassJob.Adventurer;
- ushort[] shadowbringersRoleQuestChapters = QuestData.AllRoleQuestChapters.Select(x => x[0]).ToArray();
+ EClassJob classJob = (EClassJob?)_clientState.LocalPlayer?.ClassJob.RowId ?? EClassJob.Adventurer;
+ uint[] shadowbringersRoleQuestChapters = QuestData.AllRoleQuestChapters.Select(x => x[0]).ToArray();
if (classJob != EClassJob.Adventurer)
{
priorityQuests.AddRange(_questRegistry.GetKnownClassJobQuests(classJob)
// this only checks for the current class
IQuestInfo questInfo = _questData.GetQuestInfo(leveId);
- if (!questInfo.ClassJobs.Contains((EClassJob)_clientState.LocalPlayer!.ClassJob.Id) ||
+ if (!questInfo.ClassJobs.Contains((EClassJob)_clientState.LocalPlayer!.ClassJob.RowId) ||
questInfo.Level > _clientState.LocalPlayer.Level)
return true;
public bool IsClassJobUnlocked(EClassJob classJob)
{
var classJobRow = _dataManager.GetExcelSheet<ClassJob>()!.GetRow((uint)classJob)!;
- var questId = (ushort)classJobRow.UnlockQuest.Row;
+ var questId = (ushort)classJobRow.UnlockQuest.RowId;
if (questId != 0)
return IsQuestComplete(new QuestId(questId));
public bool IsJobUnlocked(EClassJob classJob)
{
var classJobRow = _dataManager.GetExcelSheet<ClassJob>()!.GetRow((uint)classJob)!;
- return IsClassJobUnlocked((EClassJob)classJobRow.ClassJobParent.Row);
+ return IsClassJobUnlocked((EClassJob)classJobRow.ClassJobParent.RowId);
}
public GrandCompany GetGrandCompany()
using System.Collections.Generic;
using System.Collections.Immutable;
using LLib.GameData;
-using Lumina.Excel.GeneratedSheets2;
+using Lumina.Excel.Sheets;
using Questionable.Model.Questing;
namespace Questionable.Model;
public LeveInfo(Leve leve)
{
QuestId = new LeveId((ushort)leve.RowId);
- Name = leve.Name;
+ Name = leve.Name.ToString();
Level = leve.ClassJobLevel;
- JournalGenre = leve.JournalGenre.Row;
+ JournalGenre = leve.JournalGenre.RowId;
SortKey = QuestId.Value;
- IssuerDataId = leve.LevelLevemete.Value!.Object.Row;
- ClassJobs = QuestInfoUtils.AsList(leve.ClassJobCategory.Value!);
- Expansion = (EExpansionVersion)leve.LevelLevemete.Value.Territory.Value!.ExVersion.Row;
+ IssuerDataId = leve.LevelLevemete.Value.Object.RowId;
+ ClassJobs = QuestInfoUtils.AsList(leve.ClassJobCategory.Value);
+ Expansion = (EExpansionVersion)leve.LevelLevemete.Value.Territory.Value.ExVersion.RowId;
}
public ElementId QuestId { get; }
using System.Collections.Immutable;
using System.Linq;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
-using JetBrains.Annotations;
using LLib.GameData;
+using Questionable.Data.Sheets;
using Questionable.Model.Questing;
-using ExcelQuest = Lumina.Excel.GeneratedSheets2.Quest;
namespace Questionable.Model;
internal sealed class QuestInfo : IQuestInfo
{
- public QuestInfo(ExcelQuest quest, ushort newGamePlusChapter, byte startingCity)
+ public QuestInfo(QuestEx quest, uint newGamePlusChapter, byte startingCity)
{
QuestId = new QuestId((ushort)(quest.RowId & 0xFFFF));
_ => "",
};
- Name = $"{quest.Name}{suffix}";
+ Name = $"{quest.Original.Name}{suffix}";
Level = quest.ClassJobLevel[0];
- IssuerDataId = quest.IssuerStart.Row;
+ IssuerDataId = quest.IssuerStart.RowId;
IsRepeatable = quest.IsRepeatable;
PreviousQuests =
new List<PreviousQuestInfo>
{
- new(new QuestId((ushort)(quest.PreviousQuest[0].Row & 0xFFFF)), quest.Unknown7),
- new(new QuestId((ushort)(quest.PreviousQuest[1].Row & 0xFFFF))),
- new(new QuestId((ushort)(quest.PreviousQuest[2].Row & 0xFFFF)))
+ new(new QuestId((ushort)(quest.PreviousQuest[0].RowId & 0xFFFF)), quest.Unknown7),
+ new(new QuestId((ushort)(quest.PreviousQuest[1].RowId & 0xFFFF))),
+ new(new QuestId((ushort)(quest.PreviousQuest[2].RowId & 0xFFFF)))
}
.Where(x => x.QuestId.Value != 0)
.ToImmutableList();
PreviousQuestJoin = (EQuestJoin)quest.PreviousQuestJoin;
QuestLocks = quest.QuestLock
- .Select(x => new QuestId((ushort)(x.Row & 0xFFFFF)))
+ .Select(x => new QuestId((ushort)(x.RowId & 0xFFFFF)))
.Where(x => x.Value != 0)
.ToImmutableList();
QuestLockJoin = (EQuestJoin)quest.QuestLockJoin;
- JournalGenre = quest.JournalGenre?.Row;
+ JournalGenre = quest.JournalGenre.ValueNullable?.RowId;
SortKey = quest.SortKey;
- IsMainScenarioQuest = quest.JournalGenre?.Value?.JournalCategory?.Value?.JournalSection?.Row is 0 or 1;
- CompletesInstantly = quest.TodoParams[0].ToDoCompleteSeq == 0;
- PreviousInstanceContent = quest.InstanceContent.Select(x => (ushort)x.Row).Where(x => x != 0).ToList();
+ IsMainScenarioQuest = quest.JournalGenre.ValueNullable?.JournalCategory.ValueNullable?.JournalSection.ValueNullable?.RowId is 0 or 1;
+ CompletesInstantly = quest.Original.TodoParams[0].ToDoCompleteSeq == 0;
+ PreviousInstanceContent = quest.InstanceContent.Select(x => (ushort)x.RowId).Where(x => x != 0).ToList();
PreviousInstanceContentJoin = (EQuestJoin)quest.InstanceContentJoin;
- GrandCompany = (GrandCompany)quest.GrandCompany.Row;
- AlliedSociety = (EAlliedSociety)quest.BeastTribe.Row;
- ClassJobs = QuestInfoUtils.AsList(quest.ClassJobCategory0.Value!);
- IsSeasonalEvent = quest.Festival.Row != 0;
+ GrandCompany = (GrandCompany)quest.GrandCompany.RowId;
+ AlliedSociety = (EAlliedSociety)quest.BeastTribe.RowId;
+ ClassJobs = QuestInfoUtils.AsList(quest.ClassJobCategory0.ValueNullable!);
+ IsSeasonalEvent = quest.Festival.RowId != 0;
NewGamePlusChapter = newGamePlusChapter;
StartingCity = startingCity;
- Expansion = (EExpansionVersion)quest.Expansion.Row;
+ Expansion = (EExpansionVersion)quest.Expansion.RowId;
}
public EAlliedSociety AlliedSociety { get; }
public IReadOnlyList<EClassJob> ClassJobs { get; }
public bool IsSeasonalEvent { get; }
- public ushort NewGamePlusChapter { get; }
+ public uint NewGamePlusChapter { get; }
public byte StartingCity { get; set; }
public EExpansionVersion Expansion { get; }
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using LLib.GameData;
-using Lumina.Excel.GeneratedSheets2;
+using Lumina.Excel.Sheets;
namespace Questionable.Model;
{
private static readonly Dictionary<uint, IReadOnlyList<EClassJob>> CachedClassJobs = new();
- internal static IReadOnlyList<EClassJob> AsList(ClassJobCategory classJobCategory)
+ internal static IReadOnlyList<EClassJob> AsList(ClassJobCategory? optionalClassJobCategory)
{
+ if (optionalClassJobCategory == null)
+ return Enum.GetValues<EClassJob>();
+
+ ClassJobCategory classJobCategory = optionalClassJobCategory.Value;
if (CachedClassJobs.TryGetValue(classJobCategory.RowId, out IReadOnlyList<EClassJob>? classJobs))
return classJobs;
{ EClassJob.Dancer, classJobCategory.DNC },
{ EClassJob.Reaper, classJobCategory.RPR },
{ EClassJob.Sage, classJobCategory.SGE },
- { EClassJob.Viper, classJobCategory.Unknown1 },
- { EClassJob.Pictomancer, classJobCategory.Unknown2 }
+ { EClassJob.Viper, classJobCategory.VPR },
+ { EClassJob.Pictomancer, classJobCategory.PCT }
}
.Where(y => y.Value)
.Select(y => y.Key)
using System.Collections.Generic;
using System.Collections.Immutable;
using LLib.GameData;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using Questionable.Model.Questing;
namespace Questionable.Model;
public SatisfactionSupplyInfo(SatisfactionNpc npc)
{
QuestId = new SatisfactionSupplyNpcId((ushort)npc.RowId);
- Name = npc.Npc.Value!.Singular;
- IssuerDataId = npc.Npc.Row;
+ Name = npc.Npc.Value.Singular.ToString();
+ IssuerDataId = npc.Npc.RowId;
Level = npc.LevelUnlock;
SortKey = QuestId.Value;
- Expansion = (EExpansionVersion)npc.QuestRequired.Value!.Expansion.Row;
- PreviousQuests = [new PreviousQuestInfo(new QuestId((ushort)(npc.QuestRequired.Row & 0xFFFF)))];
+ Expansion = (EExpansionVersion)npc.QuestRequired.Value!.Expansion.RowId;
+ PreviousQuests = [new PreviousQuestInfo(new QuestId((ushort)(npc.QuestRequired.RowId & 0xFFFF)))];
}
public ElementId QuestId { get; }
-<Project Sdk="Dalamud.NET.Sdk/10.0.0">
+<Project Sdk="Dalamud.NET.Sdk/11.0.0">
<PropertyGroup>
<OutputPath>dist</OutputPath>
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
using System;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Dalamud.Game.Text;
using Dalamud.Utility;
using ImGuiNET;
using LLib.ImGui;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using Questionable.External;
using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany;
_notificationMasterIpc = notificationMasterIpc;
_configuration = configuration;
- var mounts = dataManager.GetExcelSheet<Mount>()!
+ var mounts = dataManager.GetExcelSheet<Mount>()
.Where(x => x is { RowId: > 0, Icon: > 0 })
.Select(x => (MountId: x.RowId, Name: x.Singular.ToString()))
.Where(x => !string.IsNullOrEmpty(x.Name))
using Dalamud.Utility.Signatures;
using ImGuiNET;
using LLib.GameData;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using Questionable.Controller;
using Questionable.Model;
using Questionable.Model.Gathering;
_gatheringPointRegistry = gatheringPointRegistry;
// TODO some of the logic here would be better suited elsewhere, in particular the [item] → [gathering item] → [location] lookup
- var routeToGatheringPoint = dataManager.GetExcelSheet<GatheringLeveRoute>()!
- .Where(x => x.UnkData0[0].GatheringPoint != 0)
- .SelectMany(x => x.UnkData0
- .Where(y => y.GatheringPoint != 0)
+ var routeToGatheringPoint = dataManager.GetExcelSheet<GatheringLeveRoute>()
+ .Where(x => x.GatheringPoint[0].RowId != 0)
+ .SelectMany(x => x.GatheringPoint
+ .Where(y => y.RowId != 0)
.Select(y => new
{
RouteId = x.RowId,
- GatheringPointId = y.GatheringPoint
+ GatheringPointId = y.RowId
}))
.GroupBy(x => x.RouteId)
.ToDictionary(x => x.Key, x => x.Select(y => y.GatheringPointId).ToList());
- var gatheringLeveSheet = dataManager.GetExcelSheet<GatheringLeve>()!;
- var territoryTypeSheet = dataManager.GetExcelSheet<TerritoryType>()!;
- var gatheringPointToLeve = dataManager.GetExcelSheet<Leve>()!
+ var gatheringLeveSheet = dataManager.GetExcelSheet<GatheringLeve>();
+ var territoryTypeSheet = dataManager.GetExcelSheet<TerritoryType>();
+ var gatheringPointToLeve = dataManager.GetExcelSheet<Leve>()
.Where(x => x.RowId > 0)
.Select(x =>
{
- uint startZonePlaceName = x.PlaceNameStartZone.Row;
+ uint startZonePlaceName = x.PlaceNameStartZone.RowId;
startZonePlaceName = startZonePlaceName switch
{
27 => 28, // limsa
_ => startZonePlaceName
};
- var territoryType = territoryTypeSheet.FirstOrDefault(y => startZonePlaceName == y.PlaceName.Row)
+ var territoryType = territoryTypeSheet.Cast<TerritoryType?>()
+ .FirstOrDefault(y => startZonePlaceName == y!.Value.PlaceName.RowId)
?? throw new InvalidOperationException($"Unable to use {startZonePlaceName}");
return new
{
LeveId = x.RowId,
LeveName = x.Name.ToString(),
TerritoryType = (ushort)territoryType.RowId,
- TerritoryName = territoryType.PlaceName.Value?.Name.ToString(),
- Expansion = (EExpansionVersion)territoryType.ExVersion.Row,
- GatheringLeve = gatheringLeveSheet.GetRow((uint)x.DataId),
+ TerritoryName = territoryType.PlaceName.ValueNullable?.Name.ToString(),
+ Expansion = (EExpansionVersion)territoryType.ExVersion.RowId,
+ GatheringLeve = gatheringLeveSheet.GetRowOrDefault(x.DataId.RowId),
};
})
.Where(x => x.GatheringLeve != null)
x.TerritoryType,
x.TerritoryName,
x.Expansion,
- GatheringPoints = x.GatheringLeve!.Route
- .Where(y => y.Row != 0)
- .SelectMany(y => routeToGatheringPoint[y.Row]),
+ GatheringPoints = x.GatheringLeve!.Value.Route
+ .Where(y => y.RowId != 0)
+ .SelectMany(y => routeToGatheringPoint[y.RowId]),
})
.SelectMany(x => x.GatheringPoints.Select(y => new
{
var itemSheet = dataManager.GetExcelSheet<Item>()!;
_gatheringItems = dataManager.GetExcelSheet<GatheringItem>()!
- .Where(x => x.RowId != 0 && x.GatheringItemLevel.Row != 0)
+ .Where(x => x.RowId != 0 && x.GatheringItemLevel.RowId != 0)
.Select(x => new
{
GatheringItemId = (int)x.RowId,
- Name = itemSheet.GetRow((uint)x.Item)?.Name.ToString()
+ Name = itemSheet.GetRowOrDefault(x.Item.RowId)?.Name.ToString()
})
.Where(x => !string.IsNullOrEmpty(x.Name))
.ToDictionary(x => x.GatheringItemId, x => x.Name!);
_gatheringPointsByExpansion = dataManager.GetExcelSheet<GatheringPoint>()!
- .Where(x => x.GatheringPointBase.Row != 0)
- .Where(x => x.GatheringPointBase.Row is < 653 or > 680) // exclude ishgard restoration phase 1
- .DistinctBy(x => x.GatheringPointBase.Row)
+ .Where(x => x.GatheringPointBase.RowId != 0)
+ .Where(x => x.GatheringPointBase.RowId is < 653 or > 680) // exclude ishgard restoration phase 1
+ .DistinctBy(x => x.GatheringPointBase.RowId)
.Select(x => new
{
GatheringPointId = x.RowId,
- Point = new DefaultGatheringPoint(new GatheringPointId((ushort)x.GatheringPointBase.Row),
- x.GatheringPointBase.Value!.GatheringType.Row switch
+ Point = new DefaultGatheringPoint(new GatheringPointId((ushort)x.GatheringPointBase.RowId),
+ x.GatheringPointBase.Value!.GatheringType.RowId switch
{
0 or 1 => EClassJob.Miner,
2 or 3 => EClassJob.Botanist,
_ => EClassJob.Fisher
},
x.GatheringPointBase.Value.GatheringLevel,
- x.GatheringPointBase.Value.Item.Where(y => y != 0).Select(y => (ushort)y).ToList(),
- (EExpansionVersion?)x.TerritoryType.Value?.ExVersion.Row ?? (EExpansionVersion)byte.MaxValue,
- (ushort)x.TerritoryType.Row,
- x.TerritoryType.Value?.PlaceName.Value?.Name.ToString(),
- $"{x.GatheringPointBase.Row} - {x.PlaceName.Value?.Name}")
+ x.GatheringPointBase.Value.Item.Where(y => y.RowId != 0).Select(y => (ushort)y.RowId).ToList(),
+ (EExpansionVersion?)x.TerritoryType.ValueNullable?.ExVersion.RowId ?? (EExpansionVersion)byte.MaxValue,
+ (ushort)x.TerritoryType.RowId,
+ x.TerritoryType.ValueNullable?.PlaceName.ValueNullable?.Name.ToString(),
+ $"{x.GatheringPointBase.RowId} - {x.PlaceName.ValueNullable?.Name}")
})
.Where(x => x.Point.ClassJob != EClassJob.Fisher)
.Select(x =>
{
- if (gatheringPointToLeve.TryGetValue((int)x.GatheringPointId, out var leve))
+ if (gatheringPointToLeve.TryGetValue(x.GatheringPointId, out var leve))
{
// it's a leve
return x.Point with
var territoryType = territoryTypeSheet.GetRow(gatheringRoot.Steps.Last().TerritoryId)!;
return x.Point with
{
- Expansion = (EExpansionVersion)territoryType.ExVersion.Row,
+ Expansion = (EExpansionVersion)territoryType.ExVersion.RowId,
TerritoryType = (ushort)territoryType.RowId,
- TerritoryName = territoryType.PlaceName.Value?.Name.ToString(),
+ TerritoryName = territoryType.PlaceName.ValueNullable?.Name.ToString(),
};
}
else
}
}
- public void ClearCounts()
+ public void ClearCounts(int type, int code)
{
foreach (var expansion in _gatheringPointsByExpansion)
{
}
}
- internal void ClearCounts()
+ internal void ClearCounts(int type, int code)
{
foreach (var genreCount in _genreCounts.ToList())
_genreCounts[genreCount.Key] = genreCount.Value with { Completed = 0 };
#endif
SizeConstraints = new WindowSizeConstraints
{
- MinimumSize = new Vector2(200, 30),
+ MinimumSize = new Vector2(230, 30),
MaximumSize = default
};
RespectCloseHotkey = false;
-Subproject commit 147e12e95f2fb781f2c8ddac31d948700ed9051c
+Subproject commit 71ee09f7cc2230a73503b945422760da1368405c