add port of game state and process management code
This commit is contained in:
parent
018e19b25f
commit
eddd69f091
|
@ -160,6 +160,12 @@
|
|||
<Compile Include="IService.cs" />
|
||||
<Compile Include="ServiceContainer.cs" />
|
||||
<Compile Include="Support\DictionaryExtensions.cs" />
|
||||
<Compile Include="States\GameState.cs" />
|
||||
<Compile Include="States\StateManager.cs" />
|
||||
<Compile Include="States\StateInfo.cs" />
|
||||
<Compile Include="Processes\GameProcess.cs" />
|
||||
<Compile Include="Processes\ProcessManager.cs" />
|
||||
<Compile Include="Processes\ProcessInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
|
@ -178,6 +184,8 @@
|
|||
<Folder Include="Entities\" />
|
||||
<Folder Include="Entities\SystemComponents\" />
|
||||
<Folder Include="Graphics\ScreenEffects\" />
|
||||
<Folder Include="States\" />
|
||||
<Folder Include="Processes\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\Fonts\Vera.ttf" />
|
||||
|
|
108
Blarg.GameFramework/Processes/GameProcess.cs
Normal file
108
Blarg.GameFramework/Processes/GameProcess.cs
Normal file
|
@ -0,0 +1,108 @@
|
|||
using System;
|
||||
using Blarg.GameFramework.Events;
|
||||
using Blarg.GameFramework.States;
|
||||
|
||||
namespace Blarg.GameFramework.Processes
|
||||
{
|
||||
public class GameProcess : EventListener, IDisposable
|
||||
{
|
||||
public readonly IGameApp GameApp;
|
||||
public readonly GameState GameState;
|
||||
public readonly ProcessManager ProcessManager;
|
||||
|
||||
public bool IsFinished { get; private set; }
|
||||
|
||||
public bool IsTransitioning
|
||||
{
|
||||
get { return ProcessManager.IsProcessTransitioning(this); }
|
||||
}
|
||||
|
||||
public GameProcess(ProcessManager processManager, EventManager eventManager)
|
||||
: base(eventManager)
|
||||
{
|
||||
if (processManager == null)
|
||||
throw new ArgumentNullException("processManager");
|
||||
if (eventManager == null)
|
||||
throw new ArgumentNullException("eventManager");
|
||||
|
||||
GameState = processManager.GameState;
|
||||
ProcessManager = processManager;
|
||||
GameApp = GameState.GameApp;
|
||||
}
|
||||
|
||||
protected void SetFinished()
|
||||
{
|
||||
IsFinished = true;
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
public virtual void OnAdd()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnRemove()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnPause(bool dueToOverlay)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnResume(bool fromOverlay)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnAppGainFocus()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnAppLostFocus()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnAppPause()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnAppResume()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnLostContext()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnNewContext()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnRender(float delta)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnResize()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnUpdate(float delta)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual bool OnTransition(float delta, bool isTransitioningOut, bool started)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Handle(Event e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
32
Blarg.GameFramework/Processes/ProcessInfo.cs
Normal file
32
Blarg.GameFramework/Processes/ProcessInfo.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
|
||||
namespace Blarg.GameFramework.Processes
|
||||
{
|
||||
internal class ProcessInfo
|
||||
{
|
||||
public readonly GameProcess Process;
|
||||
public readonly string Name;
|
||||
public readonly string Descriptor;
|
||||
|
||||
public bool IsTransitioning;
|
||||
public bool IsTransitioningOut;
|
||||
public bool IsTransitionStarting;
|
||||
public bool IsInactive;
|
||||
public bool IsBeingRemoved;
|
||||
|
||||
public ProcessInfo(GameProcess process, string name = null)
|
||||
{
|
||||
if (process == null)
|
||||
throw new ArgumentNullException("process");
|
||||
|
||||
Process = process;
|
||||
Name = name;
|
||||
IsInactive = true;
|
||||
|
||||
if (String.IsNullOrEmpty(Name))
|
||||
Descriptor = process.GetType().Name;
|
||||
else
|
||||
Descriptor = String.Format("{0}[{1}]", process.GetType().Name, Name);
|
||||
}
|
||||
}
|
||||
}
|
477
Blarg.GameFramework/Processes/ProcessManager.cs
Normal file
477
Blarg.GameFramework/Processes/ProcessManager.cs
Normal file
|
@ -0,0 +1,477 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Blarg.GameFramework.States;
|
||||
|
||||
namespace Blarg.GameFramework.Processes
|
||||
{
|
||||
public class ProcessManager : IDisposable
|
||||
{
|
||||
const string LOG_TAG = "PROCESSES";
|
||||
|
||||
LinkedList<ProcessInfo> _processes;
|
||||
Queue<ProcessInfo> _queue;
|
||||
|
||||
public readonly GameState GameState;
|
||||
|
||||
public ProcessManager(GameState state)
|
||||
{
|
||||
if (state == null)
|
||||
throw new ArgumentNullException("state");
|
||||
|
||||
GameState = state;
|
||||
_processes = new LinkedList<ProcessInfo>();
|
||||
_queue = new Queue<ProcessInfo>();
|
||||
}
|
||||
|
||||
#region Complex Properties
|
||||
|
||||
public bool IsTransitioning
|
||||
{
|
||||
get
|
||||
{
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
if (node.Value.IsTransitioning)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_processes.Count == 0 && _queue.Count == 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
public void OnPause(bool dueToOverlay)
|
||||
{
|
||||
if (_processes.Count == 0)
|
||||
return;
|
||||
|
||||
if (dueToOverlay)
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Pausing all active processes due to state being overlayed on to the parent state.");
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (!processInfo.IsInactive)
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Pausing process {0} due to parent state overlay.", processInfo.Descriptor);
|
||||
processInfo.Process.OnPause(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Transitioning out all active processes pending pause.");
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (!processInfo.IsInactive)
|
||||
StartTransitionOut(processInfo, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnResume(bool fromOverlay)
|
||||
{
|
||||
if (_processes.Count == 0)
|
||||
return;
|
||||
|
||||
if (fromOverlay)
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Resuming all active processes due to overlay state being removed from overtop of parent state.");
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (!processInfo.IsInactive)
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Resuming process {0} due to overlay state removal.", processInfo.Descriptor);
|
||||
processInfo.Process.OnResume(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Resuming processes.");
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (processInfo.IsInactive && !processInfo.IsBeingRemoved)
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Resuming process {0}", processInfo.Descriptor);
|
||||
processInfo.Process.OnResume(false);
|
||||
|
||||
StartTransitionIn(processInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAppGainFocus()
|
||||
{
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (!processInfo.IsInactive)
|
||||
processInfo.Process.OnAppGainFocus();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAppLostFocus()
|
||||
{
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (!processInfo.IsInactive)
|
||||
processInfo.Process.OnAppLostFocus();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAppPause()
|
||||
{
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (!processInfo.IsInactive)
|
||||
processInfo.Process.OnAppPause();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAppResume()
|
||||
{
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (!processInfo.IsInactive)
|
||||
processInfo.Process.OnAppResume();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnLostContext()
|
||||
{
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (!processInfo.IsInactive)
|
||||
processInfo.Process.OnLostContext();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnNewContext()
|
||||
{
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (!processInfo.IsInactive)
|
||||
processInfo.Process.OnNewContext();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnRender(float delta)
|
||||
{
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (!processInfo.IsInactive)
|
||||
processInfo.Process.OnRender(delta);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnResize()
|
||||
{
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (!processInfo.IsInactive)
|
||||
processInfo.Process.OnResize();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnUpdate(float delta)
|
||||
{
|
||||
CleanupInactiveProcesses();
|
||||
CheckForFinishedProcesses();
|
||||
ProcessQueue();
|
||||
UpdateTransitions(delta);
|
||||
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (!processInfo.IsInactive)
|
||||
processInfo.Process.OnUpdate(delta);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Add / Remove
|
||||
|
||||
public T Add<T>(string name = null) where T : GameProcess
|
||||
{
|
||||
var newProcess = (T)Activator.CreateInstance(typeof(T), this);
|
||||
var processInfo = new ProcessInfo(newProcess, name);
|
||||
Queue(processInfo);
|
||||
return newProcess;
|
||||
}
|
||||
|
||||
public void Remove(string name)
|
||||
{
|
||||
var node = GetNodeFor(name);
|
||||
if (node == null)
|
||||
throw new Exception("No process with given name.");
|
||||
StartTransitionOut(node.Value, true);
|
||||
}
|
||||
|
||||
public void RemoveFirstOf<T>() where T : GameProcess
|
||||
{
|
||||
var node = GetNodeForFirst<T>();
|
||||
if (node == null)
|
||||
throw new Exception("No process of given type.");
|
||||
StartTransitionOut(node.Value, true);
|
||||
}
|
||||
|
||||
public void RemoveAll()
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Transitioning out all processes pending removal.");
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (!processInfo.IsTransitioning && !processInfo.IsInactive)
|
||||
StartTransitionOut(processInfo, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void Queue(ProcessInfo newProcessInfo)
|
||||
{
|
||||
if (newProcessInfo == null)
|
||||
throw new ArgumentNullException("newProcessInfo");
|
||||
if (newProcessInfo.Process == null)
|
||||
throw new ArgumentException("No GameProcess provided.");
|
||||
|
||||
Platform.Logger.Info(LOG_TAG, "Queueing process {0}.", newProcessInfo.Descriptor);
|
||||
_queue.Enqueue(newProcessInfo);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Process Management
|
||||
|
||||
private void StartTransitionIn(ProcessInfo processInfo)
|
||||
{
|
||||
if (processInfo == null)
|
||||
throw new ArgumentNullException("processInfo");
|
||||
if (!processInfo.IsInactive || processInfo.IsTransitioning)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
processInfo.IsInactive = false;
|
||||
processInfo.IsTransitioning = true;
|
||||
processInfo.IsTransitioningOut = false;
|
||||
processInfo.IsTransitionStarting = true;
|
||||
Platform.Logger.Info(LOG_TAG, "Transition into process {0} started.", processInfo.Descriptor);
|
||||
}
|
||||
|
||||
private void StartTransitionOut(ProcessInfo processInfo, bool forRemoval)
|
||||
{
|
||||
if (processInfo == null)
|
||||
throw new ArgumentNullException("processInfo");
|
||||
if (processInfo.IsInactive || processInfo.IsTransitioning)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
processInfo.IsTransitioning = true;
|
||||
processInfo.IsTransitioningOut = true;
|
||||
processInfo.IsTransitionStarting = true;
|
||||
processInfo.IsBeingRemoved = forRemoval;
|
||||
Platform.Logger.Info(LOG_TAG, "Transition out of process {0} started pending {1}.", processInfo.Descriptor, (forRemoval ? "removal" : "pause"));
|
||||
}
|
||||
|
||||
private void CleanupInactiveProcesses()
|
||||
{
|
||||
var node = _processes.First;
|
||||
while (node != null)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (processInfo.IsInactive && processInfo.IsBeingRemoved)
|
||||
{
|
||||
var next = node.Next;
|
||||
_processes.Remove(node);
|
||||
node = next;
|
||||
|
||||
Platform.Logger.Info(LOG_TAG, "Deleting inactive process {0}.", processInfo.Descriptor);
|
||||
processInfo.Process.Dispose();
|
||||
processInfo = null;
|
||||
}
|
||||
else
|
||||
node = node.Next;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckForFinishedProcesses()
|
||||
{
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (!processInfo.IsInactive && processInfo.Process.IsFinished && !processInfo.IsTransitioning)
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Process {0} marked as finished.", processInfo.Descriptor);
|
||||
StartTransitionOut(processInfo, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessQueue()
|
||||
{
|
||||
while (_queue.Count > 0)
|
||||
{
|
||||
var processInfo = _queue.Dequeue();
|
||||
|
||||
Platform.Logger.Info(LOG_TAG, "Adding process {0} from queue.", processInfo.Descriptor);
|
||||
_processes.AddLast(processInfo);
|
||||
processInfo.Process.OnAdd();
|
||||
|
||||
StartTransitionIn(processInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTransitions(float delta)
|
||||
{
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
var processInfo = node.Value;
|
||||
if (processInfo.IsTransitioning)
|
||||
{
|
||||
bool isDone = processInfo.Process.OnTransition(delta, processInfo.IsTransitioningOut, processInfo.IsTransitionStarting);
|
||||
if (isDone)
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Transition {0} into process {1} finished.",
|
||||
(processInfo.IsTransitioningOut ? "out of" : "into"),
|
||||
processInfo.Descriptor);
|
||||
|
||||
// if the process was being transitioned out, then we should mark
|
||||
// it as inactive, and trigger it's OnRemove event now
|
||||
if (processInfo.IsTransitioningOut)
|
||||
{
|
||||
if (processInfo.IsBeingRemoved)
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Removing process {0}.", processInfo.Descriptor);
|
||||
processInfo.Process.OnRemove();
|
||||
}
|
||||
else
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Pausing process {0}.", processInfo.Descriptor);
|
||||
processInfo.Process.OnPause(false);
|
||||
}
|
||||
processInfo.IsInactive = true;
|
||||
}
|
||||
|
||||
// done transitioning
|
||||
processInfo.IsTransitioning = false;
|
||||
processInfo.IsTransitioningOut = false;
|
||||
}
|
||||
processInfo.IsTransitionStarting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Misc
|
||||
|
||||
public bool IsProcessTransitioning(GameProcess process)
|
||||
{
|
||||
var processInfo = GetProcessInfoFor(process);
|
||||
if (processInfo == null)
|
||||
return false;
|
||||
else
|
||||
return processInfo.IsTransitioning;
|
||||
}
|
||||
|
||||
public bool HasProcess(string name)
|
||||
{
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(node.Value.Name) && node.Value.Name == name)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private LinkedListNode<ProcessInfo> GetNodeFor(string processName)
|
||||
{
|
||||
if (String.IsNullOrEmpty(processName))
|
||||
throw new ArgumentNullException("processName");
|
||||
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
if (node.Value.Name == processName)
|
||||
return node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private LinkedListNode<ProcessInfo> GetNodeForFirst<T>() where T : GameProcess
|
||||
{
|
||||
var type = typeof(T);
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
if (node.Value.Process.GetType() == type)
|
||||
return node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ProcessInfo GetProcessInfoFor(GameProcess process)
|
||||
{
|
||||
if (process == null)
|
||||
throw new ArgumentNullException("process");
|
||||
|
||||
for (var node = _processes.First; node != null; node = node.Next)
|
||||
{
|
||||
if (node.Value.Process == process)
|
||||
return node.Value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_processes == null)
|
||||
return;
|
||||
|
||||
Platform.Logger.Info(LOG_TAG, "ProcessManager disposing.");
|
||||
|
||||
while (_processes.Count > 0)
|
||||
{
|
||||
var processInfo = _processes.Last.Value;
|
||||
Platform.Logger.Info(LOG_TAG, "Removing process {0} as part of ProcessManager shutdown.", processInfo.Descriptor);
|
||||
processInfo.Process.OnRemove();
|
||||
processInfo.Process.Dispose();
|
||||
_processes.RemoveLast();
|
||||
}
|
||||
|
||||
// the queues will likely not have anything in it, but just in case ...
|
||||
while (_queue.Count > 0)
|
||||
{
|
||||
var processInfo = _queue.Dequeue();
|
||||
Platform.Logger.Info(LOG_TAG, "Removing queued process {0} as part of ProcessManager shutdown.", processInfo.Descriptor);
|
||||
processInfo.Process.Dispose();
|
||||
}
|
||||
|
||||
_processes = null;
|
||||
_queue = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
148
Blarg.GameFramework/States/GameState.cs
Normal file
148
Blarg.GameFramework/States/GameState.cs
Normal file
|
@ -0,0 +1,148 @@
|
|||
using System;
|
||||
using Blarg.GameFramework.Events;
|
||||
using Blarg.GameFramework.Graphics.ScreenEffects;
|
||||
using Blarg.GameFramework.Processes;
|
||||
|
||||
namespace Blarg.GameFramework.States
|
||||
{
|
||||
public abstract class GameState : EventListener, IDisposable
|
||||
{
|
||||
public IGameApp GameApp { get; private set; }
|
||||
public readonly ProcessManager ProcessManager;
|
||||
public readonly ScreenEffectManager EffectManager;
|
||||
public readonly StateManager StateManager;
|
||||
|
||||
public bool IsFinished { get; private set; }
|
||||
public int? ReturnValue { get; private set; }
|
||||
|
||||
public bool IsTransitioning
|
||||
{
|
||||
get { return StateManager.IsStateTransitioning(this); }
|
||||
}
|
||||
|
||||
public bool IsTopState
|
||||
{
|
||||
get { return StateManager.IsTopState(this); }
|
||||
}
|
||||
|
||||
public GameState(StateManager stateManager, EventManager eventManager)
|
||||
: base(eventManager)
|
||||
{
|
||||
if (stateManager == null)
|
||||
throw new ArgumentNullException("stateManager");
|
||||
|
||||
GameApp = stateManager.GameApp;
|
||||
StateManager = stateManager;
|
||||
|
||||
EffectManager = new ScreenEffectManager();
|
||||
ProcessManager = new ProcessManager(this);
|
||||
}
|
||||
|
||||
protected void SetFinished()
|
||||
{
|
||||
IsFinished = true;
|
||||
ReturnValue = null;
|
||||
}
|
||||
|
||||
protected void SetFinished(int returnValue)
|
||||
{
|
||||
IsFinished = true;
|
||||
ReturnValue = returnValue;
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
public virtual void OnPush()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnPop()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnPause(bool dueToOverlay)
|
||||
{
|
||||
ProcessManager.OnPause(dueToOverlay);
|
||||
}
|
||||
|
||||
public virtual void OnResume(bool fromOverlay)
|
||||
{
|
||||
ProcessManager.OnResume(fromOverlay);
|
||||
}
|
||||
|
||||
public virtual void OnAppGainFocus()
|
||||
{
|
||||
ProcessManager.OnAppGainFocus();
|
||||
EffectManager.OnAppGainFocus();
|
||||
}
|
||||
|
||||
public virtual void OnAppLostFocus()
|
||||
{
|
||||
ProcessManager.OnAppLostFocus();
|
||||
EffectManager.OnAppLostFocus();
|
||||
}
|
||||
|
||||
public virtual void OnAppPause()
|
||||
{
|
||||
ProcessManager.OnAppPause();
|
||||
EffectManager.OnAppPause();
|
||||
}
|
||||
|
||||
public virtual void OnAppResume()
|
||||
{
|
||||
ProcessManager.OnAppResume();
|
||||
EffectManager.OnAppResume();
|
||||
}
|
||||
|
||||
public virtual void OnLostContext()
|
||||
{
|
||||
ProcessManager.OnLostContext();
|
||||
EffectManager.OnLostContext();
|
||||
}
|
||||
|
||||
public virtual void OnNewContext()
|
||||
{
|
||||
ProcessManager.OnNewContext();
|
||||
EffectManager.OnNewContext();
|
||||
}
|
||||
|
||||
public virtual void OnRender(float delta)
|
||||
{
|
||||
// switch it up and do effects before processes here so that processes
|
||||
// (which would commonly be used for UI overlay elements) don't get
|
||||
// overwritten by local effects (e.g. flashes, etc.)
|
||||
EffectManager.OnRenderLocal(delta);
|
||||
ProcessManager.OnRender(delta);
|
||||
}
|
||||
|
||||
public virtual void OnResize()
|
||||
{
|
||||
ProcessManager.OnResize();
|
||||
EffectManager.OnResize();
|
||||
}
|
||||
|
||||
public virtual void OnUpdate(float delta)
|
||||
{
|
||||
ProcessManager.OnUpdate(delta);
|
||||
EffectManager.OnUpdate(delta);
|
||||
}
|
||||
|
||||
public virtual bool OnTransition(float delta, bool isTransitioningOut, bool started)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Handle(Event e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
EffectManager.Dispose();
|
||||
ProcessManager.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
34
Blarg.GameFramework/States/StateInfo.cs
Normal file
34
Blarg.GameFramework/States/StateInfo.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
|
||||
namespace Blarg.GameFramework.States
|
||||
{
|
||||
internal class StateInfo
|
||||
{
|
||||
public readonly GameState State;
|
||||
public readonly string Name;
|
||||
public readonly string Descriptor;
|
||||
|
||||
public bool IsOverlay;
|
||||
public bool IsOverlayed;
|
||||
public bool IsTransitioning;
|
||||
public bool IsTransitioningOut;
|
||||
public bool IsTransitionStarting;
|
||||
public bool IsInactive;
|
||||
public bool IsBeingPopped;
|
||||
|
||||
public StateInfo(GameState state, string name = null)
|
||||
{
|
||||
if (state == null)
|
||||
throw new ArgumentNullException("state");
|
||||
|
||||
State = state;
|
||||
Name = name;
|
||||
IsInactive = true;
|
||||
|
||||
if (String.IsNullOrEmpty(Name))
|
||||
Descriptor = state.GetType().Name;
|
||||
else
|
||||
Descriptor = String.Format("{0}[{1}]", state.GetType().Name, Name);
|
||||
}
|
||||
}
|
||||
}
|
712
Blarg.GameFramework/States/StateManager.cs
Normal file
712
Blarg.GameFramework/States/StateManager.cs
Normal file
|
@ -0,0 +1,712 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Blarg.GameFramework.Events;
|
||||
|
||||
namespace Blarg.GameFramework.States
|
||||
{
|
||||
public class StateManager : IDisposable
|
||||
{
|
||||
const string LOG_TAG = "STATES";
|
||||
|
||||
LinkedList<StateInfo> _states;
|
||||
Queue<StateInfo> _pushQueue;
|
||||
Queue<StateInfo> _swapQueue;
|
||||
|
||||
bool _pushQueueHasOverlay;
|
||||
bool _swapQueueHasOverlay;
|
||||
bool _lastCleanedStatesWereAllOverlays;
|
||||
|
||||
public readonly IGameApp GameApp;
|
||||
public readonly EventManager EventManager;
|
||||
|
||||
public int? LastReturnValue { get; private set; }
|
||||
|
||||
public StateManager(IGameApp gameApp, EventManager eventManager)
|
||||
{
|
||||
if (gameApp == null)
|
||||
throw new ArgumentNullException("gameApp");
|
||||
if (eventManager == null)
|
||||
throw new ArgumentNullException("eventManager");
|
||||
|
||||
GameApp = gameApp;
|
||||
EventManager = eventManager;
|
||||
|
||||
_states = new LinkedList<StateInfo>();
|
||||
_pushQueue = new Queue<StateInfo>();
|
||||
_swapQueue = new Queue<StateInfo>();
|
||||
}
|
||||
|
||||
#region Complex Properties
|
||||
|
||||
public GameState TopState
|
||||
{
|
||||
get
|
||||
{
|
||||
var topInfo = Top;
|
||||
if (topInfo == null)
|
||||
return null;
|
||||
else
|
||||
return topInfo.State;
|
||||
}
|
||||
}
|
||||
|
||||
public GameState TopNonOverlayState
|
||||
{
|
||||
get
|
||||
{
|
||||
var topInfo = TopNonOverlay;
|
||||
if (topInfo == null)
|
||||
return null;
|
||||
else
|
||||
return topInfo.State;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsTransitioning
|
||||
{
|
||||
get
|
||||
{
|
||||
for (var node = _states.First; node != null; node = node.Next)
|
||||
{
|
||||
if (node.Value.IsTransitioning)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_states.Count == 0 && _pushQueue.Count == 0 && _swapQueue.Count == 0);
|
||||
}
|
||||
}
|
||||
|
||||
private StateInfo Top
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_states.Count == 0)
|
||||
return null;
|
||||
else
|
||||
return _states.Last.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private StateInfo TopNonOverlay
|
||||
{
|
||||
get
|
||||
{
|
||||
var node = TopNonOverlayNode;
|
||||
if (node != null)
|
||||
return node.Value;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private LinkedListNode<StateInfo> TopNonOverlayNode
|
||||
{
|
||||
get
|
||||
{
|
||||
var node = _states.Last;
|
||||
while (node != null && node.Value.IsOverlay)
|
||||
node = node.Previous;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
public void OnAppGainFocus()
|
||||
{
|
||||
for (var node = TopNonOverlayNode; node != null; node = node.Next)
|
||||
{
|
||||
if (!node.Value.IsInactive)
|
||||
node.Value.State.OnAppGainFocus();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAppLostFocus()
|
||||
{
|
||||
for (var node = TopNonOverlayNode; node != null; node = node.Next)
|
||||
{
|
||||
if (!node.Value.IsInactive)
|
||||
node.Value.State.OnAppLostFocus();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAppPause()
|
||||
{
|
||||
for (var node = TopNonOverlayNode; node != null; node = node.Next)
|
||||
{
|
||||
if (!node.Value.IsInactive)
|
||||
node.Value.State.OnAppPause();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAppResume()
|
||||
{
|
||||
for (var node = TopNonOverlayNode; node != null; node = node.Next)
|
||||
{
|
||||
if (!node.Value.IsInactive)
|
||||
node.Value.State.OnAppResume();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnLostContext()
|
||||
{
|
||||
for (var node = TopNonOverlayNode; node != null; node = node.Next)
|
||||
{
|
||||
if (!node.Value.IsInactive)
|
||||
node.Value.State.OnLostContext();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnNewContext()
|
||||
{
|
||||
for (var node = TopNonOverlayNode; node != null; node = node.Next)
|
||||
{
|
||||
if (!node.Value.IsInactive)
|
||||
node.Value.State.OnNewContext();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnRender(float delta)
|
||||
{
|
||||
for (var node = TopNonOverlayNode; node != null; node = node.Next)
|
||||
{
|
||||
if (!node.Value.IsInactive)
|
||||
{
|
||||
node.Value.State.OnRender(delta);
|
||||
node.Value.State.EffectManager.OnRenderGlobal(delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnResize()
|
||||
{
|
||||
for (var node = TopNonOverlayNode; node != null; node = node.Next)
|
||||
{
|
||||
if (!node.Value.IsInactive)
|
||||
node.Value.State.OnResize();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnUpdate(float delta)
|
||||
{
|
||||
// clear return values (ensuring they're only accessible for 1 tick)
|
||||
LastReturnValue = null;
|
||||
_lastCleanedStatesWereAllOverlays = false;
|
||||
|
||||
CleanupInactiveStates();
|
||||
CheckForFinishedStates();
|
||||
ProcessQueues();
|
||||
ResumeStatesIfNeeded();
|
||||
UpdateTransitions(delta);
|
||||
|
||||
for (var node = TopNonOverlayNode; node != null; node = node.Next)
|
||||
{
|
||||
if (!node.Value.IsInactive)
|
||||
node.Value.State.OnUpdate(delta);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Push / Pop / Overlay / Swap / Queue
|
||||
|
||||
public T Push<T>(string name = null) where T : GameState
|
||||
{
|
||||
var newState = (T)Activator.CreateInstance(typeof(T), this);
|
||||
var stateInfo = new StateInfo(newState, name);
|
||||
QueueForPush(stateInfo);
|
||||
return newState;
|
||||
}
|
||||
|
||||
public T Overlay<T>(string name = null) where T : GameState
|
||||
{
|
||||
var newState = (T)Activator.CreateInstance(typeof(T), this);
|
||||
var stateInfo = new StateInfo(newState, name);
|
||||
stateInfo.IsOverlay = true;
|
||||
QueueForPush(stateInfo);
|
||||
return newState;
|
||||
}
|
||||
|
||||
public T SwapTopWith<T>(string name = null) where T : GameState
|
||||
{
|
||||
// figure out if the current top state is an overlay or not. use that
|
||||
// same setting for the new state that is to be swapped in
|
||||
var currentTopStateInfo = Top;
|
||||
if (currentTopStateInfo == null)
|
||||
throw new InvalidOperationException("Cannot swap, no existing states.");
|
||||
bool isOverlay = currentTopStateInfo.IsOverlay;
|
||||
|
||||
var newState = (T)Activator.CreateInstance(typeof(T), this);
|
||||
var stateInfo = new StateInfo(newState, name);
|
||||
stateInfo.IsOverlay = isOverlay;
|
||||
QueueForSwap(stateInfo, false);
|
||||
return newState;
|
||||
}
|
||||
|
||||
public T SwapTopNonOverlayWith<T>(string name = null) where T : GameState
|
||||
{
|
||||
var newState = (T)Activator.CreateInstance(typeof(T), this);
|
||||
var stateInfo = new StateInfo(newState, name);
|
||||
QueueForSwap(stateInfo, true);
|
||||
return newState;
|
||||
}
|
||||
|
||||
public void Pop()
|
||||
{
|
||||
if (IsTransitioning)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
Platform.Logger.Info(LOG_TAG, "Pop initiated for top-most state only.");
|
||||
StartOnlyTopStateTransitioningOut(false);
|
||||
}
|
||||
|
||||
public void PopTopNonOverlay()
|
||||
{
|
||||
if (IsTransitioning)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
Platform.Logger.Info(LOG_TAG, "Pop initiated for all top active states");
|
||||
StartTopStatesTransitioningOut(false);
|
||||
}
|
||||
|
||||
private void QueueForPush(StateInfo newStateInfo)
|
||||
{
|
||||
if (newStateInfo == null)
|
||||
throw new ArgumentNullException("newStateInfo");
|
||||
if (newStateInfo.State == null)
|
||||
throw new ArgumentException("No GameState provided.");
|
||||
if (_pushQueueHasOverlay && !newStateInfo.IsOverlay)
|
||||
throw new InvalidOperationException("Cannot queue new non-overlay state while queue is active with overlay states.");
|
||||
|
||||
Platform.Logger.Info(LOG_TAG, "Queueing state {0} for pushing.", newStateInfo.Descriptor);
|
||||
|
||||
if (!newStateInfo.IsOverlay)
|
||||
StartTopStatesTransitioningOut(true);
|
||||
|
||||
_pushQueue.Enqueue(newStateInfo);
|
||||
|
||||
if (newStateInfo.IsOverlay)
|
||||
_pushQueueHasOverlay = true;
|
||||
}
|
||||
|
||||
private void QueueForSwap(StateInfo newStateInfo, bool swapTopNonOverlay)
|
||||
{
|
||||
if (newStateInfo == null)
|
||||
throw new ArgumentNullException("newStateInfo");
|
||||
if (newStateInfo.State == null)
|
||||
throw new ArgumentException("No GameState provided.");
|
||||
if (_swapQueueHasOverlay && !newStateInfo.IsOverlay)
|
||||
throw new InvalidOperationException("Cannot queue new non-overlay state while queue is active with overlay states.");
|
||||
|
||||
Platform.Logger.Info(LOG_TAG, "Queueing state {0} for swapping with {1}.", newStateInfo.Descriptor, (swapTopNonOverlay ? "all top active states" : "only top-most active state."));
|
||||
|
||||
if (swapTopNonOverlay)
|
||||
StartTopStatesTransitioningOut(false);
|
||||
else
|
||||
StartOnlyTopStateTransitioningOut(false);
|
||||
|
||||
_swapQueue.Enqueue(newStateInfo);
|
||||
|
||||
if (newStateInfo.IsOverlay)
|
||||
_swapQueueHasOverlay = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal State Management
|
||||
|
||||
private void StartTopStatesTransitioningOut(bool pausing)
|
||||
{
|
||||
for (var node = TopNonOverlayNode; node != null; node = node.Next)
|
||||
{
|
||||
// only look at active states, since inactive ones have already
|
||||
// been transitioned out and will be removed on the next OnUpdate()
|
||||
if (!node.Value.IsInactive)
|
||||
TransitionOut(node.Value, !pausing);
|
||||
}
|
||||
}
|
||||
|
||||
private void StartOnlyTopStateTransitioningOut(bool pausing)
|
||||
{
|
||||
var stateInfo = Top;
|
||||
// if it's not active, then it's just been transitioned out and will be
|
||||
// removed on the next OnUpdate()
|
||||
if (!stateInfo.IsInactive)
|
||||
TransitionOut(stateInfo, !pausing);
|
||||
}
|
||||
|
||||
private void CleanupInactiveStates()
|
||||
{
|
||||
// we don't want to remove any states until everything is done transitioning.
|
||||
// this is to avoid the scenario where the top non-overlay state finishes
|
||||
// transitioning before one of the overlays. if we removed it, the overlays
|
||||
// would then be overlayed over an inactive non-overlay (which wouldn't get
|
||||
// resumed until the current active overlays were done being transitioned)
|
||||
if (IsTransitioning)
|
||||
return;
|
||||
|
||||
bool cleanedUpSomething = false;
|
||||
bool cleanedUpNonOverlay = false;
|
||||
|
||||
var node = _states.First;
|
||||
while (node != null)
|
||||
{
|
||||
var stateInfo = node.Value;
|
||||
if (stateInfo.IsInactive && stateInfo.IsBeingPopped)
|
||||
{
|
||||
cleanedUpSomething = true;
|
||||
if (!stateInfo.IsOverlay)
|
||||
cleanedUpNonOverlay = true;
|
||||
|
||||
// remove this state and move to the next node
|
||||
var next = node.Next;
|
||||
_states.Remove(node);
|
||||
node = next;
|
||||
|
||||
Platform.Logger.Info(LOG_TAG, "Deleting inactive popped state {0}.", stateInfo.Descriptor);
|
||||
stateInfo.State.Dispose();
|
||||
stateInfo = null;
|
||||
}
|
||||
else
|
||||
node = node.Next;
|
||||
}
|
||||
|
||||
if (cleanedUpSomething && !cleanedUpNonOverlay)
|
||||
_lastCleanedStatesWereAllOverlays = true;
|
||||
}
|
||||
|
||||
private void CheckForFinishedStates()
|
||||
{
|
||||
if (_states.Count == 0)
|
||||
return;
|
||||
|
||||
// don't do anything if something is currently transitioning
|
||||
if (IsTransitioning)
|
||||
return;
|
||||
|
||||
bool needToAlsoTransitionOutOverlays = false;
|
||||
|
||||
// check the top non-overlay state first to see if it's finished
|
||||
// and should be transitioned out
|
||||
var topNonOverlayStateInfo = TopNonOverlay;
|
||||
if (!topNonOverlayStateInfo.IsInactive && topNonOverlayStateInfo.State.IsFinished)
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "State {0} marked as finished.", topNonOverlayStateInfo.Descriptor);
|
||||
TransitionOut(topNonOverlayStateInfo, true);
|
||||
|
||||
needToAlsoTransitionOutOverlays = true;
|
||||
}
|
||||
|
||||
// now also check the overlay states (if there were any). we force them to
|
||||
// transition out if the non-overlay state started to transition out so that
|
||||
// we don't end up with overlay states without a parent non-overlay state
|
||||
|
||||
// start the loop off 1 beyond the top non-overlay (which is where the
|
||||
// overlays are, if any)
|
||||
var node = TopNonOverlayNode;
|
||||
if (node != null)
|
||||
{
|
||||
for (node = node.Next; node != null; node = node.Next)
|
||||
{
|
||||
var stateInfo = node.Value;
|
||||
if (!stateInfo.IsInactive && (stateInfo.State.IsFinished || needToAlsoTransitionOutOverlays))
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "State {0} marked as finished.", stateInfo.Descriptor);
|
||||
TransitionOut(stateInfo, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessQueues()
|
||||
{
|
||||
// don't do anything if stuff is currently transitioning
|
||||
if (IsTransitioning)
|
||||
return;
|
||||
|
||||
if (_pushQueue.Count > 0 && _swapQueue.Count > 0)
|
||||
throw new InvalidOperationException("Cannot process queues when both the swap and push queues have items in them.");
|
||||
|
||||
// for each state in the queue, add it to the main list and start
|
||||
// transitioning it in
|
||||
// (note, only one of these queues will be processed each tick due to the above check!)
|
||||
|
||||
while (_pushQueue.Count > 0)
|
||||
{
|
||||
var stateInfo = _pushQueue.Dequeue();
|
||||
|
||||
if (_states.Count > 0)
|
||||
{
|
||||
// if this new state is an overlay, and the current top state is both
|
||||
// currently active and is not currently marked as being overlay-ed
|
||||
// then we should pause it due to overlay
|
||||
var currentTopStateInfo = Top;
|
||||
if (stateInfo.IsOverlay && !currentTopStateInfo.IsInactive && !currentTopStateInfo.IsOverlayed)
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Pausing {0}state {1} due to overlay.", (currentTopStateInfo.IsOverlay ? "overlay " : ""), currentTopStateInfo.Descriptor);
|
||||
currentTopStateInfo.State.OnPause(true);
|
||||
|
||||
// also mark the current top state as being overlay-ed
|
||||
currentTopStateInfo.IsOverlayed = true;
|
||||
}
|
||||
}
|
||||
|
||||
Platform.Logger.Info(LOG_TAG, "Pushing {0}state {1} from push-queue.", (stateInfo.IsOverlay ? "overlay " : ""), stateInfo.Descriptor);
|
||||
stateInfo.State.OnPush();
|
||||
|
||||
TransitionIn(stateInfo, false);
|
||||
|
||||
_states.AddLast(stateInfo);
|
||||
}
|
||||
|
||||
while (_swapQueue.Count > 0)
|
||||
{
|
||||
var stateInfo = _swapQueue.Dequeue();
|
||||
|
||||
// if this new state is an overlay, and the current top state is both
|
||||
// currently active and is not currently marked as being overlay-ed
|
||||
// then we should pause it due to overlay
|
||||
var currentTopStateInfo = Top;
|
||||
if (stateInfo.IsOverlay && !currentTopStateInfo.IsInactive && !currentTopStateInfo.IsOverlayed)
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Pausing {0}state {1} due to overlay.", (currentTopStateInfo.IsOverlay ? "overlay " : ""), currentTopStateInfo.Descriptor);
|
||||
currentTopStateInfo.State.OnPause(true);
|
||||
|
||||
// also mark the current top state as being overlay-ed
|
||||
currentTopStateInfo.IsOverlayed = true;
|
||||
}
|
||||
|
||||
Platform.Logger.Info(LOG_TAG, "Pushing {0}state {1} from swap-queue.", (stateInfo.IsOverlay ? "overlay " : ""), stateInfo.Descriptor);
|
||||
stateInfo.State.OnPush();
|
||||
|
||||
TransitionIn(stateInfo, false);
|
||||
|
||||
_states.AddLast(stateInfo);
|
||||
}
|
||||
|
||||
_pushQueueHasOverlay = false;
|
||||
_swapQueueHasOverlay = false;
|
||||
}
|
||||
|
||||
private void ResumeStatesIfNeeded()
|
||||
{
|
||||
if (_states.Count == 0)
|
||||
return;
|
||||
|
||||
// don't do anything if stuff is currently transitioning
|
||||
if (IsTransitioning)
|
||||
return;
|
||||
|
||||
// did we just clean up one or more overlay states?
|
||||
if (_lastCleanedStatesWereAllOverlays)
|
||||
{
|
||||
// then we need to resume the current top state
|
||||
// (those paused with the flag "from an overlay")
|
||||
var stateInfo = Top;
|
||||
if (stateInfo.IsInactive || !stateInfo.IsOverlayed)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
Platform.Logger.Info(LOG_TAG, "Resuming {0}state {1} due to overlay removal.", (stateInfo.IsOverlay ? "overlay " : ""), stateInfo.Descriptor);
|
||||
stateInfo.State.OnResume(true);
|
||||
|
||||
stateInfo.IsOverlayed = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// if the top state is not inactive, then we don't need to resume anything
|
||||
if (!Top.IsInactive)
|
||||
return;
|
||||
|
||||
Platform.Logger.Info(LOG_TAG, "Top-most state is inactive. Resuming all top states up to and including the next non-overlay.");
|
||||
|
||||
// top state is inactive, time to resume one or more states...
|
||||
// find the topmost non-overlay state and take it and all overlay
|
||||
// states that are above it and transition them in
|
||||
for (var node = TopNonOverlayNode; node != null; node = node.Next)
|
||||
{
|
||||
var stateInfo = node.Value;
|
||||
Platform.Logger.Info(LOG_TAG, "Resuming {0}state {1}.", (stateInfo.IsOverlay ? "overlay " : ""), stateInfo.Descriptor);
|
||||
stateInfo.State.OnResume(false);
|
||||
|
||||
TransitionIn(stateInfo, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void UpdateTransitions(float delta)
|
||||
{
|
||||
for (var node = TopNonOverlayNode; node != null; node = node.Next)
|
||||
{
|
||||
var stateInfo = node.Value;
|
||||
if (stateInfo.IsTransitioning)
|
||||
{
|
||||
bool isDone = stateInfo.State.OnTransition(delta, stateInfo.IsTransitioningOut, stateInfo.IsTransitionStarting);
|
||||
if (isDone)
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Transition {0} {1}state {2} finished.",
|
||||
(stateInfo.IsTransitioningOut ? "out of" : "into"),
|
||||
(stateInfo.IsOverlay ? "overlay " : ""),
|
||||
stateInfo.Descriptor);
|
||||
|
||||
// if the state was being transitioned out, then we should mark
|
||||
// it as inactive, and trigger it's OnPop or OnPause event now
|
||||
if (stateInfo.IsTransitioningOut)
|
||||
{
|
||||
if (stateInfo.IsBeingPopped)
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Popping {0}state {1}", (stateInfo.IsOverlay ? "overlay " : ""), stateInfo.Descriptor);
|
||||
stateInfo.State.OnPop();
|
||||
|
||||
if (stateInfo.State.ReturnValue != null)
|
||||
{
|
||||
LastReturnValue = stateInfo.State.ReturnValue;
|
||||
Platform.Logger.Info(LOG_TAG, "Return value of {0} retrieved from {1}.", LastReturnValue.Value, stateInfo.Descriptor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Platform.Logger.Info(LOG_TAG, "Pausing {0}state {1}.", (stateInfo.IsOverlay ? "overlay " : ""), stateInfo.Descriptor);
|
||||
stateInfo.State.OnPause(false);
|
||||
}
|
||||
stateInfo.IsInactive = true;
|
||||
}
|
||||
|
||||
// done transitioning
|
||||
stateInfo.IsTransitioning = false;
|
||||
stateInfo.IsTransitioningOut = false;
|
||||
}
|
||||
|
||||
stateInfo.IsTransitionStarting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TransitionIn(StateInfo stateInfo, bool forResuming)
|
||||
{
|
||||
stateInfo.IsInactive = false;
|
||||
stateInfo.IsTransitioning = true;
|
||||
stateInfo.IsTransitioningOut = false;
|
||||
stateInfo.IsTransitionStarting = true;
|
||||
Platform.Logger.Info(LOG_TAG, "Transition into {0}state {1} started.", (stateInfo.IsOverlay ? "overlay " : ""), stateInfo.Descriptor);
|
||||
|
||||
if (forResuming)
|
||||
stateInfo.State.ProcessManager.OnResume(false);
|
||||
}
|
||||
|
||||
private void TransitionOut(StateInfo stateInfo, bool forPopping)
|
||||
{
|
||||
stateInfo.IsTransitioning = true;
|
||||
stateInfo.IsTransitioningOut = true;
|
||||
stateInfo.IsTransitionStarting = true;
|
||||
stateInfo.IsBeingPopped = forPopping;
|
||||
Platform.Logger.Info(LOG_TAG, "Transition out of {0}state {1} started.", (stateInfo.IsOverlay ? "overlay " : ""), stateInfo.Descriptor);
|
||||
|
||||
if (forPopping)
|
||||
stateInfo.State.ProcessManager.RemoveAll();
|
||||
else
|
||||
stateInfo.State.ProcessManager.OnPause(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Misc
|
||||
|
||||
private StateInfo GetStateInfoFor(GameState state)
|
||||
{
|
||||
if (state == null)
|
||||
throw new ArgumentNullException("state");
|
||||
|
||||
for (var node = _states.First; node != null; node = node.Next)
|
||||
{
|
||||
if (node.Value.State == state)
|
||||
return node.Value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsStateTransitioning(GameState state)
|
||||
{
|
||||
if (state == null)
|
||||
throw new ArgumentNullException("state");
|
||||
|
||||
var info = GetStateInfoFor(state);
|
||||
if (info == null)
|
||||
return false;
|
||||
else
|
||||
return info.IsTransitioning;
|
||||
}
|
||||
|
||||
public bool IsTopState(GameState state)
|
||||
{
|
||||
if (state == null)
|
||||
throw new ArgumentNullException("state");
|
||||
|
||||
var top = Top;
|
||||
if (top == null)
|
||||
return false;
|
||||
else
|
||||
return (top.State == state);
|
||||
}
|
||||
|
||||
public bool HasState(string name)
|
||||
{
|
||||
for (var node = _states.First; node != null; node = node.Next)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(node.Value.Name) && node.Value.Name == name)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_states == null)
|
||||
return;
|
||||
|
||||
Platform.Logger.Info(LOG_TAG, "StateManager disposing.");
|
||||
|
||||
while (_states.Count > 0)
|
||||
{
|
||||
var stateInfo = _states.Last.Value;
|
||||
Platform.Logger.Info(LOG_TAG, "Popping state {0} as part of StateManager shutdown.", stateInfo.Descriptor);
|
||||
stateInfo.State.OnPop();
|
||||
stateInfo.State.Dispose();
|
||||
_states.RemoveLast();
|
||||
}
|
||||
|
||||
// these queues will likely not have anything in them, but just in case ...
|
||||
while (_pushQueue.Count > 0)
|
||||
{
|
||||
var stateInfo = _pushQueue.Dequeue();
|
||||
Platform.Logger.Info(LOG_TAG, "Deleting push-queued state {0} as part of StateManager shutdown.", stateInfo.Descriptor);
|
||||
stateInfo.State.Dispose();
|
||||
}
|
||||
while (_swapQueue.Count > 0)
|
||||
{
|
||||
var stateInfo = _swapQueue.Dequeue();
|
||||
Platform.Logger.Info(LOG_TAG, "Deleting swap-queued state {0} as part of StateManager shutdown.", stateInfo.Descriptor);
|
||||
stateInfo.State.Dispose();
|
||||
}
|
||||
|
||||
_states = null;
|
||||
_pushQueue = null;
|
||||
_swapQueue = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
Reference in a new issue