fix(ui): select and examine favorite spells like retail
This commit is contained in:
parent
043ab10b3c
commit
3e31b0ac70
23 changed files with 974 additions and 34 deletions
|
|
@ -17,6 +17,12 @@ public sealed record SpellComponentDescriptor(
|
|||
uint Category,
|
||||
uint IconId);
|
||||
|
||||
/// <summary>One resolved formula cell in retail's spell examination subview.</summary>
|
||||
public readonly record struct SpellExamineComponent(
|
||||
uint SpellComponentId,
|
||||
SpellComponentDescriptor Descriptor,
|
||||
bool Owned);
|
||||
|
||||
/// <summary>
|
||||
/// App-layer projection of retail's spell, component, and school-focus DAT tables.
|
||||
/// Keeping this catalog outside <c>GameWindow</c> makes the SCID/WCID boundary and
|
||||
|
|
@ -52,6 +58,20 @@ public sealed class MagicCatalog
|
|||
public CoreSpellTable SpellTable { get; }
|
||||
public IReadOnlyDictionary<uint, SpellComponentDescriptor> Components { get; }
|
||||
|
||||
public bool TryGetComponentBySpellComponentId(
|
||||
uint spellComponentId,
|
||||
out SpellComponentDescriptor descriptor)
|
||||
{
|
||||
if (_wcidByScid.TryGetValue(spellComponentId, out uint weenieClassId)
|
||||
&& Components.TryGetValue(weenieClassId, out SpellComponentDescriptor? found))
|
||||
{
|
||||
descriptor = found;
|
||||
return true;
|
||||
}
|
||||
descriptor = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsComponentPack(uint weenieClassId)
|
||||
=> Components.ContainsKey(weenieClassId);
|
||||
|
||||
|
|
@ -143,15 +163,43 @@ public sealed class MagicCatalog
|
|||
/// </summary>
|
||||
public sealed class MagicRuntime
|
||||
{
|
||||
private MagicRuntime(MagicCatalog catalog, SpellCastingController casting)
|
||||
private readonly SpellComponentRequirementService _requirements;
|
||||
|
||||
private MagicRuntime(
|
||||
MagicCatalog catalog,
|
||||
SpellCastingController casting,
|
||||
SpellComponentRequirementService requirements)
|
||||
{
|
||||
Catalog = catalog;
|
||||
Casting = casting;
|
||||
_requirements = requirements;
|
||||
}
|
||||
|
||||
public MagicCatalog Catalog { get; }
|
||||
public SpellCastingController Casting { get; }
|
||||
|
||||
public IReadOnlyList<SpellExamineComponent> GetExamineComponents(uint spellId)
|
||||
{
|
||||
IReadOnlyList<uint> formula = _requirements.GetAppropriateFormula(spellId);
|
||||
if (formula.Count == 0)
|
||||
return [];
|
||||
|
||||
var result = new List<SpellExamineComponent>(formula.Count);
|
||||
foreach (uint spellComponentId in formula)
|
||||
{
|
||||
if (spellComponentId == 0u
|
||||
|| !Catalog.TryGetComponentBySpellComponentId(
|
||||
spellComponentId,
|
||||
out SpellComponentDescriptor descriptor))
|
||||
continue;
|
||||
result.Add(new SpellExamineComponent(
|
||||
spellComponentId,
|
||||
descriptor,
|
||||
_requirements.IsComponentOwned(spellComponentId)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static MagicRuntime Create(
|
||||
MagicCatalog catalog,
|
||||
Spellbook spellbook,
|
||||
|
|
@ -198,7 +246,7 @@ public sealed class MagicRuntime
|
|||
incrementBusy: incrementBusy,
|
||||
canSend: canSend,
|
||||
targetCompatibleSilent: (target, spell) => TargetCompatible(target, spell, false));
|
||||
return new MagicRuntime(catalog, casting);
|
||||
return new MagicRuntime(catalog, casting, requirements);
|
||||
}
|
||||
|
||||
public void Reset() => Casting.Reset();
|
||||
|
|
|
|||
|
|
@ -75,6 +75,31 @@ public sealed class SpellComponentRequirementService
|
|||
/// (0x00567D50): an infused school or a directly carried school focus uses
|
||||
/// the scarab-only formula; otherwise the account-customized formula wins.
|
||||
/// </summary>
|
||||
public IReadOnlyList<uint> GetAppropriateFormula(uint spellId)
|
||||
{
|
||||
if (!_formulas.TryGetValue(spellId, out SpellFormulaDefinition? formula))
|
||||
return [];
|
||||
uint playerGuid = _playerGuid();
|
||||
return GetAppropriateFormula(
|
||||
formula,
|
||||
_objects.Get(playerGuid),
|
||||
playerGuid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the component tracker would leave a formula icon unghosted.
|
||||
/// The input is a retail spell-component id (SCID), not a weenie class id.
|
||||
/// </summary>
|
||||
public bool IsComponentOwned(uint spellComponentId)
|
||||
{
|
||||
if (!_wcidByScid.TryGetValue(spellComponentId, out uint weenieClassId))
|
||||
return false;
|
||||
uint playerGuid = _playerGuid();
|
||||
return _objects.Objects.Any(item =>
|
||||
item.WeenieClassId == weenieClassId
|
||||
&& IsOwnedByPlayer(item, playerGuid));
|
||||
}
|
||||
|
||||
private IReadOnlyList<uint> GetAppropriateFormula(
|
||||
SpellFormulaDefinition formula,
|
||||
ClientObject? player,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue