using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace Theriapolis.Game.Screens; /// /// Manages a stack of IScreen instances. /// Only the top screen receives Update and Draw calls. /// Push/Pop are deferred to the start of the next frame to avoid mid-loop mutation. /// public sealed class ScreenManager { private readonly Game1 _game; private readonly Stack _stack = new(); private IScreen? _pendingPush; private int _pendingPops; // count rather than bool — multiple Pops queued in one frame all apply public ScreenManager(Game1 game) { _game = game; } public IScreen? Current => _stack.Count > 0 ? _stack.Peek() : null; public void Push(IScreen screen) { _pendingPush = screen; } public void Pop() { _pendingPops++; } /// Process any pending push/pop, then update the current screen. public void Update(GameTime gameTime) { // Apply deferred transitions — drain all pending pops before any push. bool popped = false; while (_pendingPops > 0 && _stack.Count > 0) { var top = _stack.Pop(); top.Deactivate(); _pendingPops--; popped = true; } _pendingPops = 0; // Only call Reactivate once after a pop chain — not every frame. if (popped && _stack.TryPeek(out var back)) back.Reactivate(); if (_pendingPush is not null) { _stack.TryPeek(out var cur); cur?.Deactivate(); _pendingPush.Initialize(_game); _stack.Push(_pendingPush); _pendingPush = null; } Current?.Update(gameTime); } public void Draw(GameTime gameTime, SpriteBatch spriteBatch) { Current?.Draw(gameTime, spriteBatch); } }