@@ -0,0 +1,50 @@
namespace MonoRpg.Engine {
using Microsoft.Xna.Framework;

using MonoRpg.Engine.GameStates;

public class ScreenState : IStateObject {
public Color Color { get; set; }
public ScreenState(Color? color=null) {
Color = color ?? Color.Black;
}

public void Enter(EnterArgs enterParams = null) {
}
public void Exit() {
}
public void HandleInput(float dt) {
}

public bool Update(float dt) {
return true;
}

public void Render(Renderer renderer) {
renderer.DrawRect2D(-System.ScreenWidth/2, System.ScreenHeight-2, System.ScreenWidth/2, -System.ScreenHeight/2, Color);
}

}

public class CaptionState : IStateObject {
public CaptionStyle Style { get; set; }
public string Text { get; set; }

public CaptionState(CaptionStyle style, string text) {
Style = style;
Text = text;
}

public void Enter(EnterArgs enterParams = null) {}
public void Exit() {}
public void HandleInput(float dt) {}

public bool Update(float dt) {
return true;
}

public void Render(Renderer renderer) {
Style.Render(Style, renderer, Text);
}
}
}
@@ -3,26 +3,138 @@
using System.Threading.Tasks;

namespace MonoRpg.Engine {
using global::System.Diagnostics;

using Microsoft.Xna.Framework;
// type alias, because this is getting really tedious...
using StoryBoardFunc = Func<Storyboard, IStoryboardEvent>;

public interface IStoryboardEvent {
void Update(float dt);
bool IsBlocking { get; }
void Update(float dt, Storyboard storyboard);
bool IsBlocking { get; set; }
bool IsFinished { get; }
}

public static class Events {
public static StoryBoardFunc EmptyEvent => Wait(0);

public static StoryBoardFunc Wait(float seconds) {
return (storyboard) => new WaitEvent(seconds);
}

public static StoryBoardFunc BlackScreen(string id=null, float alpha=1.0f) {
if (string.IsNullOrEmpty(id)) {
id = "blackscreen";
}
var black = new Color(0,0,0,alpha);
return storyboard => {
var screen = new ScreenState(black);
storyboard.PushState(id, screen);
return EmptyEvent(storyboard);
};
}

public static StoryBoardFunc FadeScreen(float start, float finish, float duration = 3, string id = null) {
return storyboard => {
ScreenState target = storyboard.SubStack.Top as ScreenState;
if (!string.IsNullOrEmpty(id)) {
target = storyboard.States[id] as ScreenState;
}
Debug.Assert(target != null);
var tween = new Tween(start, finish, duration);
return new TweenEvent<ScreenState>(
tween,
target,
(state, value) => state.Color = new Color(state.Color, value)
);
};
}

public static StoryBoardFunc FadeInScreen(string id=null, float duration=3) {
return FadeScreen(0, 1, duration, id);
}

public static StoryBoardFunc FadeOutScreen(string id=null, float duration=3) {
return FadeScreen(1, 0, duration, id);
}

public static StoryBoardFunc Caption(string id, string style, string text) {
return storyboard => {
var styleCopy = new CaptionStyle(CaptionStyle.Styles[style]);
var caption = new CaptionState(styleCopy, text);
storyboard.PushState(id, caption);
return new TweenEvent<CaptionStyle>(new Tween(0, 1, styleCopy.Duration), styleCopy, styleCopy.ApplyFunc);
};
}
public static StoryBoardFunc FadeOutCaption(string id = null, float duration = -1) {
return storyboard => {
var target = storyboard.SubStack.Top as CaptionState;
if (id != null) {
target = storyboard.States[id] as CaptionState;
}
Debug.Assert(target != null);
var styleCopy = target.Style;
if (duration < 0) {
duration = styleCopy.Duration;
}
return new TweenEvent<CaptionStyle>(new Tween(1, 0, duration), styleCopy, styleCopy.ApplyFunc);
};
}

public static StoryBoardFunc NoBlock(StoryBoardFunc f) {
return storyboard => {
var evt = f(storyboard);
evt.IsBlocking = false;
return evt;
};
}

public static StoryBoardFunc KillState(string id) {
return storyboard => {
storyboard.RemoveState(id);
return EmptyEvent(storyboard);
};
}
}

public class WaitEvent : IStoryboardEvent{
public float Seconds { get; set; }
public bool IsBlocking => true;
public bool IsBlocking {
get;
set;
}
public bool IsFinished => Seconds <= 0;

private WaitEvent(float seconds) {
internal WaitEvent(float seconds) {
Seconds = seconds;
IsBlocking = true;
}

public void Update(float dt) {
public void Update(float dt, Storyboard storyboard) {
Seconds -= dt;
}
}

public class TweenEvent<T> : IStoryboardEvent {
public Tween Tween { get; set; }
public T Target { get; set; }
public Action<T, float> ApplyFunc { get; set; }

public bool IsBlocking { get; set; }
public bool IsFinished => Tween.Finished;

public TweenEvent(Tween tween, T target, Action<T, float> applyFunc) {
Tween = tween;
Target = target;
ApplyFunc = applyFunc;
IsBlocking = true;

}

public static Func<WaitEvent> Wait(float seconds) {
return () => new WaitEvent(seconds);
public void Update(float dt, Storyboard storyboard) {
Tween.Update(dt);
ApplyFunc(Target, Tween.Value);
}
public void Render() { }
}
}
@@ -8,18 +8,26 @@

public class Storyboard : IStateObject{
public StateStack Stack { get; set; }
public List<Func<IStoryboardEvent>> EventFactories { get; set; }
public List<Func<Storyboard, IStoryboardEvent>> EventFactories { get; set; }
public List<IStoryboardEvent> InstantiatedEvents { get; set; }

public Storyboard(StateStack stack, params Func<IStoryboardEvent>[] events) {
public Dictionary<string, IStateObject> States { get; set; }
public StateStack SubStack { get; set; }


public Storyboard(StateStack stack, params Func<Storyboard, IStoryboardEvent>[] events) {
Stack = stack;
EventFactories = events.ToList();
InstantiatedEvents = new List<IStoryboardEvent>();
for (var index = 0; index < EventFactories.Count; index++) {
InstantiatedEvents.Add(null);
}
States = new Dictionary<string, IStateObject>();
SubStack = new StateStack();
}



public void Enter(EnterArgs enterParams = null) {
}
public void Exit() {
@@ -29,17 +37,19 @@ public class Storyboard : IStateObject{
}

public bool Update(float dt) {
SubStack.Update(dt);

if (EventFactories.Count == 0) {
Stack.Pop();
}
var deleteIndex = -1;
for (var index = 0; index < EventFactories.Count; index++) {
var evt = InstantiatedEvents[index];
if (evt == null) {
InstantiatedEvents[index] = EventFactories[index]();
InstantiatedEvents[index] = EventFactories[index](this);
evt = InstantiatedEvents[index];
}
evt.Update(dt);
evt.Update(dt, this);
if (evt.IsFinished) {
deleteIndex = index;
break;
@@ -56,8 +66,21 @@ public class Storyboard : IStateObject{
}

public void Render(Renderer renderer) {
var debugText = $"Events Stack: {InstantiatedEvents.Count}";
renderer.DrawText2D(0,0, debugText);
SubStack.Render(renderer);

//var debugText = $"Events Stack: {InstantiatedEvents.Count}";
//renderer.DrawText2D(0,0, debugText);
}

public void PushState(string id, IStateObject state) {
States[id] = state;
SubStack.Push(state);
}

public void RemoveState(string id) {
var state = States[id];
States.Remove(id);
SubStack.States.Remove(state);
}
}
}