// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; namespace osu.Game.Screens.Edit.Compose { /// /// A queue which processes events from the many s in a nested hierarchy. /// internal class HitObjectContainerEventQueue : Component { /// /// Invoked when a becomes used by a . /// /// /// If the ruleset uses pooled objects, this represents the time when the s become alive. /// public event Action HitObjectUsageBegan; /// /// Invoked when a becomes unused by a . /// /// /// If the ruleset uses pooled objects, this represents the time when the s become dead. /// public event Action HitObjectUsageFinished; /// /// Invoked when a has been transferred to another . /// public event Action HitObjectUsageTransferred; private readonly Playfield playfield; /// /// Creates a new . /// /// The most top-level . public HitObjectContainerEventQueue([NotNull] Playfield playfield) { this.playfield = playfield; playfield.HitObjectUsageBegan += onHitObjectUsageBegan; playfield.HitObjectUsageFinished += onHitObjectUsageFinished; } private readonly Dictionary pendingEvents = new Dictionary(); private void onHitObjectUsageBegan(HitObject hitObject) => updateEvent(hitObject, EventType.Began); private void onHitObjectUsageFinished(HitObject hitObject) => updateEvent(hitObject, EventType.Finished); private void updateEvent(HitObject hitObject, EventType newEvent) { if (!pendingEvents.TryGetValue(hitObject, out EventType existingEvent)) { pendingEvents[hitObject] = newEvent; return; } switch (existingEvent, newEvent) { case (EventType.Transferred, EventType.Finished): pendingEvents[hitObject] = EventType.Finished; break; case (EventType.Began, EventType.Finished): case (EventType.Finished, EventType.Began): pendingEvents[hitObject] = EventType.Transferred; break; default: throw new ArgumentOutOfRangeException($"Unexpected event update ({existingEvent} => {newEvent})."); } } protected override void Update() { base.Update(); foreach (var (hitObject, e) in pendingEvents) { switch (e) { case EventType.Began: HitObjectUsageBegan?.Invoke(hitObject); break; case EventType.Transferred: HitObjectUsageTransferred?.Invoke(hitObject, playfield.AllHitObjects.Single(d => d.HitObject == hitObject)); break; case EventType.Finished: HitObjectUsageFinished?.Invoke(hitObject); break; } } pendingEvents.Clear(); } protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); playfield.HitObjectUsageBegan -= onHitObjectUsageBegan; playfield.HitObjectUsageFinished -= onHitObjectUsageFinished; } private enum EventType { Began, Finished, Transferred } } }