using System.Collections.Generic; using KernelPanic.Data; using System; namespace KernelPanic.Meta { /// /// Tracks player-owned persistent unlocks for menu presentation and collection views. /// public sealed class PlayerCollection { private readonly List _ownedUnits = new(); private readonly List _ownedPackages = new(); private readonly Dictionary _versions = new(StringComparer.OrdinalIgnoreCase); private int _featuredIndex; // TODO: Populate owned units from SaveService during bootstrap. public IReadOnlyList OwnedUnits => _ownedUnits; public IReadOnlyList OwnedPackageInstances => _ownedPackages; public IReadOnlyList OwnedPackages => BuildOwnedPackageDefinitions(); public DistroDefinition FeaturedUnit => _ownedUnits.Count == 0 ? null : _ownedUnits[Math.Clamp(_featuredIndex, 0, _ownedUnits.Count - 1)]; public event Action Changed; public void Add(DistroDefinition unit) { Add(unit, 1); } public void Add(DistroDefinition unit, int version) { Add(unit, version, true); } public void AddSilently(DistroDefinition unit, int version) { Add(unit, version, false); } public void AddPackage(PackageDefinition package) { AddPackage(package, 0, true); } public void AddPackageSilently(PackageDefinition package) { AddPackage(package, 0, false); } public void AddPackageSilently(PackageDefinition package, int upgradeLevel) { AddPackage(package, upgradeLevel, false); } private void AddPackage(PackageDefinition package, int upgradeLevel, bool notify) { if (package == null || IsPackageOwned(package.Id)) { return; } _ownedPackages.Add(new OwnedPackageInstance(package, upgradeLevel)); if (notify) { Changed?.Invoke(); } } private void Add(DistroDefinition unit, int version, bool notify) { if (unit == null) { return; } if (IsOwned(unit.Id)) { SetVersion(unit.Id, version, notify); return; } // Persistence is wired by the owner so this domain class stays IO-free. _ownedUnits.Add(unit); _versions[unit.Id] = Math.Max(1, version); _featuredIndex = Math.Clamp(_featuredIndex, 0, _ownedUnits.Count - 1); if (notify) { Changed?.Invoke(); } } public bool IsOwned(string unitId) { return GetOwnedCount(unitId) > 0; } public int GetOwnedCount(string unitId) { if (string.IsNullOrWhiteSpace(unitId)) { return 0; } int count = 0; for (int i = 0; i < _ownedUnits.Count; i++) { DistroDefinition unit = _ownedUnits[i]; if (unit != null && string.Equals(unit.Id, unitId, StringComparison.OrdinalIgnoreCase)) { count++; } } return count; } public bool IsPackageOwned(string packageId) { if (string.IsNullOrWhiteSpace(packageId)) { return false; } for (int i = 0; i < _ownedPackages.Count; i++) { OwnedPackageInstance package = _ownedPackages[i]; if (package != null && string.Equals(package.PackageId, packageId, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } public OwnedPackageInstance GetOwnedPackage(string packageId) { if (string.IsNullOrWhiteSpace(packageId)) { return null; } for (int i = 0; i < _ownedPackages.Count; i++) { OwnedPackageInstance package = _ownedPackages[i]; if (package != null && string.Equals(package.PackageId, packageId, StringComparison.OrdinalIgnoreCase)) { return package; } } return null; } public bool RemovePackage(string packageId) { for (int i = _ownedPackages.Count - 1; i >= 0; i--) { OwnedPackageInstance package = _ownedPackages[i]; if (package != null && string.Equals(package.PackageId, packageId, StringComparison.OrdinalIgnoreCase)) { _ownedPackages.RemoveAt(i); Changed?.Invoke(); return true; } } return false; } public int GetVersion(string unitId) { if (string.IsNullOrWhiteSpace(unitId)) { return 0; } return _versions.TryGetValue(unitId, out int version) ? Math.Max(1, version) : IsOwned(unitId) ? 1 : 0; } public void SetVersion(string unitId, int version) { SetVersion(unitId, version, true); } public void SetVersionSilently(string unitId, int version) { SetVersion(unitId, version, false); } private void SetVersion(string unitId, int version, bool notify) { if (string.IsNullOrWhiteSpace(unitId) || !IsOwned(unitId)) { return; } int safeVersion = Math.Max(1, version); if (_versions.TryGetValue(unitId, out int oldVersion) && oldVersion == safeVersion) { return; } _versions[unitId] = safeVersion; if (notify) { Changed?.Invoke(); } } public void SelectNextFeatured() { if (_ownedUnits.Count < 2) { return; } _featuredIndex = (_featuredIndex + 1) % _ownedUnits.Count; } private IReadOnlyList BuildOwnedPackageDefinitions() { List definitions = new(); for (int i = 0; i < _ownedPackages.Count; i++) { if (_ownedPackages[i]?.Definition != null) { definitions.Add(_ownedPackages[i].Definition); } } return definitions; } } public sealed class OwnedPackageInstance { public OwnedPackageInstance(PackageDefinition definition, int upgradeLevel) { Definition = definition; UpgradeLevel = Math.Max(0, Math.Min(PackageTuning.MaxPackageLevel, upgradeLevel)); } public PackageDefinition Definition { get; } public string PackageId => Definition == null ? string.Empty : Definition.Id; public int UpgradeLevel { get; private set; } public PackageInstance ToRuntimeInstance() { return new PackageInstance(Definition, UpgradeLevel); } public void SetUpgradeLevel(int level) { UpgradeLevel = Math.Max(0, Math.Min(PackageTuning.MaxPackageLevel, level)); } } }