/*
* 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);
}
}
}