using System;
using System.Collections.Generic;
namespace AcDream.Core.Spells;
///
/// Pure port of retail's ordinary in-effect registry projection:
/// CEnchantmentRegistry::GetEnchantmentsInEffect @ 0x00594540,
/// GetEnchantmentsInEffectFromList @ 0x00594430,
/// Duel @ 0x005942B0, and Enchantment::Duel @ 0x005CACE0.
///
public static class EnchantmentRegistryProjection
{
///
/// Merges multiplicative then additive records and retains one winner per
/// spell category. Higher power wins; equal power is replaced only by a
/// later start time. Vitae and cooldown registries do not participate.
///
public static IReadOnlyList GetEnchantmentsInEffect(
IEnumerable enchantments)
{
ArgumentNullException.ThrowIfNull(enchantments);
ActiveEnchantmentRecord[] source = enchantments as ActiveEnchantmentRecord[]
?? [.. enchantments];
var result = new List();
DuelList(source, bucket: 1u, result);
DuelList(source, bucket: 2u, result);
return result;
}
private static void DuelList(
IReadOnlyList source,
uint bucket,
List result)
{
for (int sourceIndex = 0; sourceIndex < source.Count; sourceIndex++)
{
ActiveEnchantmentRecord candidate = source[sourceIndex];
if (candidate.Bucket != bucket) continue;
int incumbentIndex = result.FindIndex(existing =>
existing.SpellCategory == candidate.SpellCategory);
if (incumbentIndex >= 0)
{
ActiveEnchantmentRecord incumbent = result[incumbentIndex];
if (!CandidateWins(incumbent, candidate))
continue;
// Retail removes the losing node, then appends the winner.
result.RemoveAt(incumbentIndex);
}
result.Add(candidate);
}
}
private static bool CandidateWins(
ActiveEnchantmentRecord incumbent,
ActiveEnchantmentRecord candidate)
{
// Retail Enchantment::_power_level is signed int even though the wire
// field is read as four raw bytes.
int incumbentPower = unchecked((int)incumbent.PowerLevel);
int candidatePower = unchecked((int)candidate.PowerLevel);
return candidatePower > incumbentPower
|| (candidatePower == incumbentPower
&& candidate.StartTime > incumbent.StartTime);
}
}