merge: #267 vitae character-panel display (attributes vitae-immune per retail; skill dual parentheticals)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-30 18:14:48 +02:00
commit 2493f24c63
12 changed files with 984 additions and 108 deletions

View file

@ -24,6 +24,7 @@ public sealed class Spellbook
private readonly Dictionary<uint, ActiveEnchantmentRecord> _activeById = new();
private readonly Dictionary<uint, List<uint>> _enchantmentOrderByBucket = new();
private readonly Dictionary<uint, EnchantmentMath.VitalMod> _vitalModCache = new();
private readonly Dictionary<uint, EnchantmentMath.VitalMod> _attributeModCache = new();
private readonly Dictionary<uint, EnchantmentMath.VitalMod> _skillModCache = new();
private readonly List<uint>[] _favoriteSpells = Enumerable.Range(0, 8)
.Select(_ => new List<uint>()).ToArray();
@ -104,12 +105,40 @@ public sealed class Spellbook
}
/// <summary>
/// Campaign P Slice P1 (2026-07-30) — combined vitae + skill-enchantment
/// buff modifier for a Skill id (ACE Skill enum ordinal — Run=24,
/// Jump=22), matching retail <c>CEnchantmentRegistry::EnchantSkill</c>
/// (0x005947b0). Mirrors <see cref="GetVitalMod"/>'s caching shape;
/// consumed by <c>AcDream.Runtime.Gameplay.RuntimeCharacterState</c>'s
/// run/jump skill recompute.
/// Issue #267 — combined buff modifier for a primary attribute
/// (Strength/Endurance/Coordination/Quickness/Focus/Self; ACE
/// <c>PropertyAttribute</c> id 1-6). Retail
/// <c>CEnchantmentRegistry::EnchantAttribute</c> (0x00594570) never
/// references the vitae singleton, so this excludes vitae —
/// primary attributes are vitae-immune in retail. Feed the result
/// through <see cref="EnchantmentMath.EnchantAttribute"/> with the
/// base value to get the final displayed int.
/// </summary>
public EnchantmentMath.VitalMod GetAttributeMod(uint attributeId)
{
if (_attributeModCache.TryGetValue(
attributeId,
out EnchantmentMath.VitalMod cached))
{
return cached;
}
EnchantmentMath.VitalMod calculated = EnchantmentMath.GetMod(
ActiveEnchantments,
_table,
attributeId,
EnchantmentMath.EnchantmentTypeFlag.Attribute,
includeVitae: false);
_attributeModCache.Add(attributeId, calculated);
return calculated;
}
/// <summary>
/// Issue #267 — combined (vitae-inclusive) buff modifier for a skill
/// (ACE <c>SkillId</c>). Retail <c>CEnchantmentRegistry::EnchantSkill</c>
/// (0x005947b0) applies the vitae singleton before the mult/add buff
/// lists. Feed the result through <see cref="EnchantmentMath.EnchantSkill"/>
/// with the base skill level to get the final displayed int.
/// </summary>
public EnchantmentMath.VitalMod GetSkillMod(uint skillId)
{
@ -380,6 +409,7 @@ public sealed class Spellbook
_activeById.Clear();
_enchantmentOrderByBucket.Clear();
_vitalModCache.Clear();
_attributeModCache.Clear();
_skillModCache.Clear();
foreach (ActiveEnchantmentRecord enchantment in enchantments)
{
@ -469,6 +499,7 @@ public sealed class Spellbook
_activeById.Clear();
_enchantmentOrderByBucket.Clear();
_vitalModCache.Clear();
_attributeModCache.Clear();
_skillModCache.Clear();
foreach (List<uint> tab in _favoriteSpells) tab.Clear();
_desiredComponents.Clear();
@ -488,6 +519,7 @@ public sealed class Spellbook
private void NotifyEnchantmentsChanged()
{
_vitalModCache.Clear();
_attributeModCache.Clear();
_skillModCache.Clear();
EnchantmentsChanged?.Invoke();
StateChanged?.Invoke();