/* * OutputState.cs * Authors: August Zinsser * * Copyright Matthew Belmonte 2007 */ using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace tAC_Engine { /// /// This class handles the recording of entities presented on the screen. It is essentially /// the counterpart to InputState. Any entity may be registered with this class, /// which automatically logs its appearance with nearly the accuracy specified by the maingame's /// target elapsed time. /// public static class OutputState { /// /// Used to keep track of which codes to log when entities are regeistered. If no screenPosition /// is specified, then OutputState uses the entity's position.X and position.Y as the screenPosition /// private class EntityCodePair { public Entity entity; public Logger.ExperimentalCode code; public Vector2? screenPosition; // If not null, use this value instead of the entity's position public bool livesInTheBackbuffer; // Must wait a draw cycle because the game is double-buffered public EntityCodePair(Entity entity, Logger.ExperimentalCode code) { this.entity = entity; this.code = code; this.screenPosition = null; this.livesInTheBackbuffer = true; } public EntityCodePair(Entity entity, Logger.ExperimentalCode code, Vector2 screenPosition) { this.entity = entity; this.code = code; this.screenPosition = screenPosition; this.livesInTheBackbuffer = true; } } private static List mDisplayList; // A list of entites that should be logged as soon as they are displayed private static int mLastScanLine; // Where the scan was last update private static int mCurrentScanLine; // Where the scan is this update /// /// Constructor /// static OutputState() { mDisplayList = new List(); } /// /// Register an entity, which will automatically log itself the first time it is displayed. /// Invisible entities will wait until they are visible to log themselves. Entity's Position /// property is assumed to be the coordinates in screen space (ignoring Position.Z). /// /// /// public static void Register(Entity entity, Logger.ExperimentalCode code) { mDisplayList.Add(new EntityCodePair(entity, code)); } /// /// Register an entity, which will automatically log itself the first time it is displayed. /// Invisible entities will wait until they are visible to log themselves. Entity's Position /// property is ignored, instead assuming that the specified screenPosition argument refers /// to the location at which the entity will be drawn. /// /// /// public static void Register(Entity entity, Logger.ExperimentalCode code, Vector2 screenPosition) { mDisplayList.Add(new EntityCodePair(entity, code, screenPosition)); } /// /// Returns a list of any associated codes that have not been logged yet for a given entity. /// /// /// public static List GetCodes(Entity entity) { List retVal = new List(); foreach (EntityCodePair ecp in mDisplayList) { if (ecp.entity == entity) { retVal.Add(ecp.code); } } return retVal; } /// /// Removes all yet unlogged instances for when the entity would be displayed /// /// public static void UnRegister(Entity entity) { for (int i = mDisplayList.Count; i >= 0; i--) if (mDisplayList[i].entity == entity) mDisplayList.Remove(mDisplayList[i]); } /// /// Removes all yet unlogged instances /// public static void UnRegisterAll() { mDisplayList = new List(); } /// /// Called at the rate specified by mainGame.targetElapsedTime, this function logs any registered /// entites that are >50% drawn to the screen. Once logged, that instance is removed and the entity /// will only cause another log entry if one is explicitly created. This function assumes /// that the display device draws from top to bottom. /// It also ignores any graphics that may be /// occluding registered entities. /// public static void Update(GraphicsDevice device) { // Find which scanline was just sent to the output device mLastScanLine = mCurrentScanLine; mCurrentScanLine = device.RasterStatus.ScanLine; // If a VBlank has passed, tag any waiting entities to be loggable if (mCurrentScanLine < mLastScanLine) { foreach (EntityCodePair ecp in mDisplayList) { if (ecp.entity.Visible) ecp.livesInTheBackbuffer = false; } //Logger.LogCode(Logger.ExperimentalCode.DEBUG_VBlank); } // See if any unregistered entitis have sat through the backbuffer and are now in the frontbuffer // If so, check if the scan has passed their origin List deathRow = new List(); foreach (EntityCodePair ecp in mDisplayList) { bool logTheCode = false; if (ecp.entity.Visible && !ecp.livesInTheBackbuffer) { // Use the screenPosition if it exists, otherwise use entity.Position if (ecp.screenPosition != null && mCurrentScanLine > ecp.screenPosition.Value.Y) logTheCode = true; else if (mCurrentScanLine > ecp.entity.Position.Y) logTheCode = true; } if (logTheCode) { // Log it and then remove the entity from the list Logger.LogCode(ecp.code); deathRow.Add(ecp); } } foreach (EntityCodePair ecp in deathRow) mDisplayList.Remove(ecp); } } }