Ui/EffectTray.razor

The UI component displays active card effects and tags for the owning player. It subscribes to board events to keep the icons and tags list updated.

@namespace CitySim
@using CitySim.DesignSystem
@using CitySim.Punchboard
@using CitySim.Cards
@using CitySim.Match
@inherits MatchPanel

<root class="effect-tray">
@if (icons.Count > 0 || tags != Tag.None) {
<div class="effect-icons">@foreach (var icon in icons) { <PbSlot CssClass="effect-icon" Icon=@(CardPresentation.Artwork(icon.Card)) [email protected] TooltipText=@(GameText.Plain($"{icon.Card.Name} ×{icon.Count}\n{icon.Card.Rules}")) /> }</div>

<div class="tag-row">@foreach (var tag in TagChip.Values(tags)) { <TagChip City=@City Tag=@tag Owner=@owner /> }</div>
}
</root>

@code {
	sealed record IconCount(CardDef Card,int Count);
	List<IconCount> icons = new();
	Guid owner => CityGame.Me;
	Tag tags;
	void Read()
	{
		icons = BoundMatch?.Cells().Where(c => c.Owner == owner && CardLibrary.Get(c.CardId).EffectTray).GroupBy(c => c.CardId).Select(g => new IconCount(CardLibrary.Get(g.Key),g.Count())).ToList() ?? new();
		tags = BoundMatch?.Cells().Where(c => c.Owner == owner).Aggregate(Tag.None,(all,c) => all | CardLibrary.Get(c.CardId).Tags) ?? Tag.None; Refresh();
	}
	protected override void Subscribe() { if (City != null) City.UiChanged += Read; if (BoundMatch != null) { BoundMatch.Events.EffectTrayChanged += Changed; BoundMatch.Events.CardPlaced += Placed; BoundMatch.Events.CellUpdated += Placed; } Read(); }
	protected override void Unsubscribe() { if (City != null) City.UiChanged -= Read; if (BoundMatch != null) { BoundMatch.Events.EffectTrayChanged -= Changed; BoundMatch.Events.CardPlaced -= Placed; BoundMatch.Events.CellUpdated -= Placed; } }
	void Changed(Guid id) { if (id == owner) Read(); }
	void Placed(BoardCell cell) { if (cell.Owner == owner) Read(); }
}