namespace ActionBlocks.Game.Items; /// All items that exist in the game. Register new items here. public sealed class ItemRegistry { private readonly Dictionary _items; public ItemRegistry(IEnumerable items) { _items = items.ToDictionary(i => i.Id); } public static ItemRegistry Default { get; } = new( [ new AddGarbageLineItem(), new ClearBottomLineItem(), new ClearBoardItem(), new SwapBoardItem(), new InvisibilityItem(), new TransformationItem(), ]); public IReadOnlyCollection All => _items.Values; public IItem? Get(string id) => _items.GetValueOrDefault(id); public IItem PickRandom(Random random) { var total = _items.Values.Sum(i => i.SpawnWeight); var roll = random.Next(total); foreach (var item in _items.Values) { roll -= item.SpawnWeight; if (roll < 0) { return item; } } return _items.Values.First(); } } /// Decides which minos of a new piece carry an item. Each mino rolls chance (0..1) on its own. public sealed class ItemSpawner(ItemRegistry registry, double chance, Random random) { private readonly double _chance = double.IsNaN(chance) ? 0 : Math.Clamp(chance, 0, 1); public Core.PieceSpec Decorate(Core.PieceType type) { if (registry.All.Count == 0) { return new Core.PieceSpec(type); } var items = new string?[Core.PieceDefinitions.Minos(type, 0).Count]; var any = false; for (var i = 0; i < items.Length; i++) { if (random.NextDouble() < _chance) { items[i] = registry.PickRandom(random).Id; any = true; } } return any ? new Core.PieceSpec(type, items) : new Core.PieceSpec(type); } }