using System; using System.Collections.Generic; using KernelPanic.Data; namespace KernelPanic.Meta { public enum PackageLoadoutFailureReason { None, WrongSlot, NotOwned, SlotOccupied, NotEquipped } /// /// Pure meta model for persistent package slots. Unlike CardLoadout, this is not /// run-only: equipped package ids are saved per distro and reused in every run. /// public sealed class PackageLoadout { private readonly PlayerCollection _collection; private readonly PackageDatabase _database; private readonly Dictionary> _equippedByDistro = new(StringComparer.OrdinalIgnoreCase); public PackageLoadout(PlayerCollection collection, PackageDatabase database) { _collection = collection; _database = database; } public IReadOnlyDictionary GetEquippedPackageIds(string distroId) { return !string.IsNullOrWhiteSpace(distroId) && _equippedByDistro.TryGetValue(distroId, out Dictionary equipped) ? equipped : Empty; } public PackageDefinition GetEquippedPackage(string distroId, PackageSlot slot) { if (string.IsNullOrWhiteSpace(distroId) || !_equippedByDistro.TryGetValue(distroId, out Dictionary equipped) || !equipped.TryGetValue(slot, out string packageId)) { return null; } return _database == null ? null : _database.FindById(packageId); } public IReadOnlyList GetEquippedPackages(string distroId) { List packages = new(); AddIfEquipped(distroId, PackageSlot.Kernel, packages); AddIfEquipped(distroId, PackageSlot.Runtime, packages); AddIfEquipped(distroId, PackageSlot.Daemon, packages); return packages; } public IReadOnlyList GetEquippedPackageInstances(string distroId) { List packages = new(); AddInstanceIfEquipped(distroId, PackageSlot.Kernel, packages); AddInstanceIfEquipped(distroId, PackageSlot.Runtime, packages); AddInstanceIfEquipped(distroId, PackageSlot.Daemon, packages); return packages; } public bool IsEquipped(string packageId) { if (string.IsNullOrWhiteSpace(packageId)) { return false; } foreach (KeyValuePair> distro in _equippedByDistro) { foreach (KeyValuePair slot in distro.Value) { if (string.Equals(slot.Value, packageId, StringComparison.OrdinalIgnoreCase)) { return true; } } } return false; } public bool TryEquip(string distroId, PackageSlot slot, string packageId, out PackageLoadoutFailureReason reason) { PackageDefinition package = _database == null ? null : _database.FindById(packageId); if (package == null || _collection == null || !_collection.IsPackageOwned(package.Id)) { reason = PackageLoadoutFailureReason.NotOwned; return false; } if (package.Slot != slot) { reason = PackageLoadoutFailureReason.WrongSlot; return false; } Dictionary equipped = EnsureDistro(distroId); if (equipped.TryGetValue(slot, out string current) && !string.Equals(current, package.Id, StringComparison.OrdinalIgnoreCase)) { reason = PackageLoadoutFailureReason.SlotOccupied; return false; } equipped[slot] = package.Id; reason = PackageLoadoutFailureReason.None; return true; } public bool TryUnequip(string distroId, PackageSlot slot, out PackageLoadoutFailureReason reason) { if (string.IsNullOrWhiteSpace(distroId) || !_equippedByDistro.TryGetValue(distroId, out Dictionary equipped) || !equipped.Remove(slot)) { reason = PackageLoadoutFailureReason.NotEquipped; return false; } reason = PackageLoadoutFailureReason.None; return true; } public void Load(string distroId, IEnumerable slots) { if (string.IsNullOrWhiteSpace(distroId)) { return; } Dictionary equipped = EnsureDistro(distroId); equipped.Clear(); foreach (PackageLoadoutSlotSaveEntry slotEntry in slots ?? Array.Empty()) { PackageDefinition package = _database == null ? null : _database.FindById(slotEntry?.packageId); if (package == null || !_collection.IsPackageOwned(package.Id) || package.Slot != slotEntry.slot) { continue; } equipped[package.Slot] = package.Id; } } public void Clear() { _equippedByDistro.Clear(); } private void AddIfEquipped(string distroId, PackageSlot slot, List packages) { PackageDefinition package = GetEquippedPackage(distroId, slot); if (package != null) { packages.Add(package); } } private void AddInstanceIfEquipped(string distroId, PackageSlot slot, List packages) { PackageDefinition definition = GetEquippedPackage(distroId, slot); OwnedPackageInstance owned = definition == null ? null : _collection.GetOwnedPackage(definition.Id); if (owned != null) { packages.Add(owned.ToRuntimeInstance()); } } private Dictionary EnsureDistro(string distroId) { string key = string.IsNullOrWhiteSpace(distroId) ? string.Empty : distroId; if (!_equippedByDistro.TryGetValue(key, out Dictionary equipped)) { equipped = new Dictionary(); _equippedByDistro[key] = equipped; } return equipped; } private static readonly IReadOnlyDictionary Empty = new Dictionary(); } }