The MatchEvents class provides an event dispatcher for match state changes. It uses public events to notify subscribers of card purchases, resource changes, and phase transitions.
using System;
using System.Collections.Generic;
namespace CitySim.Match;
public enum MatchPhase { Lobby, RoundStart, Bid, Build, Payout, EndGame }
public sealed record PhaseChange( MatchPhase Phase, int Round, double EndsAt );
public sealed record ResourceChange( Guid Player, int Previous, int Current );
public sealed record PlacementResult( bool Success, string Reason, BoardCell Cell = null );
public sealed record CardEventResult(Guid Player,string CardId,int Coins = 0,TileClaim Claim = null,int Vp = 0);
public sealed record BoardReplacement(BoardCell Before,BoardCell After);
public sealed record CardPurchase(Guid Player,string CardId,int Price,int MarketId,int Lot);
public sealed record HandCardChange(Guid Player,string CardId);
/// <summary>Per-match notifications. Subscribers detach when their view is destroyed or rebound.</summary>
public sealed class MatchEvents
{
public event Action<CardPurchase> CardPurchased;
public event Action<HandCardChange> CardReceived;
public event Action<HandCardChange> CardDiscarded;
internal void Purchased(CardPurchase value) => CardPurchased?.Invoke(value);
internal void Received(HandCardChange value) => CardReceived?.Invoke(value);
internal void Discarded(HandCardChange value) => CardDiscarded?.Invoke(value);
public event Action<PhaseChange> PhaseChanged;
public event Action<ResourceChange> CoinsChanged;
public event Action<ResourceChange> VpChanged;
public event Action<Guid> HandChanged;
public event Action<BoardCell> CardPlaced;
public event Action<BoardReplacement> BuildingReplaced;
public event Action<PlacementResult> PlacementRejected;
public event Action<PayoutResult> PayoutResolved;
public event Action<Guid> BidChanged;
public event Action<Guid> BidStatusChanged;
public event Action<Guid> PlayerReadyChanged;
public event Action<int> LotsRevealed;
public event Action<AuctionResult> AuctionResolved;
public event Action<PlacementEffects> PlacementResolved;
public event Action<ParkScoreResult> ParkScored;
public event Action<GrowthResult> BuildingGrown;
public event Action<BoardCell> CellUpdated;
public event Action<Guid> EffectTrayChanged;
public event Action ClaimsChanged;
public event Action<EndScore> MatchScored;
public event Action<CardEventResult> EventPlayed;
public event Action PublicGoalsChanged;
public event Action<GoalsResult> GoalsClaimed;
public event Action<HospitalEmergencyResult> HospitalEmergency;
public event Action<Guid> ContractChanged;
public event Action<IReadOnlyList<ContractReveal>> ContractsRevealed;
internal void Phase( PhaseChange change ) => PhaseChanged?.Invoke( change );
internal void Coins( ResourceChange change ) => CoinsChanged?.Invoke( change );
internal void Vp( ResourceChange change ) => VpChanged?.Invoke( change );
internal void Hand( Guid player ) => HandChanged?.Invoke( player );
internal void Placed( BoardCell cell ) => CardPlaced?.Invoke( cell );
internal void Replaced(BoardReplacement change) => BuildingReplaced?.Invoke(change);
internal void Rejected( PlacementResult result ) => PlacementRejected?.Invoke( result );
internal void Payout( PayoutResult result ) => PayoutResolved?.Invoke( result );
internal void Bid( Guid owner ) => BidChanged?.Invoke(owner);
internal void BidStatus( Guid owner ) => BidStatusChanged?.Invoke(owner);
internal void Ready( Guid owner ) => PlayerReadyChanged?.Invoke(owner);
internal void Reveal( int id ) => LotsRevealed?.Invoke(id);
internal void Auction( AuctionResult result ) => AuctionResolved?.Invoke(result);
internal void Placement(PlacementEffects effects) => PlacementResolved?.Invoke(effects);
internal void Park(ParkScoreResult result) => ParkScored?.Invoke(result);
internal void Growth(GrowthResult result) => BuildingGrown?.Invoke(result);
internal void Updated(BoardCell cell) => CellUpdated?.Invoke(cell);
internal void Tray(Guid owner) => EffectTrayChanged?.Invoke(owner);
internal void Claims() => ClaimsChanged?.Invoke();
internal void Scored(EndScore result) => MatchScored?.Invoke(result);
internal void Played(CardEventResult result) => EventPlayed?.Invoke(result);
internal void GoalsChanged() => PublicGoalsChanged?.Invoke();
internal void GoalClaims(GoalsResult result) => GoalsClaimed?.Invoke(result);
internal void Emergency(HospitalEmergencyResult result) => HospitalEmergency?.Invoke(result);
internal void Contract(Guid owner) => ContractChanged?.Invoke(owner);
internal void RevealContracts(IReadOnlyList<ContractReveal> revealed) => ContractsRevealed?.Invoke(revealed);
}