aboutsummaryrefslogtreecommitdiff
path: root/V3/Screens
diff options
context:
space:
mode:
Diffstat (limited to 'V3/Screens')
-rw-r--r--V3/Screens/AbstractScreen.cs57
-rw-r--r--V3/Screens/AchievementsScreen.cs221
-rw-r--r--V3/Screens/DeathScreen.cs140
-rw-r--r--V3/Screens/DebugScreen.cs92
-rw-r--r--V3/Screens/FpsCounter.cs46
-rw-r--r--V3/Screens/GameScreen.cs306
-rw-r--r--V3/Screens/HudScreen.cs349
-rw-r--r--V3/Screens/IDrawable.cs19
-rw-r--r--V3/Screens/IScreen.cs39
-rw-r--r--V3/Screens/IScreenFactory.cs27
-rw-r--r--V3/Screens/IScreenManager.cs35
-rw-r--r--V3/Screens/IUpdatable.cs16
-rw-r--r--V3/Screens/LoadScreen.cs124
-rw-r--r--V3/Screens/MainScreen.cs195
-rw-r--r--V3/Screens/MenuActions.cs208
-rw-r--r--V3/Screens/OptionsScreen.cs197
-rw-r--r--V3/Screens/PauseScreen.cs140
-rw-r--r--V3/Screens/ScreenManager.cs164
-rw-r--r--V3/Screens/StatisticsScreen.cs186
-rw-r--r--V3/Screens/TechdemoScreen.cs325
-rw-r--r--V3/Screens/VictoryScreen.cs76
21 files changed, 2962 insertions, 0 deletions
diff --git a/V3/Screens/AbstractScreen.cs b/V3/Screens/AbstractScreen.cs
new file mode 100644
index 0000000..becd38d
--- /dev/null
+++ b/V3/Screens/AbstractScreen.cs
@@ -0,0 +1,57 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Input;
+
+namespace V3.Screens
+{
+ public abstract class AbstractScreen : IScreen
+ {
+ /// <summary>
+ /// Indicates whether screens below this one should be updated.
+ /// </summary>
+ public bool UpdateLower { get; }
+
+ /// <summary>
+ /// Indicates whether screens below this one should be drawn.
+ /// </summary>
+ public bool DrawLower { get; }
+
+ protected AbstractScreen(bool updateLower, bool drawLower)
+ {
+ UpdateLower = updateLower;
+ DrawLower = drawLower;
+ }
+
+ /// <summary>
+ /// Handles the given key event and returns whether it should be passed
+ /// to the screens below this one.
+ /// </summary>
+ /// <param name="keyEvent">the key event that occurred</param>
+ /// <returns>true if the event has been handeled by this screen and
+ /// should not be passed to the lower screens, false otherwise</returns>
+ public virtual bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ return false;
+ }
+
+ /// <summary>
+ /// Handles the given mouse event and returns whether it should be passed
+ /// to the screens below this one.
+ /// </summary>
+ /// <param name="mouseEvent">the mouse event that occurred</param>
+ /// <returns>true if the event has been handeled by this screen and
+ /// should not be passed to the lower screens, false otherwise</returns>
+ public virtual bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ return true;
+ }
+
+ public virtual void Update(GameTime gameTime)
+ {
+ }
+
+ public virtual void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ }
+ }
+}
diff --git a/V3/Screens/AchievementsScreen.cs b/V3/Screens/AchievementsScreen.cs
new file mode 100644
index 0000000..096fb81
--- /dev/null
+++ b/V3/Screens/AchievementsScreen.cs
@@ -0,0 +1,221 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using V3.Input;
+using V3.Objects;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// The screen for the AchievementsAndStatistics.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class AchievementsScreen : AbstractScreen, IInitializable
+ {
+ //private AchievementBox mFirstSteps;
+ private AchievementBox mKillPrince;
+ private AchievementBox mKillKing;
+ //private AchievementBox mWarmasterAndWizard;
+ private AchievementBox mKaboom;
+ private AchievementBox mMarathonRunner;
+ private AchievementBox mIronMan;
+ private AchievementBox mMeatballCompany;
+ private AchievementBox mSkeletonHorseCavalry;
+ private AchievementBox mRightHandOfDeath;
+ private AchievementBox mMinimalist;
+ private AchievementBox mHundredDeadCorpses;
+ private AchievementBox mUndeadArmy;
+ private AchievementBox mHellsNotWaiting;
+
+
+ private readonly ContentManager mContentManager;
+ private readonly IMenuFactory mMenuFactory;
+ private readonly MenuActions mMenuActions;
+ private readonly WidgetFactory mWidgetFactory;
+ private readonly AchievementsAndStatistics mAchievementsAndStatistics;
+ private readonly ObjectsManager mObjectsManager;
+
+ private Button mButtonBack;
+ private SelectButton mSelectPage;
+
+ private List<IMenu> mMenuList = new List<IMenu>();
+ private Texture2D mRectangle;
+
+ public AchievementsScreen(ContentManager contentManager, MenuActions menuActions, WidgetFactory widgetFactory,
+ IMenuFactory menuFactory, AchievementsAndStatistics achievementsAndStatistics, ObjectsManager objectsManager)
+ : base(false, true)
+ {
+ mContentManager = contentManager;
+ mMenuFactory = menuFactory;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ mAchievementsAndStatistics = achievementsAndStatistics;
+ mObjectsManager = objectsManager;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+
+ mButtonBack = mWidgetFactory.CreateButton("Zurück");
+ mSelectPage = mWidgetFactory.CreateSelectButton();
+
+ var menu = mMenuFactory.CreateVerticalMenu();
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mSelectPage);
+
+ mKillPrince = mWidgetFactory.CreateAchievementBox();
+ mKillPrince.SetText("Erbfolge aufgehalten", "Vernichtet Prinz Erhard.");
+ menu.Widgets.Add(mKillPrince);
+
+ mKaboom = mWidgetFactory.CreateAchievementBox();
+ mKaboom.SetText("KABUMM!!!", "Tötet mindestens 10 Gegner mit einer einzigen Fleischklops-Explosion.");
+ menu.Widgets.Add(mKaboom);
+
+ mKillKing = mWidgetFactory.CreateAchievementBox();
+ mKillKing.SetText("Königsmord", "Nehmt eure Vergeltung am König und tötet ihn.");
+ menu.Widgets.Add(mKillKing);
+
+ menu.Widgets.Add(mButtonBack);
+
+ menu = mMenuFactory.CreateVerticalMenu();
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mSelectPage);
+
+ mHundredDeadCorpses = mWidgetFactory.CreateAchievementBox();
+ mHundredDeadCorpses.SetText("Leichenfledderer", "Tötet in einer Mission mindestens 100 Gegner.");
+ menu.Widgets.Add(mHundredDeadCorpses);
+
+ mUndeadArmy = mWidgetFactory.CreateAchievementBox();
+ mUndeadArmy.SetText("Untote Armee", "Tötet in einer Mission mindestens 1000 Gegner.");
+ menu.Widgets.Add(mUndeadArmy);
+
+ mRightHandOfDeath = mWidgetFactory.CreateAchievementBox();
+ mRightHandOfDeath.SetText("Die erbarmungslose rechte Hand des Todes", "Tötet insgesamt 10 000 Gegner.");
+ menu.Widgets.Add(mRightHandOfDeath);
+
+ menu.Widgets.Add(mButtonBack);
+
+ menu = mMenuFactory.CreateVerticalMenu();
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mSelectPage);
+
+ mMeatballCompany = mWidgetFactory.CreateAchievementBox();
+ mMeatballCompany.SetText("Fleischpanzer-Kompanie", "Erschafft und befehligt in einer Mission 10 Fleischklopse gleichzeitig.");
+ menu.Widgets.Add(mMeatballCompany);
+
+ mSkeletonHorseCavalry = mWidgetFactory.CreateAchievementBox();
+ mSkeletonHorseCavalry.SetText("Klappernde Kavallerie", "Erschafft und befehligt in einer Mission 25 Skelettpferde gleichzeitig.");
+ menu.Widgets.Add(mSkeletonHorseCavalry);
+
+ mMinimalist = mWidgetFactory.CreateAchievementBox();
+ mMinimalist.SetText("Minimalist", "Beendet eine Mission und setzt dabei weniger als 100 Einheiten ein.");
+ menu.Widgets.Add(mMinimalist);
+
+ menu.Widgets.Add(mButtonBack);
+
+ menu = mMenuFactory.CreateVerticalMenu();
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mSelectPage);
+
+ mHellsNotWaiting = mWidgetFactory.CreateAchievementBox();
+ mHellsNotWaiting.SetText("Die Hölle wartet nicht", "Beendet eine Mission in weniger als 5 Minuten.");
+ menu.Widgets.Add(mHellsNotWaiting);
+
+ mMarathonRunner = mWidgetFactory.CreateAchievementBox();
+ mMarathonRunner.SetText("Marathonläufer", "Legt in einer Mission mindestens 1000m zurück.");
+ menu.Widgets.Add(mMarathonRunner);
+
+ mIronMan = mWidgetFactory.CreateAchievementBox();
+ mIronMan.SetText("Der Iron Man", "Legt insgesamt eine Strecke von 10 000m zurück.");
+ menu.Widgets.Add(mIronMan);
+
+ menu.Widgets.Add(mButtonBack);
+
+ for (var i = 0; i < mMenuList.Count; i++)
+ mSelectPage.Values.Add($"Seite {i + 1}");
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.Escape)
+ mMenuActions.Close(this);
+
+ return true;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ GetCurrentMenu().HandleMouseEvent(mouseEvent);
+ return true;
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ if (mButtonBack.IsClicked)
+ {
+ mMenuActions.Close(this);
+ }
+ GetCurrentMenu().Update();
+
+ // achievement datas. if one value becomes a given value, the corresponding achievement will be enabled.
+
+ if (mAchievementsAndStatistics.mKillPrince)
+ mKillPrince.IsEnabled = true;
+ if (mAchievementsAndStatistics.mKillKing)
+ mKillKing.IsEnabled = true;
+ if (mAchievementsAndStatistics.mHellsNotWaiting)
+ mHellsNotWaiting.IsEnabled = true;
+ if (mAchievementsAndStatistics.mKaboom)
+ mKaboom.IsEnabled = true;
+
+ if (mAchievementsAndStatistics.mMarathonRunner >= 1000)
+ mMarathonRunner.IsEnabled = true;
+ if (mAchievementsAndStatistics.mIronMan >= 10000)
+ mIronMan.IsEnabled = true;
+ if (mAchievementsAndStatistics.mMeatballCompany >= 10)
+ mMeatballCompany.IsEnabled = true;
+ if (mAchievementsAndStatistics.mSkeletonHorseCavalry >= 25)
+ mSkeletonHorseCavalry.IsEnabled = true;
+ if (mAchievementsAndStatistics.mRightHandOfDeath >= 10000)
+ mRightHandOfDeath.IsEnabled = true;
+ if (mAchievementsAndStatistics.mMinimalist <= 100 && mObjectsManager.Boss != null && mObjectsManager.Boss.IsDead)
+ mMinimalist.IsEnabled = true;
+ if (mAchievementsAndStatistics.mHundredDeadCorpses >= 100)
+ mHundredDeadCorpses.IsEnabled = true;
+ if (mAchievementsAndStatistics.mUndeadArmy >= 1000)
+ mUndeadArmy.IsEnabled = true;
+ }
+
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ var menu = GetCurrentMenu();
+ var backgroundRectangle = new Rectangle((int)menu.Position.X,
+ (int)menu.Position.Y, (int)menu.Size.X,
+ (int)menu.Size.Y);
+ backgroundRectangle.X -= 30;
+ backgroundRectangle.Y -= 30;
+ backgroundRectangle.Width += 60;
+ backgroundRectangle.Height += 60;
+
+ spriteBatch.Begin();
+ spriteBatch.Draw(mRectangle, backgroundRectangle, Color.WhiteSmoke);
+ spriteBatch.End();
+
+ menu.Draw(spriteBatch);
+ }
+
+ private IMenu GetCurrentMenu()
+ {
+ return mMenuList[mSelectPage.SelectedIndex];
+ }
+ }
+}
diff --git a/V3/Screens/DeathScreen.cs b/V3/Screens/DeathScreen.cs
new file mode 100644
index 0000000..230d40a
--- /dev/null
+++ b/V3/Screens/DeathScreen.cs
@@ -0,0 +1,140 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using V3.Input;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class DeathScreen : AbstractScreen, IInitializable
+ {
+ private static TimeSpan sTotalDelay = TimeSpan.FromSeconds(2);
+
+ private readonly ContentManager mContentManager;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly MenuActions mMenuActions;
+ private readonly WidgetFactory mWidgetFactory;
+
+ private Button mMenuButton;
+ private Button mCloseGameButton;
+ private Vector2 mButtonPosition;
+ private Vector2 mCenter;
+ private Vector2 mFontCenter;
+ private SpriteFont mDeathFont;
+ private Texture2D mRectangle;
+
+ private TimeSpan mDelayTimer = sTotalDelay;
+
+ /// <summary>
+ /// Creates a death screen if the players health reaches 0.
+ /// </summary>
+
+ public DeathScreen(ContentManager contentManager,
+ GraphicsDeviceManager graphicsDeviceManager,
+ MenuActions menuActions,
+ WidgetFactory widgetFactory)
+ : base(false, true)
+ {
+ mContentManager = contentManager;
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ return false;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ if (mDelayTimer <= TimeSpan.Zero)
+ {
+ mMenuButton.HandleMouseEvent(mouseEvent);
+ mCloseGameButton.HandleMouseEvent(mouseEvent);
+ }
+ return false;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ mDeathFont = mContentManager.Load<SpriteFont>("Fonts/DeathFont");
+
+ mMenuButton = mWidgetFactory.CreateButton("Zum Hauptmenü");
+ mMenuButton.Position = Vector2.Zero;
+ mMenuButton.PaddingX = 10;
+ mMenuButton.PaddingY = 0;
+ mMenuButton.Size = mMenuButton.GetMinimumSize();
+ mMenuButton.BackgroundColor = Color.Gray * 0.7f;
+
+ mCloseGameButton = mWidgetFactory.CreateButton("Spiel schließen");
+ mCloseGameButton.Position = Vector2.Zero;
+ mCloseGameButton.PaddingX = 10;
+ mCloseGameButton.PaddingY = 0;
+ mCloseGameButton.Size = mCloseGameButton.GetMinimumSize();
+ mCloseGameButton.BackgroundColor = Color.Gray * 0.7f;
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ var viewport = mGraphicsDeviceManager.GraphicsDevice.Viewport;
+
+ mCenter = new Vector2(viewport.Width / 2f, viewport.Height / 2f);
+ mFontCenter = mDeathFont.MeasureString("Ihr seid tot") / 2;
+ mButtonPosition = new Vector2(mCenter.X - mFontCenter.X * 2/3, mCenter.Y + mFontCenter.Y * 2);
+
+ if (mDelayTimer > TimeSpan.Zero)
+ mDelayTimer -= gameTime.ElapsedGameTime;
+
+ mMenuButton.Position = mButtonPosition;
+ mCloseGameButton.Position = mMenuButton.Position + new Vector2(mFontCenter.X * 3/ 4, 0);
+
+ if (mMenuButton.IsClicked)
+ {
+ mMenuActions.OpenMainScreen();
+ }
+ else if (mCloseGameButton.IsClicked)
+ {
+ mMenuActions.Exit();
+ }
+
+ mMenuButton.IsSelected = mMenuButton.CheckSelected(Mouse.GetState().Position);
+ mMenuButton.IsClicked = false;
+
+ mCloseGameButton.IsSelected = mCloseGameButton.CheckSelected(Mouse.GetState().Position);
+ mCloseGameButton.IsClicked = false;
+ }
+
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ float displayRatio = (float) (1 - mDelayTimer.TotalMilliseconds / sTotalDelay.TotalMilliseconds);
+
+ spriteBatch.Begin();
+ spriteBatch.Draw(mRectangle,
+ mGraphicsDeviceManager.GraphicsDevice.Viewport.Bounds,
+ Color.Black * 0.5f * displayRatio);
+
+ spriteBatch.DrawString(mDeathFont,
+ "Ihr seid tot",
+ mCenter,
+ Color.Firebrick * displayRatio,
+ 0,
+ mFontCenter,
+ 1.0f,
+ SpriteEffects.None,
+ 0.5f);
+
+ if (mDelayTimer <= TimeSpan.Zero)
+ {
+ mMenuButton.Draw(spriteBatch);
+ mCloseGameButton.Draw(spriteBatch);
+ }
+ spriteBatch.End();
+ }
+ }
+}
diff --git a/V3/Screens/DebugScreen.cs b/V3/Screens/DebugScreen.cs
new file mode 100644
index 0000000..a1e0820
--- /dev/null
+++ b/V3/Screens/DebugScreen.cs
@@ -0,0 +1,92 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Ninject;
+using V3.Data;
+using V3.Input;
+using V3.Objects;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// A special screen that counts and show the number of frames per
+ /// second (if debug is enabled). This screen is not added to the
+ /// screen stack, but is always drawn and updated by the screen manager.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class DebugScreen : AbstractScreen, IInitializable
+ {
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly IObjectsManager mObjectsManager;
+ private readonly IOptionsManager mOptionsManager;
+ private readonly WidgetFactory mWidgetFactory;
+ private FpsCounter mFpsCounter;
+ private Label mFpsLabel;
+ private Label mUnitCountLabel;
+ private int mUnitCount;
+
+ /// <summary>
+ /// Creates a new debug screen.
+ /// </summary>
+ public DebugScreen(GraphicsDeviceManager graphicsDeviceManager,
+ IObjectsManager objectsManager, IOptionsManager optionsManager,
+ WidgetFactory widgetFactory) : base(true, true)
+ {
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mObjectsManager = objectsManager;
+ mOptionsManager = optionsManager;
+ mWidgetFactory = widgetFactory;
+ }
+
+ public void Initialize()
+ {
+ mFpsCounter = new FpsCounter();
+
+ mFpsLabel = mWidgetFactory.CreateLabel("");
+ mFpsLabel.PaddingX = 10;
+ mFpsLabel.PaddingY = 0;
+ mFpsLabel.HorizontalOrientation = HorizontalOrientation.Left;
+ mFpsLabel.Color = Color.Red;
+
+ mUnitCountLabel = mWidgetFactory.CreateLabel("");
+ mUnitCountLabel.PaddingX = 10;
+ mUnitCountLabel.PaddingY = 0;
+ mUnitCountLabel.HorizontalOrientation = HorizontalOrientation.Left;
+ mUnitCountLabel.Color = Color.Red;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ return false;
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ mFpsCounter.Update(gameTime);
+ mUnitCount = mObjectsManager.CreatureList?.Count ?? 0;
+ }
+
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ mFpsCounter.AddFrame();
+
+ if (mOptionsManager.Options.DebugMode == DebugMode.Off)
+ return;
+
+ var viewport = mGraphicsDeviceManager.GraphicsDevice.Viewport;
+
+ mFpsLabel.Text = $"FPS: {mFpsCounter.Fps} " + (gameTime.IsRunningSlowly ? "!" : "");
+ mFpsLabel.Size = mFpsLabel.GetMinimumSize();
+ mFpsLabel.Position = new Vector2(0, viewport.Height - mFpsLabel.Size.Y);
+
+ mUnitCountLabel.Text = $"# units: {mUnitCount}";
+ mUnitCountLabel.Size = mUnitCountLabel.GetMinimumSize();
+ mUnitCountLabel.Position = mFpsLabel.Position - new Vector2(0, mFpsLabel.Size.Y);
+
+ spriteBatch.Begin();
+ mFpsLabel.Draw(spriteBatch);
+ mUnitCountLabel.Draw(spriteBatch);
+ spriteBatch.End();
+ }
+ }
+}
diff --git a/V3/Screens/FpsCounter.cs b/V3/Screens/FpsCounter.cs
new file mode 100644
index 0000000..5c3ac0c
--- /dev/null
+++ b/V3/Screens/FpsCounter.cs
@@ -0,0 +1,46 @@
+using System;
+using Microsoft.Xna.Framework;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// Counts the frames per second based on the elapsed time and the number
+ /// of frames that have been drawn. Call Update in each Update, and
+ /// AddFrame in each Draw.
+ /// </summary>
+ public sealed class FpsCounter
+ {
+ /// <summary>
+ /// The current frames per second.
+ /// </summary>
+ public int Fps { get; private set; }
+
+ private int mFrameCount;
+ private TimeSpan mTimeSpan = TimeSpan.Zero;
+
+ /// <summary>
+ /// Updates the elapsed time and -- once every second -- the fps value.
+ /// </summary>
+ /// <param name="gameTime">the elapsed game time</param>
+ public void Update(GameTime gameTime)
+ {
+ mTimeSpan += gameTime.ElapsedGameTime;
+
+ if (mTimeSpan > TimeSpan.FromSeconds(1))
+ {
+ mTimeSpan -= TimeSpan.FromSeconds(1);
+ Fps = mFrameCount;
+ mFrameCount = 0;
+ }
+ }
+
+ /// <summary>
+ /// Registers that a frame has been drawn. Should be called once for
+ /// every Draw.
+ /// </summary>
+ public void AddFrame()
+ {
+ mFrameCount++;
+ }
+ }
+}
diff --git a/V3/Screens/GameScreen.cs b/V3/Screens/GameScreen.cs
new file mode 100644
index 0000000..e37b100
--- /dev/null
+++ b/V3/Screens/GameScreen.cs
@@ -0,0 +1,306 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using System.Collections.Generic;
+using V3.AI;
+using V3.Camera;
+using V3.Data;
+using V3.Effects;
+using V3.Input;
+using V3.Map;
+using V3.Objects;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// The main game screen.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class GameScreen : AbstractScreen, IInitializable
+ {
+ private readonly CameraManager mCameraManager;
+ private readonly ContentManager mContentManager;
+ private readonly CreatureFactory mCreatureFactory;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly MenuActions mMenuActions;
+ private readonly IOptionsManager mOptionsManager;
+ private readonly Selection mSelection;
+ private readonly Transformation mTransformation;
+ private readonly IAiPlayer mAiPlayer;
+ private readonly IMapManager mMapManager;
+ private readonly IEffectsManager mEffectsManager;
+ private readonly IObjectsManager mObjectsManager;
+ private readonly Pathfinder mPathfinder;
+ private readonly Texture2D mOnePixelTexture;
+ private readonly FogOfWar mFog;
+ private bool mFogOfWarActivaded = true;
+ private AchievementsAndStatistics mAchievementsAndStatistics;
+ private int mFogCounter;
+ // Fields for handling mouse input.
+ private Point mInitialClickPosition;
+ private bool mLeftButtonPressed;
+ private bool mRightButtonPressed;
+ private Vector2 mRightButtonPosition;
+
+ /// <summary>
+ /// Creates a new game screen.
+ /// </summary>
+ public GameScreen(IOptionsManager optionsManager, CameraManager cameraManager,
+ ContentManager contentManager, CreatureFactory creatureFactory,
+ GraphicsDeviceManager graphicsDeviceManager, IMapManager mapManager,
+ MenuActions menuActions, IAiPlayer aiPlayer, IObjectsManager objectsManager,
+ Pathfinder pathfinder, Selection selection, FogOfWar fog, Transformation transformation,
+ IEffectsManager effectsManager, AchievementsAndStatistics achievementsAndStatistics) : base(false, false)
+ {
+ mMapManager = mapManager;
+ mObjectsManager = objectsManager;
+ mCameraManager = cameraManager;
+ mOptionsManager = optionsManager;
+ mContentManager = contentManager;
+ mCreatureFactory = creatureFactory;
+ mEffectsManager = effectsManager;
+ mTransformation = transformation;
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mMenuActions = menuActions;
+ mAiPlayer = aiPlayer;
+ mPathfinder = pathfinder;
+ mSelection = selection;
+ mFog = fog;
+ mOnePixelTexture = contentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ mAchievementsAndStatistics = achievementsAndStatistics;
+ }
+
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ if (mouseEvent.MouseButton == MouseButton.Left && mouseEvent.ButtonState == ButtonState.Pressed)
+ {
+ if (!mLeftButtonPressed)
+ {
+ mLeftButtonPressed = true;
+ mInitialClickPosition = mouseEvent.PositionPressed;
+ }
+ }
+ else
+ {
+ if (mLeftButtonPressed)
+ {
+ mSelection.Select(mInitialClickPosition + mCameraManager.GetCamera().Location.ToPoint(),
+ mouseEvent.PositionReleased.GetValueOrDefault() + mCameraManager.GetCamera().Location.ToPoint());
+ mLeftButtonPressed = false;
+ }
+ }
+ if (mouseEvent.MouseButton == MouseButton.Right && mouseEvent.ButtonState == ButtonState.Pressed)
+ {
+ if (!mRightButtonPressed)
+ {
+ mRightButtonPressed = true;
+ mInitialClickPosition = mouseEvent.PositionPressed;
+ }
+ }
+ if (mouseEvent.MouseButton == MouseButton.Right && mouseEvent.ButtonState == ButtonState.Released)
+ {
+ if (mouseEvent.PositionReleased != null && mouseEvent.ReleasedOnScreen)
+ {
+ mRightButtonPressed = false;
+ mRightButtonPosition = mouseEvent.PositionReleased.Value.ToVector2() + mCameraManager.GetCamera().Location;
+ mSelection.Move(mouseEvent.PositionReleased.Value.ToVector2()
+ + mCameraManager.GetCamera().Location);
+ }
+ }
+ return true;
+ }
+
+ public void Initialize()
+ {
+#if NEWMAP
+ mMapManager.Load("work_in_progress");
+#else
+ mMapManager.Load("map_grassland");
+#endif
+ mObjectsManager.Initialize(mMapManager);
+ mCameraManager.Initialize(mMapManager.SizeInPixel);
+ mPathfinder.LoadGrid(mMapManager.GetPathfindingGrid());
+ mFog.LoadContent(mContentManager);
+ mFog.LoadGrid(mMapManager.SizeInPixel);
+ mTransformation.mSelection = mSelection;
+ mTransformation.LoadArea(mContentManager);
+ mTransformation.ObjectsManager = mObjectsManager;
+ }
+
+ /// <summary>
+ /// Create the initial population as designated by the map manager,
+ /// the player character, and possibly other creatures.
+ /// </summary>
+ public void CreateInitialPopulation()
+ {
+ // Create initial population.
+ foreach (var creature in mMapManager.GetPopulation(mCreatureFactory, mPathfinder))
+ {
+ mObjectsManager.CreateCreature(creature);
+ }
+
+#if NEWMAP
+ var necromancer = mCreatureFactory.CreateNecromancer(new Vector2(385, 420), MovementDirection.S);
+ mObjectsManager.CreatePlayerCharacter(necromancer);
+ // Create Prince and his guard.
+ mObjectsManager.CreatePrince(mCreatureFactory.CreatePrince(new Vector2(6139, 3039), MovementDirection.SO));
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateKingsGuard(new Vector2(5794, 2900), MovementDirection.N));
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateKingsGuard(new Vector2(5858, 2900), MovementDirection.N));
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateKingsGuard(new Vector2(5936, 2885), MovementDirection.NW));
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateKingsGuard(new Vector2(6000, 2885), MovementDirection.NW));
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateKingsGuard(new Vector2(6064, 2885), MovementDirection.NW));
+#else
+ // Add creatures for testing purposes.
+ var necromancer = mCreatureFactory.CreateNecromancer(new Vector2(300, 150), MovementDirection.S);
+ var zombie1 = mCreatureFactory.CreateZombie(new Vector2(800, 200), MovementDirection.S);
+ var zombie2 = mCreatureFactory.CreateZombie(new Vector2(850, 250), MovementDirection.S);
+ var zombie3 = mCreatureFactory.CreateZombie(new Vector2(900, 200), MovementDirection.S);
+ var zombie4 = mCreatureFactory.CreateZombie(new Vector2(950, 250), MovementDirection.S);
+ var zombie5 = mCreatureFactory.CreateZombie(new Vector2(1000, 200), MovementDirection.S);
+ var zombieToGo = mCreatureFactory.CreateZombie(new Vector2(400, 400), MovementDirection.S);
+ var knight = mCreatureFactory.CreateKnight(new Vector2(1500, 210), MovementDirection.W);
+ var horse = mCreatureFactory.CreateSkeletonHorse(new Vector2(1300, 500), MovementDirection.SW);
+ var prince = mCreatureFactory.CreatePrince(new Vector2(1800, 1000), MovementDirection.S);
+ var meatball = mCreatureFactory.CreateMeatball(new Vector2(350, 150), MovementDirection.S);
+ // Add creatures to obects manager.
+ mObjectsManager.CreatePlayerCharacter(necromancer);
+ mObjectsManager.CreatePrince(prince);
+ mObjectsManager.CreateCreature(zombie1);
+ mObjectsManager.CreateCreature(zombie2);
+ mObjectsManager.CreateCreature(zombie3);
+ mObjectsManager.CreateCreature(zombie4);
+ mObjectsManager.CreateCreature(zombie5);
+ mObjectsManager.CreateCreature(zombieToGo);
+ mObjectsManager.CreateCreature(knight);
+ mObjectsManager.CreateCreature(horse);
+ mObjectsManager.CreateCreature(meatball);
+#endif
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.Escape)
+ mMenuActions.OpenPauseScreen();
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.F5)
+ {
+ Rectangle cameraRectangle = mCameraManager.GetCamera().ScreenRectangle;
+ mEffectsManager.PlayOnce(new SmokeBig(), cameraRectangle.Center, cameraRectangle.Size);
+ mObjectsManager.ExposeTheLiving();
+ }
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.F6)
+ {
+ (mObjectsManager.PlayerCharacter as Necromancer)?.ChangeSex();
+ mEffectsManager.PlayOnce(new SmokeSmall(), mObjectsManager.PlayerCharacter.Position.ToPoint(), new Point(256));
+ }
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.F8)
+ {
+ mFogOfWarActivaded = !mFogOfWarActivaded;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Updates the status of this object.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ public override void Update(GameTime gameTime)
+ {
+#if DEBUG
+#else
+ try
+ {
+#endif
+ if (mObjectsManager.Boss != null && mObjectsManager.Boss.IsDead)
+ mAchievementsAndStatistics.mKillKing = true;
+ if (mObjectsManager.Prince != null && mObjectsManager.Prince.IsDead)
+ mAchievementsAndStatistics.mKillPrince = true;
+ mObjectsManager.Update(gameTime, mRightButtonPressed, mRightButtonPosition, mCameraManager.GetCamera());
+ mCameraManager.Update(mObjectsManager.PlayerCharacter);
+ mEffectsManager.Update(gameTime);
+ mAiPlayer.Update(gameTime);
+
+ // Call for Transformations
+ mTransformation.Transform();
+
+ // Check whether creature is in Necromancers Radius
+ mSelection.UpdateSelection();
+
+ // Update for FogOfWar.
+ for (int i = mFogCounter; i < mObjectsManager.UndeadCreatures.Count; i += 30)
+ {
+ mFog.Update(mObjectsManager.UndeadCreatures[i]);
+ }
+ if (mFogCounter < 30)
+ mFogCounter++;
+ else
+ mFogCounter = 0;
+
+#if DEBUG
+#else
+ }
+ catch (System.Exception e)
+ {
+ System.Console.WriteLine(e.Message);
+ }
+#endif
+ }
+
+ /// <summary>
+ /// Draws this object using the given sprite batch.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// this object</param>
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ mGraphicsDeviceManager.GraphicsDevice.Clear(Color.Black);
+ spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend,
+ null, null, null, null, mCameraManager.GetCamera().Transform);
+ mMapManager.DrawFloor(spriteBatch, mCameraManager.GetCamera());
+ mObjectsManager.Draw(spriteBatch, mCameraManager.GetCamera());
+ mTransformation.DrawNecroArea(spriteBatch);
+ mEffectsManager.Draw(spriteBatch);
+ // Draws the selection rectangle when left mouse button is pressed.
+ if (mLeftButtonPressed)
+ mSelection.Draw(spriteBatch, mInitialClickPosition + mCameraManager.GetCamera().Location.ToPoint(),
+ Mouse.GetState().Position + mCameraManager.GetCamera().Location.ToPoint());
+ if (mFogOfWarActivaded) mFog.DrawFog(spriteBatch);
+ if (mOptionsManager.Options.DebugMode == DebugMode.Full)
+ {
+ mMapManager.DrawPathfindingGrid(spriteBatch, mCameraManager.GetCamera());
+ mObjectsManager.DrawQuadtree(spriteBatch);
+ DrawLastSelection(spriteBatch, mSelection.LastSelection);
+ }
+ spriteBatch.End();
+ }
+
+ private void DrawLastSelection(SpriteBatch spriteBatch, Rectangle selection)
+ {
+ spriteBatch.Draw(mOnePixelTexture, selection, new Color(Color.Black, 100));
+ }
+
+ public void SetFog(List<Rectangle> fog)
+ {
+ mFog.SetFog(fog);
+ }
+
+ public List<Rectangle> GetFog()
+ {
+ return mFog.GetFog();
+ }
+
+ public void SetAiState(AiState state)
+ {
+ mAiPlayer.State = state;
+ }
+
+ public AiState GetAiState()
+ {
+ return mAiPlayer.State;
+ }
+ }
+}
diff --git a/V3/Screens/HudScreen.cs b/V3/Screens/HudScreen.cs
new file mode 100644
index 0000000..123ba8a
--- /dev/null
+++ b/V3/Screens/HudScreen.cs
@@ -0,0 +1,349 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using System.Collections.Generic;
+using V3.Data;
+using V3.Camera;
+using V3.Input;
+using V3.Map;
+using V3.Objects;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// Creates a new HUD screen.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class HudScreen : AbstractScreen, IInitializable
+ {
+ private readonly MenuActions mMenuActions;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly ContentManager mContentManager;
+ private readonly WidgetFactory mWidgetFactory;
+ private readonly IOptionsManager mOptionsManager;
+ private readonly CameraManager mCameraManager;
+ private readonly IMapManager mMapManager;
+ private readonly IObjectsManager mObjectsManager;
+ private AchievementsAndStatistics mAchievementsAndStatistics;
+
+ private KeyboardState mCurrentState;
+ private KeyboardState mPreviousState;
+ private SpriteFont mFont;
+
+ private List<Button> mButtons = new List<Button>();
+ private Button mPauseButton;
+ private Button mCameraButton;
+ private Button mButton1;
+ private Button mButton2;
+ private Button mButton3;
+ private Button mButton4;
+ private Button mButton5;
+
+ private Rectangle mLifeBarRectangle;
+ private Rectangle mBackgroundRectangle;
+ private Rectangle mUnitRectangle;
+ private Rectangle mUnitInformationRectangle;
+ private Rectangle mMiniMapRectangle;
+ private Rectangle mMiniMapCamera;
+
+ private Texture2D mRectangle;
+
+ private Point mRectanglePos;
+ private Point mRectangleSize;
+ private int mCurrentHealth;
+ private int mMaxHealth;
+ private int mCounter;
+
+ private Vector2 mCreatureNamePosition;
+ private readonly Selection mSelection;
+ private int mScaleFactorX;
+ private int mScaleFactorY;
+
+ public HudScreen(ContentManager contentManager, GraphicsDeviceManager graphicsDeviceManager,
+ IOptionsManager optionsManager, MenuActions menuActions,
+ WidgetFactory widgetFactory, Selection selection, CameraManager cameraManager,
+ IMapManager mapManager, IObjectsManager objectsManager,
+ AchievementsAndStatistics achievementsAndStatistics): base(true, true)
+ {
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mContentManager = contentManager;
+ mOptionsManager = optionsManager;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ mSelection = selection;
+ mCameraManager = cameraManager;
+ mMapManager = mapManager;
+ mObjectsManager = objectsManager;
+ mAchievementsAndStatistics = achievementsAndStatistics;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ mFont = mContentManager.Load<SpriteFont>("Fonts/UnitFont");
+
+ mPauseButton = CreateButton("Buttons/Button-06",
+ "Pause",
+ "Pausiert das Spiel und \n" +
+ "öffnet das Pausemenü.");
+ mCameraButton = CreateButton("Buttons/Button-07",
+ "Kamera",
+ "Ändert die Kameraeinstellung zu \n" +
+ "zentrierter oder schiebender Kamera. ");
+ mButton1 = CreateButton("Buttons/Button-01",
+ "Explosion",
+ "Lasst den Fleischklops explodieren \n" +
+ "und fügt im Umkreis Schaden zu.");
+ mButton2 = CreateButton("Buttons/Button-02",
+ "Zombie beschwören",
+ "Wiederbelebt alle toten Gegner im Umkreis \n" +
+ "und lasst sie für euch kämpfen.");
+ mButton3 = CreateButton("Buttons/Button-03",
+ "Zombies verwandeln",
+ "Verschmelzt 5 ausgewählte Zombies \n" +
+ "zu einem Fleischklops.");
+ mButton4 = CreateButton("Buttons/Button-04",
+ "Skelett erschaffen",
+ "Verwandelt einen Zombie zu einem \n" +
+ "Skelett.");
+ mButton5 = CreateButton("Buttons/Button-05",
+ "Totenpferd beschwören",
+ "Vereinigt 3 Skelette zu einem \n" +
+ "Totenpferd.");
+
+ if (mObjectsManager.PlayerCharacter != null)
+ {
+ mCurrentHealth = mObjectsManager.PlayerCharacter.Life;
+ mMaxHealth = mObjectsManager.PlayerCharacter.MaxLife;
+ }
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ mButtons.ForEach(b => b.HandleMouseEvent(mouseEvent));
+ if (mMiniMapRectangle.Contains(mouseEvent.PositionPressed))
+ {
+ return true;
+ }
+ foreach (var button in mButtons)
+ {
+ if (button.Rectangle.Contains(mouseEvent.PositionPressed))
+ return true;
+ if (mouseEvent.PositionReleased.HasValue && button.Rectangle.Contains(mouseEvent.PositionReleased.Value))
+ return true;
+ }
+
+ return false;
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ var viewport = mGraphicsDeviceManager.GraphicsDevice.Viewport;
+ mRectanglePos = new Point(viewport.Width / 2 - viewport.Width / 8, viewport.Height - viewport.Height / 20 - 2);
+ mRectangleSize = new Point(viewport.Width / 4, viewport.Height / 20);
+ mCurrentHealth = mObjectsManager.PlayerCharacter.Life;
+
+ mLifeBarRectangle = new Rectangle(mRectanglePos.X, mRectanglePos.Y, mRectangleSize.X * mCurrentHealth / mMaxHealth, mRectangleSize.Y);
+ mBackgroundRectangle = new Rectangle(mRectanglePos - new Point(2, 2), mRectangleSize + new Point(4, 4));
+ mUnitInformationRectangle = new Rectangle(viewport.Width * 2/3, viewport.Height * 7/8, viewport.Width / 3, viewport.Height / 8);
+ mUnitRectangle = new Rectangle(mUnitInformationRectangle.X + mUnitInformationRectangle.Width * 2 / 3, mUnitInformationRectangle.Y + 5, mUnitInformationRectangle.Width / 3 - 15, mUnitInformationRectangle.Height - 10);
+ mCreatureNamePosition = new Vector2(mUnitInformationRectangle.X + mUnitInformationRectangle.Width / 20, mUnitInformationRectangle.Y + mUnitInformationRectangle.Height / 20);
+
+ // Logic for camera toggle
+ mPreviousState = mCurrentState;
+ mCurrentState = Keyboard.GetState();
+
+ if (mCameraButton.IsClicked || (mCurrentState.IsKeyDown(Keys.C) && mPreviousState.IsKeyUp(Keys.C)))
+ {
+ if (mOptionsManager.Options.CameraType == CameraType.Centered)
+ {
+ mOptionsManager.Options.CameraType = CameraType.Scrolling;
+ }
+ else if (mOptionsManager.Options.CameraType == CameraType.Scrolling)
+ {
+ mOptionsManager.Options.CameraType = CameraType.Centered;
+ }
+ mMenuActions.SaveOptions();
+ mMenuActions.ApplyOptions();
+ }
+
+ // Logic for opening the death screen if the player's life reaches 0 or the victory screen if the boss is defeated.
+ if (mObjectsManager.PlayerCharacter.IsDead)
+ {
+ mCounter++;
+ if (mCounter == 120)
+ {
+ mMenuActions.OpenDeathScreen();
+ mCounter = 0;
+ }
+ }
+
+ else if (mObjectsManager.Boss != null && mObjectsManager.Boss.IsDead)
+ {
+ mCounter++;
+ if (mCounter == 120)
+ {
+ mAchievementsAndStatistics.mUsedTime -= TimeSpan.FromSeconds(2);
+ if (gameTime.TotalGameTime <= TimeSpan.FromMinutes(5))
+ mAchievementsAndStatistics.mHellsNotWaiting = true;
+ mMenuActions.OpenVictoryScreen();
+ mCounter = 0;
+ }
+ }
+
+ // Define the Minimap into the upper right corner and the scale factors needed.
+
+ mMiniMapRectangle = new Rectangle(viewport.Width * 3 / 4, 0, viewport.Width * 1 / 4, viewport.Height * 1 / 4);
+ mScaleFactorX = mCameraManager.GetCamera().MapPixelWidth / mMiniMapRectangle.Width;
+ mScaleFactorY = mCameraManager.GetCamera().MapPixelHeight / 2 / mMiniMapRectangle.Height;
+ mMiniMapCamera = new Rectangle(mMiniMapRectangle.X + (int)mCameraManager.GetCamera().Location.X / mScaleFactorX,
+ mMiniMapRectangle.Y + (int)mCameraManager.GetCamera().Location.Y / mScaleFactorY,
+ viewport.Width / mScaleFactorX, viewport.Height / mScaleFactorY);
+
+ if (mMiniMapRectangle.Contains(Mouse.GetState().Position))
+ {
+ if (Mouse.GetState().LeftButton == ButtonState.Pressed)
+ {
+ float newCameraLocationX = MathHelper.Clamp(((float) Mouse.GetState().Position.X - mMiniMapRectangle.X) * mScaleFactorX -
+ mMiniMapCamera.Width / 2f * mScaleFactorX, 0, mCameraManager.GetCamera().MapPixelWidth - viewport.Width);
+ float newCameraLocationY = MathHelper.Clamp(((float) Mouse.GetState().Position.Y - mMiniMapRectangle.Y) * mScaleFactorY -
+ mMiniMapCamera.Height / 2f * mScaleFactorY, 0, mCameraManager.GetCamera().MapPixelHeight / 2f - viewport.Height);
+ mCameraManager.GetCamera().Location = new Vector2(newCameraLocationX, newCameraLocationY);
+ }
+ }
+
+ // Call for transformations
+ if (mButton1.IsClicked)
+ mSelection.Specialattack();
+ if (mButton2.IsClicked)
+ mSelection.TransformZombie();
+ if (mButton3.IsClicked)
+ mSelection.TransformMeatball();
+ if (mButton4.IsClicked)
+ mSelection.TransformSkeleton();
+ if (mButton5.IsClicked)
+ mSelection.TransformSkeletonHorse();
+
+ UpdateButtons();
+
+ foreach (var unit in mSelection.SelectedCreatures)
+ {
+ if (unit is Meatball)
+ mAchievementsAndStatistics.mMeatballCompany += 1;
+ else if (unit is SkeletonHorse)
+ mAchievementsAndStatistics.mSkeletonHorseCavalry += 1;
+ }
+
+ mAchievementsAndStatistics.mWalkedDistance += ((Necromancer)mObjectsManager.PlayerCharacter).WalkedPixels / 64;
+ mAchievementsAndStatistics.mMarathonRunner += ((Necromancer)mObjectsManager.PlayerCharacter).WalkedPixels / 64;
+ mAchievementsAndStatistics.mIronMan += ((Necromancer)mObjectsManager.PlayerCharacter).WalkedPixels / 64;
+ mAchievementsAndStatistics.mUsedTime += gameTime.ElapsedGameTime;
+ }
+
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ spriteBatch.Begin();
+ mButtons.ForEach(b => b.Draw(spriteBatch));
+ spriteBatch.Draw(mRectangle, mBackgroundRectangle, Color.Black * 0.7f);
+ spriteBatch.Draw(mRectangle, mLifeBarRectangle, Color.Firebrick);
+ spriteBatch.Draw(mRectangle, mUnitInformationRectangle, Color.Chocolate * 0.7f);
+ spriteBatch.Draw(mRectangle, mUnitRectangle, Color.Gainsboro * 0.6f);
+
+ // Draw the minimap.
+ mMapManager.DrawMinimap(spriteBatch, mMiniMapRectangle);
+
+ // Draw a rectangle on the part of the MiniMap that is shown by the camera.
+ // Upper line
+ spriteBatch.Draw(mRectangle, new Rectangle(mMiniMapCamera.X, mMiniMapCamera.Y, mMiniMapCamera.Width, 1), Color.White);
+ // Left line
+ spriteBatch.Draw(mRectangle, new Rectangle(mMiniMapCamera.X, mMiniMapCamera.Y, 1, mMiniMapCamera.Height), Color.White);
+ // Right line
+ spriteBatch.Draw(mRectangle, new Rectangle(mMiniMapCamera.X + mMiniMapCamera.Width, mMiniMapCamera.Y, 1, mMiniMapCamera.Height), Color.White);
+ // Bottom line
+ spriteBatch.Draw(mRectangle, new Rectangle(mMiniMapCamera.X, mMiniMapCamera.Y + mMiniMapCamera.Height, mMiniMapCamera.Width, 1), Color.White);
+
+ foreach (var creature in mSelection.SelectedCreatures)
+ {
+ if (creature.IsSelected && mSelection.SelectedCreatures.Count == 1)
+ {
+ spriteBatch.DrawString(mFont, creature.Name, mCreatureNamePosition, Color.Black);
+ spriteBatch.DrawString(mFont,
+ "Leben: " + creature.Life,
+ mCreatureNamePosition + new Vector2(0, 20),
+ Color.Black);
+ spriteBatch.DrawString(mFont,
+ "Kraft: " + creature.Attack,
+ mCreatureNamePosition + new Vector2(0, 40),
+ Color.Black);
+ creature.DrawStatic(spriteBatch,
+ new Point(mUnitRectangle.X + mUnitRectangle.Width / 2, mUnitRectangle.Y + mUnitRectangle.Height * 3/4));
+ }
+ else if (mSelection.SelectedCreatures.Count > 1)
+ {
+ var showedCreature = mSelection.SelectedCreatures[0];
+ spriteBatch.DrawString(mFont, showedCreature.Name, mCreatureNamePosition, Color.Black);
+ spriteBatch.DrawString(mFont,
+ "Leben: " + showedCreature.Life,
+ mCreatureNamePosition + new Vector2(0, 20),
+ Color.Black);
+ spriteBatch.DrawString(mFont,
+ "Kraft: " + showedCreature.Attack,
+ mCreatureNamePosition + new Vector2(0, 40),
+ Color.Black);
+ showedCreature.DrawStatic(spriteBatch,
+ new Point(mUnitRectangle.X + mUnitRectangle.Width / 2, mUnitRectangle.Y + mUnitRectangle.Height * 3 / 4));
+ }
+ }
+ spriteBatch.End();
+ }
+
+ private void UpdateButtons()
+ {
+ var viewport = mGraphicsDeviceManager.GraphicsDevice.Viewport;
+ // Button Size
+ var buttonLength = viewport.Height / 12;
+ var buttonSize = new Vector2(buttonLength, buttonLength);
+ mButtons.ForEach(b => b.Size = buttonSize);
+
+ // Rectangles for the buttons
+ var buttonXVector = new Vector2(buttonLength + 2, 0);
+ mPauseButton.Position = new Vector2(5, 5);
+ mCameraButton.Position = new Vector2(mPauseButton.Position.X + mPauseButton.Size.X + 5, 5);
+ mButton1.Position = new Vector2(2, viewport.Height - buttonLength);
+ mButton2.Position = mButton1.Position + buttonXVector;
+ mButton3.Position = mButton2.Position + buttonXVector;
+ mButton4.Position = mButton3.Position + buttonXVector;
+ mButton5.Position = mButton4.Position + buttonXVector;
+
+ if (mPauseButton.IsClicked)
+ mMenuActions.OpenPauseScreen();
+
+ var mousePosition = Mouse.GetState().Position;
+ mButtons.ForEach(b => b.IsSelected = b.CheckSelected(mousePosition));
+ mButtons.ForEach(b => b.IsClicked = false);
+ }
+
+ private Button CreateButton(string textureName, string tooltipTitle, string tooltip)
+ {
+ return CreateButton(mContentManager.Load<Texture2D>(textureName), tooltipTitle, tooltip);
+ }
+
+ private Button CreateButton(Texture2D texture, string tooltipTitle, string tooltip)
+ {
+ var button = mWidgetFactory.CreateButton("");
+ button.Position = Vector2.Zero;
+ button.PaddingX = 15;
+ button.PaddingY = 5;
+ button.Size = button.GetMinimumSize();
+ button.Image = texture;
+ button.Tooltip = tooltip;
+ button.TooltipTitle = tooltipTitle;
+ mButtons.Add(button);
+ return button;
+ }
+ }
+}
diff --git a/V3/Screens/IDrawable.cs b/V3/Screens/IDrawable.cs
new file mode 100644
index 0000000..b940d94
--- /dev/null
+++ b/V3/Screens/IDrawable.cs
@@ -0,0 +1,19 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// An object that can be drawn using a sprite batch.
+ /// </summary>
+ public interface IDrawable
+ {
+ /// <summary>
+ /// Draws this object using the given sprite batch.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// this object</param>
+ void Draw(GameTime gameTime, SpriteBatch spriteBatch);
+ }
+}
diff --git a/V3/Screens/IScreen.cs b/V3/Screens/IScreen.cs
new file mode 100644
index 0000000..197aaec
--- /dev/null
+++ b/V3/Screens/IScreen.cs
@@ -0,0 +1,39 @@
+using V3.Input;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// A screen that can be handled by a screen manager and that provides
+ /// information on how to deal with other screens below this one.
+ /// </summary>
+ public interface IScreen : IDrawable, IUpdateable
+ {
+ /// <summary>
+ /// Indicates whether screens below this one should be updated.
+ /// </summary>
+ bool UpdateLower { get; }
+
+ /// <summary>
+ /// Indicates whether screens below this one should be drawn.
+ /// </summary>
+ bool DrawLower { get; }
+
+ /// <summary>
+ /// Handles the given key event and returns whether it should be passed
+ /// to the screens below this one.
+ /// </summary>
+ /// <param name="keyEvent">the key event that occurred</param>
+ /// <returns>true if the event has been handeled by this screen and
+ /// should not be passed to the lower screens, false otherwise</returns>
+ bool HandleKeyEvent(IKeyEvent keyEvent);
+
+ /// <summary>
+ /// Handles the given mouse event and returns whether it should be passed
+ /// to the screens below this one.
+ /// </summary>
+ /// <param name="mouseEvent">the mouse event that occurred</param>
+ /// <returns>true if the event has been handeled by this screen and
+ /// should not be passed to the lower screens, false otherwise</returns>
+ bool HandleMouseEvent(IMouseEvent mouseEvent);
+ }
+}
diff --git a/V3/Screens/IScreenFactory.cs b/V3/Screens/IScreenFactory.cs
new file mode 100644
index 0000000..a2d844b
--- /dev/null
+++ b/V3/Screens/IScreenFactory.cs
@@ -0,0 +1,27 @@
+namespace V3.Screens
+{
+ public interface IScreenFactory
+ {
+ GameScreen CreateGameScreen();
+
+ HudScreen CreateHudScreen();
+
+ LoadScreen CreateLoadScreen();
+
+ MainScreen CreateMainScreen();
+
+ PauseScreen CreatePauseScreen();
+
+ OptionsScreen CreateOptionsScreen();
+
+ DeathScreen CreateDeathScreen();
+
+ VictoryScreen CreateVictoryScreen();
+
+ TechdemoScreen CreaTechdemoScreen();
+
+ StatisticsScreen CreateStatisticsScreen();
+
+ AchievementsScreen CreateAchievementsScreen();
+ }
+}
diff --git a/V3/Screens/IScreenManager.cs b/V3/Screens/IScreenManager.cs
new file mode 100644
index 0000000..e61c49c
--- /dev/null
+++ b/V3/Screens/IScreenManager.cs
@@ -0,0 +1,35 @@
+namespace V3.Screens
+{
+ /// <summary>
+ /// Handles screens using a screen stack. You can add screens to the
+ /// foreground using the AddScreen method, remove screens from the
+ /// foreground using the RemoveScreen method and remove all screens using
+ /// the Clear method. The top screen is always drawn and updated, and
+ /// it can decide whether lower screens should be drawn and/or updated
+ /// too. The screens are updated from top to bottom, and drawn from
+ /// bottom to top.
+ /// </summary>
+ public interface IScreenManager : IDrawable, IUpdateable
+ {
+ /// <summary>
+ /// Adds a screen to the foreground.
+ /// </summary>
+ /// <param name="screen">the screen to add in the foreground</param>
+ void AddScreen(IScreen screen);
+
+ /// <summary>
+ /// Removes the given screen if it is on the top of the screen stack.
+ /// </summary>
+ /// <param name="screen">the screen to remove</param>
+ /// <returns>true if the screen was on top and has been removed,
+ /// false otherwise</returns>
+ void RemoveScreen(IScreen screen);
+
+ /// <summary>
+ /// Clears the screen stack.
+ /// </summary>
+ void Clear();
+
+ GameScreen GetGameScreen();
+ }
+}
diff --git a/V3/Screens/IUpdatable.cs b/V3/Screens/IUpdatable.cs
new file mode 100644
index 0000000..61972e7
--- /dev/null
+++ b/V3/Screens/IUpdatable.cs
@@ -0,0 +1,16 @@
+using Microsoft.Xna.Framework;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// An object that can be updated.
+ /// </summary>
+ public interface IUpdateable
+ {
+ /// <summary>
+ /// Updates the status of this object.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ void Update(GameTime gameTime);
+ }
+}
diff --git a/V3/Screens/LoadScreen.cs b/V3/Screens/LoadScreen.cs
new file mode 100644
index 0000000..0e58147
--- /dev/null
+++ b/V3/Screens/LoadScreen.cs
@@ -0,0 +1,124 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using System.Collections.Generic;
+using V3.Data;
+using V3.Input;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// The screen for the load menu.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class LoadScreen : AbstractScreen, IInitializable
+ {
+ private readonly ContentManager mContentManager;
+ private readonly IMenu mMenu;
+ private readonly ISaveGameManager mSaveGameManager;
+ private readonly MenuActions mMenuActions;
+ private readonly WidgetFactory mWidgetFactory;
+
+ private Texture2D mRectangle;
+ private Button mButtonBack;
+ private SelectButton mButtonSaveGame;
+ private Button mButtonLoad;
+ private List<ISaveGame> mSaveGames;
+
+ /// <summary>
+ /// Creates a new load screen.
+ /// </summary>
+ public LoadScreen(ContentManager contentManager, FormMenu menu,
+ ISaveGameManager saveGameManager, MenuActions menuActions,
+ WidgetFactory widgetFactory)
+ : base(false, true)
+ {
+ mContentManager = contentManager;
+ mMenu = menu;
+ mSaveGameManager = saveGameManager;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+
+ mButtonBack = mWidgetFactory.CreateButton("Zurück");
+ mButtonLoad = mWidgetFactory.CreateButton("Laden");
+ mButtonSaveGame = mWidgetFactory.CreateSelectButton();
+ mSaveGames = mSaveGameManager.GetSaveGames();
+ foreach (var saveGame in mSaveGames)
+ {
+ mButtonSaveGame.Values.Add(GetSaveGameString(saveGame));
+ }
+ mButtonSaveGame.SelectedIndex = mSaveGames.Count - 1;
+
+ mMenu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ mMenu.Widgets.Add(mButtonBack);
+ mMenu.Widgets.Add(mWidgetFactory.CreateLabel("Spielstand"));
+ mMenu.Widgets.Add(mButtonSaveGame);
+ mMenu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ mMenu.Widgets.Add(mButtonLoad);
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.Escape)
+ mMenuActions.Close(this);
+
+ return true;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ mMenu.HandleMouseEvent(mouseEvent);
+ return true;
+ }
+
+ /// <summary>
+ /// Draws this object using the given sprite batch.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// this object</param>
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ var backgroundRectangle = new Rectangle((int)mMenu.Position.X,
+ (int)mMenu.Position.Y, (int)mMenu.Size.X, (int)mMenu.Size.Y);
+ backgroundRectangle.X -= 30;
+ backgroundRectangle.Y -= 30;
+ backgroundRectangle.Width += 60;
+ backgroundRectangle.Height += 60;
+
+ spriteBatch.Begin();
+ spriteBatch.Draw(mRectangle, backgroundRectangle, Color.LightGray);
+ spriteBatch.End();
+
+ mMenu.Draw(spriteBatch);
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ if (mButtonBack.IsClicked)
+ {
+ mMenuActions.Close(this);
+ }
+ else if (mButtonLoad.IsClicked)
+ {
+ var saveGame = mSaveGames[mButtonSaveGame.SelectedIndex];
+ mMenuActions.LoadGame(saveGame);
+ }
+
+ mMenu.Update();
+ }
+
+ private static string GetSaveGameString(ISaveGame saveGame)
+ {
+ return saveGame.Timestamp.ToString("s");
+ }
+ }
+}
diff --git a/V3/Screens/MainScreen.cs b/V3/Screens/MainScreen.cs
new file mode 100644
index 0000000..b7932bf
--- /dev/null
+++ b/V3/Screens/MainScreen.cs
@@ -0,0 +1,195 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Ninject;
+using V3.Input;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// The screen for the main menu.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public class MainScreen : AbstractScreen, IInitializable
+ {
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly IMenu mMenu;
+ private readonly MenuActions mMenuActions;
+ private readonly WidgetFactory mWidgetFactory;
+ private readonly ContentManager mContentManager;
+
+ private Button mButtonStart;
+ private Button mButtonLoad;
+ private Button mButtonOptions;
+ private Button mButtonTechDemo;
+ private Button mButtonExit;
+ private Button mButtonStatistics;
+ private Button mButtonAchievements;
+
+ private Texture2D mTitleImage;
+ private Texture2D mBackgroundImage;
+ private Vector2 mImagePosition;
+ private Random mRandom;
+ private Vector2 mImageScrollDirection;
+ private Rectangle TitleImageBounds => mTitleImage.Bounds;
+ private Rectangle BackgroundImageBounds => mBackgroundImage.Bounds;
+ private Rectangle ScreenBounds => mGraphicsDeviceManager.GraphicsDevice.Viewport.Bounds;
+
+ /// <summary>
+ /// Creates a new main screen.
+ /// </summary>
+ public MainScreen(GraphicsDeviceManager graphicsDeviceManager,
+ VerticalMenu menu, MenuActions menuActions,
+ WidgetFactory widgetFactory, ContentManager contentManager) : base(false, false)
+ {
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mMenu = menu;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ mContentManager = contentManager;
+ }
+
+ public void Initialize()
+ {
+ mMenuActions.ApplyOptions();
+
+ mButtonStart = mWidgetFactory.CreateButton("Neues Spiel");
+ mButtonLoad = mWidgetFactory.CreateButton("Spiel laden");
+ mButtonLoad.IsEnabled = mMenuActions.CanLoadGame();
+ mButtonOptions = mWidgetFactory.CreateButton("Optionen");
+ mButtonStatistics = mWidgetFactory.CreateButton("Statistiken");
+ mButtonAchievements = mWidgetFactory.CreateButton("Erfolge");
+ mButtonTechDemo = mWidgetFactory.CreateButton("Tech-Demo");
+ mButtonExit = mWidgetFactory.CreateButton("Spiel schließen");
+
+ mMenu.Widgets.Add(mButtonStart);
+ mMenu.Widgets.Add(mButtonLoad);
+ mMenu.Widgets.Add(mButtonOptions);
+ mMenu.Widgets.Add(mButtonStatistics);
+ mMenu.Widgets.Add(mButtonAchievements);
+ mMenu.Widgets.Add(mButtonTechDemo);
+ mMenu.Widgets.Add(mButtonExit);
+
+ mTitleImage = mContentManager.Load<Texture2D>("Menu/Titel");
+ mBackgroundImage = mContentManager.Load<Texture2D>("Menu/mainscreen");
+ mRandom = new Random();
+ mImagePosition = RandomPosition();
+ mImageScrollDirection = RandomScrollDirection();
+ mButtonStart.BackgroundColor = Color.Red;
+ mButtonLoad.BackgroundColor = Color.Red;
+ mButtonOptions.BackgroundColor = Color.Red;
+ mButtonStatistics.BackgroundColor = Color.Red;
+ mButtonAchievements.BackgroundColor = Color.Red;
+ mButtonTechDemo.BackgroundColor = Color.Red;
+ mButtonExit.BackgroundColor = Color.Red;
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ return true;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ mMenu.HandleMouseEvent(mouseEvent);
+ return true;
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ if (mButtonStart.IsClicked)
+ mMenuActions.StartNewGame();
+ else if (mButtonLoad.IsClicked)
+ mMenuActions.OpenLoadScreen();
+ else if (mButtonOptions.IsClicked)
+ mMenuActions.OpenOptionsScreen();
+ else if (mButtonTechDemo.IsClicked)
+ mMenuActions.OpenTechDemo();
+ else if (mButtonStatistics.IsClicked)
+ mMenuActions.OpenStatisticsScreen();
+ else if (mButtonAchievements.IsClicked)
+ mMenuActions.OpenAchievementsScreen();
+ else if (mButtonExit.IsClicked)
+ mMenuActions.Exit();
+
+ mMenu.Update();
+ UpdateBackgroundPosition();
+ }
+
+ /// <summary>
+ /// Draws this object using the given sprite batch.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// this object</param>
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ spriteBatch.Begin();
+ spriteBatch.Draw(mBackgroundImage, mImagePosition, Color.White);
+ Point titleSize = new Point(ScreenBounds.Width * 9 / 10, ScreenBounds.Width * TitleImageBounds.Height * 9 / 10 / TitleImageBounds.Width);
+ Point titlePosition = new Point(ScreenBounds.Width / 2 - titleSize.X / 2, ScreenBounds.Height / 20);
+ spriteBatch.Draw(mTitleImage,
+ new Rectangle(titlePosition, titleSize),
+ Color.White);
+ spriteBatch.End();
+
+ mMenu.Draw(spriteBatch);
+ }
+
+ /// <summary>
+ /// Calculate a random section of the background image to show.
+ /// If the screen is larger than the image, just draw it to the upper left corner.
+ /// </summary>
+ /// <returns>A random Vector2 fitting the image to the screen.</returns>
+ private Vector2 RandomPosition()
+ {
+ Vector2 position;
+ try
+ {
+ var randomX = mRandom.Next(-BackgroundImageBounds.Width + ScreenBounds.Width, 0);
+ var randomY = mRandom.Next(-BackgroundImageBounds.Height + ScreenBounds.Height, 0);
+ position = new Vector2(randomX, randomY);
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ position = Vector2.Zero;
+ }
+ return position;
+ }
+
+ private Vector2 RandomScrollDirection()
+ {
+ var imageScrollDirection = new Vector2((float)mRandom.NextDouble(), (float)mRandom.NextDouble());
+ imageScrollDirection.Normalize();
+ switch (mRandom.Next(4))
+ {
+ case 0:
+ return imageScrollDirection;
+ case 1:
+ return new Vector2(imageScrollDirection.X, -imageScrollDirection.Y);
+ case 2:
+ return new Vector2(-imageScrollDirection.X, imageScrollDirection.Y);
+ case 3:
+ return new Vector2(-imageScrollDirection.X, -imageScrollDirection.Y);
+ default:
+ return imageScrollDirection;
+ }
+ }
+
+ private void UpdateBackgroundPosition()
+ {
+ if (mImagePosition.X < 0 && mImagePosition.X > -BackgroundImageBounds.Width + ScreenBounds.Width
+ && mImagePosition.Y < 0 && mImagePosition.Y > -BackgroundImageBounds.Height + ScreenBounds.Height)
+ {
+ mImagePosition += mImageScrollDirection;
+ }
+ else
+ {
+ mImagePosition = RandomPosition();
+ mImageScrollDirection = RandomScrollDirection();
+ }
+ }
+ }
+}
diff --git a/V3/Screens/MenuActions.cs b/V3/Screens/MenuActions.cs
new file mode 100644
index 0000000..6de9e15
--- /dev/null
+++ b/V3/Screens/MenuActions.cs
@@ -0,0 +1,208 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Media;
+using V3.Camera;
+using V3.Data;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// Provides common actions for the main and pause menu, like saving and
+ /// loading the game or removing and adding a screen to the screen stack.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class MenuActions
+ {
+ private readonly Game mGame;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly IGameStateManager mGameStateManager;
+ private readonly IOptionsManager mOptionsManager;
+ private readonly ISaveGameManager mSaveGameManager;
+ private readonly IScreenManager mScreenManager;
+ private readonly IScreenFactory mScreenFactory;
+ private readonly CameraManager mCameraManager;
+
+ /// <summary>
+ /// Creates a new menu actions instance.
+ /// </summary>
+ public MenuActions(Game game, GraphicsDeviceManager graphicsDeviceManager,
+ IGameStateManager gameStateManager, IOptionsManager optionsManager,
+ ISaveGameManager saveGameManager, IScreenManager screenManager,
+ IScreenFactory screenFactory, CameraManager cameraManager)
+ {
+ mGame = game;
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mGameStateManager = gameStateManager;
+ mOptionsManager = optionsManager;
+ mSaveGameManager = saveGameManager;
+ mScreenManager = screenManager;
+ mScreenFactory = screenFactory;
+ mCameraManager = cameraManager;
+ }
+
+ /// <summary>
+ /// Checks whether it is possible to load a game, i. e. whether there
+ /// are save games.
+ /// </summary>
+ /// <returns>true if there are save games to load, false otherwise
+ /// </returns>
+ public bool CanLoadGame()
+ {
+ return mSaveGameManager.GetSaveGames().Count > 0;
+ }
+
+ /// <summary>
+ /// Removes the given screen from the screen manager.
+ /// </summary>
+ /// <param name="screen">the screen to remove</param>
+ public void Close(IScreen screen)
+ {
+ // TODO: something like RemoveUntil
+ mScreenManager.RemoveScreen(screen);
+ }
+
+ /// <summary>
+ /// Quits the game.
+ /// </summary>
+ public void Exit()
+ {
+ mGame.Exit();
+ }
+
+ public void LoadGame(ISaveGame saveGame)
+ {
+ mScreenManager.Clear();
+ var gameScreen = mScreenFactory.CreateGameScreen();
+ var gameState = saveGame.GameState;
+ mGameStateManager.LoadGameState(gameState);
+ gameScreen.SetFog(gameState.mFog);
+ gameScreen.SetAiState(gameState.mAiState);
+ mScreenManager.AddScreen(gameScreen);
+ mScreenManager.AddScreen(mScreenFactory.CreateHudScreen());
+ }
+
+ /// <summary>
+ /// Opens the main screen and removes all other screens.
+ /// </summary>
+ public void OpenMainScreen()
+ {
+ mScreenManager.Clear();
+ mScreenManager.AddScreen(mScreenFactory.CreateMainScreen());
+ }
+
+ /// <summary>
+ /// Opens the options screen in the front.
+ /// </summary>
+ public void OpenOptionsScreen()
+ {
+ mScreenManager.AddScreen(mScreenFactory.CreateOptionsScreen());
+ }
+
+ public void OpenLoadScreen()
+ {
+ mScreenManager.AddScreen(mScreenFactory.CreateLoadScreen());
+ }
+
+ /// <summary>
+ /// Opens the pause screen in the front.
+ /// </summary>
+ public void OpenPauseScreen()
+ {
+ mScreenManager.AddScreen(mScreenFactory.CreatePauseScreen());
+ }
+
+ /// <summary>
+ /// Opens the death screen in the front.
+ /// </summary>
+ public void OpenDeathScreen()
+ {
+ mScreenManager.AddScreen(mScreenFactory.CreateDeathScreen());
+ }
+
+ /// <summary>
+ /// Opens the victory screen in the front.
+ /// </summary>
+ public void OpenVictoryScreen()
+ {
+ mScreenManager.AddScreen(mScreenFactory.CreateVictoryScreen());
+ }
+
+ /// <summary>
+ /// Opens the statistics screen in the front.
+ /// </summary>
+ public void OpenStatisticsScreen()
+ {
+ mScreenManager.AddScreen(mScreenFactory.CreateStatisticsScreen());
+ }
+
+ /// <summary>
+ /// Opens the achievements screen in the front.
+ /// </summary>
+ public void OpenAchievementsScreen()
+ {
+ mScreenManager.AddScreen(mScreenFactory.CreateAchievementsScreen());
+ }
+
+ /// <summary>
+ /// Opens the tech demo.
+ /// </summary>
+ public void OpenTechDemo()
+ {
+ mScreenManager.Clear();
+ mScreenManager.AddScreen(mScreenFactory.CreaTechdemoScreen());
+ mScreenManager.AddScreen(mScreenFactory.CreateHudScreen());
+ }
+
+ /// <summary>
+ /// Creates a new save game from the current game state.
+ /// </summary>
+ public void SaveGame()
+ {
+ var gameState = mGameStateManager.GetGameState();
+ var gameScreen = mScreenManager.GetGameScreen();
+ if (gameScreen != null)
+ {
+ gameState.mFog = gameScreen.GetFog();
+ gameState.mAiState = gameScreen.GetAiState();
+ }
+ mSaveGameManager.CreateSaveGame(gameState);
+ }
+
+ /// <summary>
+ /// Apply the current graphics options.
+ /// </summary>
+ public void ApplyOptions()
+ {
+ mGraphicsDeviceManager.PreferredBackBufferWidth = mOptionsManager.Options.Resolution.X;
+ mGraphicsDeviceManager.PreferredBackBufferHeight = mOptionsManager.Options.Resolution.Y;
+ mGraphicsDeviceManager.IsFullScreen = mOptionsManager.Options.IsFullScreen;
+ mGraphicsDeviceManager.ApplyChanges();
+
+#if NO_AUDIO
+#else
+ MediaPlayer.Volume = mOptionsManager.Options.GetEffectiveVolume();
+#endif
+ }
+
+ /// <summary>
+ /// Save the current graphics options.
+ /// </summary>
+ public void SaveOptions()
+ {
+ mOptionsManager.SaveOptions();
+ }
+
+ /// <summary>
+ /// Opens the game screen with a new game.
+ /// </summary>
+ public void StartNewGame()
+ {
+ mScreenManager.Clear();
+ var gameScreen = mScreenFactory.CreateGameScreen();
+ gameScreen.CreateInitialPopulation();
+ mScreenManager.AddScreen(gameScreen);
+ mScreenManager.AddScreen(mScreenFactory.CreateHudScreen());
+ if (mCameraManager.GetCamera() is CameraScrolling)
+ mCameraManager.GetCamera().Location = Vector2.Zero;
+ }
+ }
+}
diff --git a/V3/Screens/OptionsScreen.cs b/V3/Screens/OptionsScreen.cs
new file mode 100644
index 0000000..b23e1fe
--- /dev/null
+++ b/V3/Screens/OptionsScreen.cs
@@ -0,0 +1,197 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using V3.Camera;
+using V3.Data;
+using V3.Input;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// The screen for the options menu.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class OptionsScreen : AbstractScreen, IInitializable
+ {
+ private readonly ContentManager mContentManager;
+ private readonly IMenu mMenu;
+ private readonly IOptionsManager mOptionsManager;
+ private readonly MenuActions mMenuActions;
+ private readonly WidgetFactory mWidgetFactory;
+
+ private Texture2D mRectangle;
+ private Button mButtonBack;
+ private SelectButton mButtonSize;
+ private SelectButton mButtonFullscreen;
+ private SelectButton mButtonCamera;
+ private SelectButton mButtonDebug;
+ private SelectButton mButtonMute;
+ private SelectButton mButtonVolume;
+ private Button mButtonApply;
+
+ /// <summary>
+ /// Creates a new options screen.
+ /// </summary>
+ public OptionsScreen(ContentManager contentManager, FormMenu menu,
+ IOptionsManager optionsManager, MenuActions menuActions,
+ WidgetFactory widgetFactory)
+ : base(false, true)
+ {
+ mContentManager = contentManager;
+ mMenu = menu;
+ mOptionsManager = optionsManager;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+
+ mButtonBack = mWidgetFactory.CreateButton("Zurück");
+ mButtonApply = mWidgetFactory.CreateButton("Bestätigen");
+ mButtonSize = mWidgetFactory.CreateSelectButton();
+ foreach (var resolution in Options.Resolutions)
+ {
+ mButtonSize.Values.Add(GetResolutionString(resolution));
+ }
+ mButtonSize.SelectedIndex = Options.Resolutions.IndexOf(mOptionsManager.Options.Resolution);
+ mButtonFullscreen = mWidgetFactory.CreateSelectButton(new[] { "aus", "an" });
+ mButtonFullscreen.SelectedIndex = mOptionsManager.Options.IsFullScreen ? 1 : 0;
+ mButtonCamera = mWidgetFactory.CreateSelectButton();
+ Options.CameraTypes.ForEach(t => mButtonCamera.Values.Add(GetCameraTypeString(t)));
+ mButtonCamera.SelectedIndex = Options.CameraTypes.IndexOf(mOptionsManager.Options.CameraType);
+ mButtonDebug = mWidgetFactory.CreateSelectButton();
+ Options.DebugModes.ForEach(m => mButtonDebug.Values.Add(GetDebugModeString(m)));
+ mButtonDebug.SelectedIndex = Options.DebugModes.IndexOf(mOptionsManager.Options.DebugMode);
+ mButtonMute = mWidgetFactory.CreateSelectButton(new[] { "aus", "an" });
+ mButtonMute.SelectedIndex = mOptionsManager.Options.IsMuted ? 0 : 1;
+ mButtonVolume = mWidgetFactory.CreateSelectButton();
+ foreach (var volume in Options.Volumes)
+ {
+ mButtonVolume.Values.Add(GetVolumeString(volume));
+ }
+ mButtonVolume.SelectedIndex = Options.Volumes.IndexOf(mOptionsManager.Options.Volume);
+
+ mMenu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ mMenu.Widgets.Add(mButtonBack);
+ mMenu.Widgets.Add(mWidgetFactory.CreateLabel("Auflösung"));
+ mMenu.Widgets.Add(mButtonSize);
+ mMenu.Widgets.Add(mWidgetFactory.CreateLabel("Fullscreen"));
+ mMenu.Widgets.Add(mButtonFullscreen);
+ mMenu.Widgets.Add(mWidgetFactory.CreateLabel("Kamera"));
+ mMenu.Widgets.Add(mButtonCamera);
+ mMenu.Widgets.Add(mWidgetFactory.CreateLabel("Debug"));
+ mMenu.Widgets.Add(mButtonDebug);
+ mMenu.Widgets.Add(mWidgetFactory.CreateLabel("Sound"));
+ mMenu.Widgets.Add(mButtonMute);
+ mMenu.Widgets.Add(mWidgetFactory.CreateLabel("Lautstärke"));
+ mMenu.Widgets.Add(mButtonVolume);
+ mMenu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ mMenu.Widgets.Add(mButtonApply);
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.Escape)
+ mMenuActions.Close(this);
+
+ return true;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ mMenu.HandleMouseEvent(mouseEvent);
+ return true;
+ }
+
+ /// <summary>
+ /// Draws this object using the given sprite batch.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// this object</param>
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ var backgroundRectangle = new Rectangle((int)mMenu.Position.X,
+ (int)mMenu.Position.Y, (int)mMenu.Size.X, (int)mMenu.Size.Y);
+ backgroundRectangle.X -= 30;
+ backgroundRectangle.Y -= 30;
+ backgroundRectangle.Width += 60;
+ backgroundRectangle.Height += 60;
+
+ spriteBatch.Begin();
+ spriteBatch.Draw(mRectangle, backgroundRectangle, Color.LightGray);
+ spriteBatch.End();
+
+ mMenu.Draw(spriteBatch);
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ if (mButtonBack.IsClicked)
+ {
+ mMenuActions.Close(this);
+ }
+ else if (mButtonApply.IsClicked)
+ {
+ UpdateOptions();
+ mMenuActions.SaveOptions();
+ mMenuActions.ApplyOptions();
+ }
+
+ mMenu.Update();
+ }
+
+ private void UpdateOptions()
+ {
+ mOptionsManager.Options.IsFullScreen = mButtonFullscreen.SelectedIndex != 0;
+ mOptionsManager.Options.DebugMode = Options.DebugModes[mButtonDebug.SelectedIndex];
+ mOptionsManager.Options.Resolution = Options.Resolutions[mButtonSize.SelectedIndex];
+ mOptionsManager.Options.CameraType = Options.CameraTypes[mButtonCamera.SelectedIndex];
+ mOptionsManager.Options.IsMuted = mButtonMute.SelectedIndex == 0;
+ mOptionsManager.Options.Volume = Options.Volumes[mButtonVolume.SelectedIndex];
+ }
+
+ private static string GetCameraTypeString(CameraType cameraType)
+ {
+ switch (cameraType)
+ {
+ case CameraType.Centered:
+ return "Zentriert";
+ case CameraType.Scrolling:
+ return "Schiebend";
+ default:
+ return "Unkown";
+ }
+ }
+
+ private static string GetDebugModeString(DebugMode debugMode)
+ {
+ switch (debugMode)
+ {
+ case DebugMode.Off:
+ return "Aus";
+ case DebugMode.Fps:
+ return "FPS Zähler";
+ case DebugMode.Full:
+ return "An";
+ default:
+ return "Unknown";
+ }
+ }
+
+ private static string GetResolutionString(Point resolution)
+ {
+ return $"{resolution.X}x{resolution.Y}";
+ }
+
+ private static string GetVolumeString(int volume)
+ {
+ return $"{volume} %";
+ }
+ }
+}
diff --git a/V3/Screens/PauseScreen.cs b/V3/Screens/PauseScreen.cs
new file mode 100644
index 0000000..e7b679f
--- /dev/null
+++ b/V3/Screens/PauseScreen.cs
@@ -0,0 +1,140 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using V3.Input;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// The screen for the pause menu.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class PauseScreen : AbstractScreen, IInitializable
+ {
+ private readonly ContentManager mContentManager;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly IMenu mMenu;
+ private readonly MenuActions mMenuActions;
+ private readonly WidgetFactory mWidgetFactory;
+
+ private Texture2D mRectangle;
+ private Button mButtonBack;
+ private Button mButtonSave;
+ private Button mButtonLoad;
+ private Button mButtonOptions;
+ private Button mButtonStatistics;
+ private Button mButtonAchievements;
+ private Button mButtonMain;
+ private Button mButtonExit;
+
+ /// <summary>
+ /// Creates a new pause screen.
+ /// </summary>
+ public PauseScreen(ContentManager contentManager,
+ GraphicsDeviceManager graphicsDeviceManager, VerticalMenu menu,
+ MenuActions menuActions, WidgetFactory widgetFactory)
+ : base(false, true)
+ {
+ mContentManager = contentManager;
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mMenu = menu;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+
+ mButtonBack = mWidgetFactory.CreateButton("Zurück zum Spiel");
+ mButtonSave = mWidgetFactory.CreateButton("Spiel speichern");
+ mButtonLoad = mWidgetFactory.CreateButton("Spiel laden");
+ mButtonLoad.IsEnabled = mMenuActions.CanLoadGame();
+ mButtonStatistics = mWidgetFactory.CreateButton("Statistiken");
+ mButtonAchievements = mWidgetFactory.CreateButton("Erfolge");
+ mButtonOptions = mWidgetFactory.CreateButton("Optionen");
+ mButtonMain = mWidgetFactory.CreateButton("Hauptmenü");
+ mButtonExit = mWidgetFactory.CreateButton("Spiel schließen");
+
+ mMenu.Widgets.Add(mButtonBack);
+ mMenu.Widgets.Add(mButtonSave);
+ mMenu.Widgets.Add(mButtonLoad);
+ mMenu.Widgets.Add(mButtonOptions);
+ mMenu.Widgets.Add(mButtonStatistics);
+ mMenu.Widgets.Add(mButtonAchievements);
+ mMenu.Widgets.Add(mButtonMain);
+ mMenu.Widgets.Add(mButtonExit);
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.Escape)
+ mMenuActions.Close(this);
+
+ return true;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ mMenu.HandleMouseEvent(mouseEvent);
+ return true;
+ }
+
+ /// <summary>
+ /// Draws this object using the given sprite batch.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// this object</param>
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ spriteBatch.Begin();
+ spriteBatch.Draw(mRectangle, mGraphicsDeviceManager.GraphicsDevice.Viewport.Bounds, Color.Black * 0.7f);
+ spriteBatch.End();
+
+ mMenu.Draw(spriteBatch);
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ if (mButtonBack.IsClicked)
+ {
+ mMenuActions.Close(this);
+ }
+ else if (mButtonSave.IsClicked)
+ {
+ mMenuActions.SaveGame();
+ mMenuActions.Close(this);
+ }
+ else if (mButtonLoad.IsClicked)
+ {
+ mMenuActions.OpenLoadScreen();
+ }
+ else if (mButtonOptions.IsClicked)
+ {
+ mMenuActions.OpenOptionsScreen();
+ }
+ else if (mButtonStatistics.IsClicked)
+ {
+ mMenuActions.OpenStatisticsScreen();
+ }
+ else if (mButtonAchievements.IsClicked)
+ {
+ mMenuActions.OpenAchievementsScreen();
+ }
+ else if (mButtonMain.IsClicked)
+ {
+ mMenuActions.OpenMainScreen();
+ }
+ else if (mButtonExit.IsClicked)
+ {
+ mMenuActions.Exit();
+ }
+
+ mMenu.Update();
+ }
+ }
+}
diff --git a/V3/Screens/ScreenManager.cs b/V3/Screens/ScreenManager.cs
new file mode 100644
index 0000000..97c4e28
--- /dev/null
+++ b/V3/Screens/ScreenManager.cs
@@ -0,0 +1,164 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Media;
+using V3.Input;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// Default implementation of IScreenManager.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ internal sealed class ScreenManager : IScreenManager
+ {
+ private readonly IInputManager mInputManager;
+ private readonly DebugScreen mDebugScreen;
+ private readonly ContentManager mContentManager;
+
+ private readonly Stack<IScreen> mScreens = new Stack<IScreen>();
+
+ /// <summary>
+ /// Creates a new screen manager using the given input manager.
+ /// </summary>
+ public ScreenManager(IInputManager inputManager, DebugScreen debugScreen, ContentManager contentManager)
+ {
+ mInputManager = inputManager;
+ mDebugScreen = debugScreen;
+ mContentManager = contentManager;
+ }
+
+ /// <summary>
+ /// Adds a screen to the foreground.
+ /// </summary>
+ /// <param name="screen">the screen to add in the foreground</param>
+ public void AddScreen(IScreen screen)
+ {
+ mScreens.Push(screen);
+#if NO_AUDIO
+#else
+ if (screen is MainScreen)
+ {
+ MediaPlayer.IsRepeating = true;
+ MediaPlayer.Play(mContentManager.Load<Song>("Sounds/Kosta_T_-_06"));
+ }
+ else if (screen is GameScreen)
+ {
+ MediaPlayer.IsRepeating = true;
+ MediaPlayer.Play(mContentManager.Load<Song>("Sounds/Afraid_to_Go"));
+
+ }
+ //else if (screen is PauseScreen)
+ //{
+ // mAbstractCreature.GetSelf();
+ // mAbstractCreature.mSoundEffectInstance.Stop();
+ // mAbstractCreature.mSoundEffectInstanceFight.Stop();
+ // mAbstractCreature.mSoundEffectInstanceHorse.Stop();
+ // mAbstractCreature.mSoundEffectInstanceKnight.Stop();
+ // mAbstractCreature.mSoundEffectInstanceMeatball.Stop();
+ //}
+#endif
+ }
+
+ /// <summary>
+ /// Removes the given screen if it is on the top of the screen stack.
+ /// </summary>
+ /// <param name="screen">the screen to remove</param>
+ public void RemoveScreen(IScreen screen)
+ {
+ if (mScreens.Count > 0 && screen.Equals(mScreens.Peek()))
+ {
+ mScreens.Pop();
+ }
+ }
+
+ /// <summary>
+ /// Clears the screen stack.
+ /// </summary>
+ public void Clear()
+ {
+ mScreens.Clear();
+ }
+
+ /// <summary>
+ /// Draws the top screen and, if enabled, the lower screens. The draw
+ /// order is from bottom to top, i. e. the lowest enabled screen is
+ /// drawn first, and the top screen is drawn last.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// </param>
+ public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ var drawScreens = new Stack<IScreen>();
+ foreach (var screen in mScreens)
+ {
+ drawScreens.Push(screen);
+ if (!screen.DrawLower)
+ break;
+ }
+
+ foreach (var screen in drawScreens)
+ {
+ screen.Draw(gameTime, spriteBatch);
+ }
+ mDebugScreen.Draw(gameTime, spriteBatch);
+ }
+
+ /// <summary>
+ /// Updates the top screen and, if enabled, the lower screens. The
+ /// update order is from top to bottom, i. e. the lowest enabled screen
+ /// is drawn first, and the top screen is drawn last.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ public void Update(GameTime gameTime)
+ {
+ mInputManager.Update();
+ HandleInputEvents();
+
+ mDebugScreen.Update(gameTime);
+ var currentScreens = new Stack<IScreen>(new Stack<IScreen>(mScreens));
+ foreach (var screen in currentScreens)
+ {
+ screen.Update(gameTime);
+ if (!screen.UpdateLower)
+ break;
+ }
+ }
+
+ private void HandleInputEvents()
+ {
+ // We need to clone the stack as the input management methods
+ // might want to modify the screen stack. We need two stacks as
+ // each stack reverses the order.
+ var currentScreens = new Stack<IScreen>(new Stack<IScreen>(mScreens));
+ foreach (var keyEvent in mInputManager.KeyEvents)
+ {
+ foreach (var screen in currentScreens)
+ {
+ if (screen.HandleKeyEvent(keyEvent))
+ break;
+ }
+ }
+ foreach (var mouseEvent in mInputManager.MouseEvents)
+ {
+ foreach (var screen in currentScreens)
+ {
+ if (screen.HandleMouseEvent(mouseEvent))
+ break;
+ }
+ }
+ }
+
+ public GameScreen GetGameScreen()
+ {
+ foreach (var screen in mScreens)
+ {
+ if (screen is GameScreen)
+ return (GameScreen) screen;
+ }
+ return null;
+ }
+ }
+}
diff --git a/V3/Screens/StatisticsScreen.cs b/V3/Screens/StatisticsScreen.cs
new file mode 100644
index 0000000..90e0ff4
--- /dev/null
+++ b/V3/Screens/StatisticsScreen.cs
@@ -0,0 +1,186 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using V3.Input;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// The screen for the statistics.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class StatisticsScreen : AbstractScreen, IInitializable
+ {
+ private readonly ContentManager mContentManager;
+ private readonly IMenuFactory mMenuFactory;
+ private readonly MenuActions mMenuActions;
+ private readonly WidgetFactory mWidgetFactory;
+ private readonly AchievementsAndStatistics mAchievementsAndStatistics;
+
+ private Button mButtonBack;
+ private SelectButton mSelectMission;
+
+ private List<IMenu> mMenuList = new List<IMenu>();
+ private Texture2D mRectangle;
+
+ public StatisticsScreen(ContentManager contentManager, MenuActions menuActions, WidgetFactory widgetFactory,
+ IMenuFactory menuFactory, AchievementsAndStatistics achievementsAndStatistics)
+ : base(false, true)
+ {
+ mContentManager = contentManager;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ mMenuFactory = menuFactory;
+ mAchievementsAndStatistics = achievementsAndStatistics;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+
+ mButtonBack = mWidgetFactory.CreateButton("Zurück");
+ mSelectMission = mWidgetFactory.CreateSelectButton();
+
+ var menu = mMenuFactory.CreateFormMenu();
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Auswahl"));
+ menu.Widgets.Add(mSelectMission);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Zeit"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + string.Format("{0:T}",mAchievementsAndStatistics.mUsedTime)));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Getötete Gegner"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + string.Format("{0:00000}", mAchievementsAndStatistics.mKilledCreatures)));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Verlorene Einheiten"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + string.Format("{0:00000}", mAchievementsAndStatistics.mLostServants)));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("zurückgelegte Entfernung"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + string.Format("{0:00000}", mAchievementsAndStatistics.mWalkedDistance) + " m"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ menu.Widgets.Add(mButtonBack);
+
+ menu = mMenuFactory.CreateFormMenu();
+
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Auswahl"));
+ menu.Widgets.Add(mSelectMission);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Zeit"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "00:00:00"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Getötete Gegner"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Verlorene Einheiten"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("zurückgelegte Entfernung"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ menu.Widgets.Add(mButtonBack);
+
+ menu = mMenuFactory.CreateFormMenu();
+
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Auswahl"));
+ menu.Widgets.Add(mSelectMission);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Zeit"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "00:00:00"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Getötete Gegner"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Verlorene Einheiten"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("zurückgelegte Entfernung"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ menu.Widgets.Add(mButtonBack);
+
+ for (var i = 0; i < mMenuList.Count; i++)
+ mSelectMission.Values.Add($"Mission {i + 1}");
+
+ menu = mMenuFactory.CreateFormMenu();
+
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Auswahl"));
+ menu.Widgets.Add(mSelectMission);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Zeit"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "00:00:00"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Getötete Gegner"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Verlorene Einheiten"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("zurückgelegte Entfernung"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ menu.Widgets.Add(mButtonBack);
+
+ mSelectMission.Values.Add("Gesamt");
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.Escape)
+ mMenuActions.Close(this);
+
+ return true;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ GetCurrentMenu().HandleMouseEvent(mouseEvent);
+ return true;
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ if (mButtonBack.IsClicked)
+ {
+ mMenuActions.Close(this);
+ }
+ GetCurrentMenu().Update();
+ }
+
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ var menu = GetCurrentMenu();
+ var backgroundRectangle = new Rectangle((int)menu.Position.X,
+ (int)menu.Position.Y, (int)menu.Size.X, (int)menu.Size.Y);
+ backgroundRectangle.X -= 30;
+ backgroundRectangle.Y -= 30;
+ backgroundRectangle.Width += 60;
+ backgroundRectangle.Height += 60;
+
+ spriteBatch.Begin();
+ spriteBatch.Draw(mRectangle, backgroundRectangle, Color.LightGray);
+ spriteBatch.End();
+
+ GetCurrentMenu().Draw(spriteBatch);
+ }
+
+ private IMenu GetCurrentMenu()
+ {
+ return mMenuList[mSelectMission.SelectedIndex];
+ }
+ }
+}
diff --git a/V3/Screens/TechdemoScreen.cs b/V3/Screens/TechdemoScreen.cs
new file mode 100644
index 0000000..748c9a1
--- /dev/null
+++ b/V3/Screens/TechdemoScreen.cs
@@ -0,0 +1,325 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using V3.Camera;
+using V3.Data;
+using V3.Effects;
+using V3.Input;
+using V3.Map;
+using V3.Objects;
+
+namespace V3.Screens
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public class TechdemoScreen : AbstractScreen, IInitializable
+ {
+ private enum Creatures
+ {
+ Empty, Zombie, Skeleton, Peasant, Knight
+ }
+
+ private Creatures mCreatePerKey;
+
+ private readonly CameraManager mCameraManager;
+ private readonly ContentManager mContentManager;
+ private readonly CreatureFactory mCreatureFactory;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly MenuActions mMenuActions;
+ private readonly IOptionsManager mOptionsManager;
+ private readonly IEffectsManager mEffectsManager;
+ private readonly Selection mSelection;
+ private readonly Transformation mTransformation;
+ private readonly IMapManager mMapManager;
+ private readonly IObjectsManager mObjectsManager;
+ private readonly Pathfinder mPathfinder;
+ private readonly Texture2D mOnePixelTexture;
+ // Fields for handling mouse input.
+ private Point mInitialClickPosition;
+ private bool mLeftButtonPressed;
+ private bool mRightButtonPressed;
+ private Vector2 mRightButtonPosition;
+
+ /// <summary>
+ /// Creates a new game screen.
+ /// </summary>
+ public TechdemoScreen(IOptionsManager optionsManager, CameraManager cameraManager,
+ ContentManager contentManager, CreatureFactory creatureFactory,
+ GraphicsDeviceManager graphicsDeviceManager, IMapManager mapManager,
+ MenuActions menuActions, IObjectsManager objectsManager,
+ Pathfinder pathfinder, Selection selection, Transformation transformation,
+ IEffectsManager effectsManager) : base(false, false)
+ {
+ mMapManager = mapManager;
+ mObjectsManager = objectsManager;
+ mCameraManager = cameraManager;
+ mOptionsManager = optionsManager;
+ mContentManager = contentManager;
+ mCreatureFactory = creatureFactory;
+ mEffectsManager = effectsManager;
+ mTransformation = transformation;
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mMenuActions = menuActions;
+ mPathfinder = pathfinder;
+ mSelection = selection;
+ mOnePixelTexture = contentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ }
+
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ if (mouseEvent.MouseButton == MouseButton.Left && mouseEvent.ButtonState == ButtonState.Pressed)
+ {
+ if (!mLeftButtonPressed)
+ {
+ mLeftButtonPressed = true;
+ mInitialClickPosition = mouseEvent.PositionPressed;
+ }
+ }
+ else
+ {
+ if (mLeftButtonPressed)
+ {
+ mSelection.Select(mInitialClickPosition + mCameraManager.GetCamera().Location.ToPoint(),
+ mouseEvent.PositionReleased.GetValueOrDefault() + mCameraManager.GetCamera().Location.ToPoint());
+ mLeftButtonPressed = false;
+ }
+ }
+ if (mouseEvent.MouseButton == MouseButton.Right && mouseEvent.ButtonState == ButtonState.Pressed)
+ {
+ if (!mRightButtonPressed)
+ {
+ mRightButtonPressed = true;
+ mInitialClickPosition = mouseEvent.PositionPressed;
+ }
+ }
+ if (mouseEvent.MouseButton == MouseButton.Right && mouseEvent.ButtonState == ButtonState.Released)
+ {
+ if (mouseEvent.PositionReleased != null && mouseEvent.ReleasedOnScreen)
+ {
+ mRightButtonPressed = false;
+ mRightButtonPosition = mouseEvent.PositionReleased.Value.ToVector2() + mCameraManager.GetCamera().Location;
+ mSelection.Move(mouseEvent.PositionReleased.Value.ToVector2()
+ + mCameraManager.GetCamera().Location);
+ }
+ }
+ if (mouseEvent.ReleasedOnScreen)
+ {
+ var position = mouseEvent.PositionPressed.ToVector2() + mCameraManager.GetCamera().Location;
+ switch (mCreatePerKey)
+ {
+ case Creatures.Zombie:
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateZombie(position, MovementDirection.S));
+ break;
+ case Creatures.Skeleton:
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateSkeleton(position, MovementDirection.S));
+ break;
+ case Creatures.Peasant:
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateMalePeasant(position, MovementDirection.S));
+ break;
+ case Creatures.Knight:
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateKnight(position, MovementDirection.S));
+ break;
+ }
+ }
+ return true;
+ }
+
+ public void Initialize()
+ {
+ mMapManager.Load("techdemo");
+ mObjectsManager.Initialize(mMapManager);
+ mCameraManager.Initialize(mMapManager.SizeInPixel);
+ mPathfinder.LoadGrid(mMapManager.GetPathfindingGrid());
+ mTransformation.mSelection = mSelection;
+ mTransformation.LoadArea(mContentManager);
+
+ // Add creatures for testing purposes.
+ var necromancer = mCreatureFactory.CreateNecromancer(new Vector2(1592, 1609), MovementDirection.S);
+ var king = mCreatureFactory.CreateKing(new Vector2(1500, 200), MovementDirection.S);
+ mObjectsManager.CreatePlayerCharacter(necromancer);
+ mObjectsManager.CreateBoss(king);
+ int makeCreatureStrongerModifier = 100;
+ int makePeasantsNotSoMuchStrongerModifier = 10;
+ AddCreature<MalePeasant>(new Point(4), new Point(200, 2000), new Point(2800, 3000), makePeasantsNotSoMuchStrongerModifier);
+ AddCreature<FemalePeasant>(new Point(4), new Point(232, 2032), new Point(2800, 3000), makePeasantsNotSoMuchStrongerModifier);
+ AddCreature<Knight>(new Point(4), new Point(200, 200), new Point(2800, 1000), makeCreatureStrongerModifier);
+ AddCreature<KingsGuard>(new Point(4), new Point(232, 232), new Point(2800, 1000), makeCreatureStrongerModifier);
+ AddCreature<Zombie>(new Point(3), new Point(264, 264), new Point(2800, 1000), makeCreatureStrongerModifier);
+ AddCreature<Skeleton>(new Point(3), new Point(290, 290), new Point(2800, 1000), makeCreatureStrongerModifier);
+ AddCreature<Zombie>(new Point(4), new Point(264, 2064), new Point(2800, 3000), makeCreatureStrongerModifier);
+
+ mTransformation.ObjectsManager = mObjectsManager;
+ }
+
+ /// <summary>
+ /// Add a generic creature to Techdemo.
+ /// Using pixel coordinates for calculating start and end point of map.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="density">Density on X and Y axis. More creatures when lower.</param>
+ /// <param name="start">Start point for creating the creature batallion.</param>
+ /// <param name="end">At which point to stop creating creatures.</param>
+ /// <param name="makeStronger">Give more HP. Better for testing.</param>
+ private void AddCreature<T>(Point density, Point start, Point end, int makeStronger) where T:ICreature
+ {
+ var type = typeof(T);
+ for (int i = start.Y; i < end.Y; i += 32 * density.Y)
+ {
+ for (int j = start.X; j < end.X; j += 32 * density.X)
+ {
+ Vector2 position = new Vector2(j, i);
+ ICreature creature;
+ if (type == typeof(MalePeasant))
+ {
+ creature = mCreatureFactory.CreateMalePeasant(position, MovementDirection.S);
+ }
+ else if (type == typeof(FemalePeasant))
+ {
+ creature = mCreatureFactory.CreateFemalePeasant(position, MovementDirection.S);
+ }
+ else if (type == typeof(Skeleton))
+ {
+ creature = mCreatureFactory.CreateSkeleton(position, MovementDirection.S);
+ }
+ else if (type == typeof(Zombie))
+ {
+ creature = mCreatureFactory.CreateZombie(position, MovementDirection.S);
+ }
+ else if (type == typeof(Knight))
+ {
+ creature = mCreatureFactory.CreateKnight(position, MovementDirection.S);
+ }
+ else if (type == typeof(SkeletonHorse))
+ {
+ creature = mCreatureFactory.CreateSkeletonHorse(position, MovementDirection.S);
+ }
+ else if (type == typeof(Meatball))
+ {
+ creature = mCreatureFactory.CreateMeatball(position, MovementDirection.S);
+ }
+ else if (type == typeof(KingsGuard))
+ {
+ creature = mCreatureFactory.CreateKingsGuard(position, MovementDirection.S);
+ }
+ else if (type == typeof(SkeletonElite))
+ {
+ creature = mCreatureFactory.CreateSkeletonElite(position, MovementDirection.S);
+ }
+ else
+ {
+ creature = mCreatureFactory.CreateZombie(position, MovementDirection.S);
+ }
+ creature.Empower(makeStronger);
+ mObjectsManager.CreateCreature(creature);
+ }
+ }
+
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ if (keyEvent.KeyState == KeyState.Down)
+ {
+ Creatures createPerKey = Creatures.Empty;
+ switch (keyEvent.Key)
+ {
+ case Keys.Escape:
+ mMenuActions.OpenPauseScreen();
+ break;
+ case Keys.F1:
+ createPerKey = Creatures.Zombie;
+ break;
+ case Keys.F2:
+ createPerKey = Creatures.Skeleton;
+ break;
+ case Keys.F3:
+ createPerKey = Creatures.Peasant;
+ break;
+ case Keys.F4:
+ createPerKey = Creatures.Knight;
+ break;
+ case Keys.F5:
+ Rectangle cameraRectangle = mCameraManager.GetCamera().ScreenRectangle;
+ mEffectsManager.PlayOnce(new SmokeBig(), cameraRectangle.Center, cameraRectangle.Size);
+ mObjectsManager.ExposeTheLiving();
+ break;
+ case Keys.F6:
+ (mObjectsManager.PlayerCharacter as Necromancer)?.ChangeSex();
+ mEffectsManager.PlayOnce(new SmokeSmall(), mObjectsManager.PlayerCharacter.Position.ToPoint(), new Point(256));
+ break;
+ }
+ if (createPerKey == mCreatePerKey)
+ {
+ mCreatePerKey = Creatures.Empty;
+ }
+ else
+ {
+ mCreatePerKey = createPerKey;
+ }
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Updates the status of this object.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ public override void Update(GameTime gameTime)
+ {
+#if DEBUG
+#else
+ try
+ {
+#endif
+ mObjectsManager.Update(gameTime, mRightButtonPressed, mRightButtonPosition, mCameraManager.GetCamera());
+ mCameraManager.Update(mObjectsManager.PlayerCharacter);
+ mEffectsManager.Update(gameTime);
+
+ // Call for Transformations
+ mTransformation.Transform();
+
+ mSelection.UpdateSelection();
+#if DEBUG
+#else
+ }
+ catch (System.Exception e)
+ {
+ System.Console.WriteLine(e.Message);
+ }
+#endif
+ }
+
+ /// <summary>
+ /// Draws this object using the given sprite batch.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// this object</param>
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ mGraphicsDeviceManager.GraphicsDevice.Clear(Color.Black);
+ spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null, mCameraManager.GetCamera().Transform);
+ mMapManager.DrawFloor(spriteBatch, mCameraManager.GetCamera());
+ mObjectsManager.Draw(spriteBatch, mCameraManager.GetCamera());
+ mTransformation.DrawNecroArea(spriteBatch);
+ mEffectsManager.Draw(spriteBatch);
+ if (mLeftButtonPressed)
+ mSelection.Draw(spriteBatch, mInitialClickPosition + mCameraManager.GetCamera().Location.ToPoint(), Mouse.GetState().Position + mCameraManager.GetCamera().Location.ToPoint());
+ if (mOptionsManager.Options.DebugMode == DebugMode.Full)
+ {
+ mMapManager.DrawPathfindingGrid(spriteBatch, mCameraManager.GetCamera());
+ mObjectsManager.DrawQuadtree(spriteBatch);
+ DrawLastSelection(spriteBatch, mSelection.LastSelection);
+ }
+ // Draws the selection rectangle when left mouse button is pressed.
+ spriteBatch.End();
+ }
+
+ private void DrawLastSelection(SpriteBatch spriteBatch, Rectangle selection)
+ {
+ spriteBatch.Draw(mOnePixelTexture, selection, new Color(Color.Black, 100));
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Screens/VictoryScreen.cs b/V3/Screens/VictoryScreen.cs
new file mode 100644
index 0000000..ffb2406
--- /dev/null
+++ b/V3/Screens/VictoryScreen.cs
@@ -0,0 +1,76 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Ninject;
+using V3.Input;
+
+namespace V3.Screens
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class VictoryScreen : AbstractScreen, IInitializable
+ {
+ private static TimeSpan sTotalDelay = TimeSpan.FromSeconds(1);
+
+ private readonly ContentManager mContentManager;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+
+ private Rectangle mVictoryRectangle;
+ private Vector2 mFontCenter;
+ private Vector2 mCenter;
+ private SpriteFont mVictoryFont;
+ private Texture2D mRectangle;
+
+ private TimeSpan mDelayTimer = sTotalDelay;
+
+ /// <summary>
+ /// Creates a victory screen if the player defeats the boss enemy.
+ /// </summary>
+
+ public VictoryScreen(ContentManager contentManager,
+ GraphicsDeviceManager graphicsDeviceManager
+ )
+ : base(false, true)
+ {
+ mContentManager = contentManager;
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ return false;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ mVictoryFont = mContentManager.Load<SpriteFont>("Fonts/VictoryFont");
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ var viewport = mGraphicsDeviceManager.GraphicsDevice.Viewport;
+ if (mDelayTimer > TimeSpan.Zero)
+ mDelayTimer -= gameTime.ElapsedGameTime;
+
+ mCenter = new Vector2(viewport.Width / 2f, viewport.Height / 2f);
+ mFontCenter = mVictoryFont.MeasureString("Die Rache ist euer!") / 2;
+ mVictoryRectangle = new Rectangle(0, (int)mCenter.Y - (int)mFontCenter.Y - 10, viewport.Width, (int)mFontCenter.Y * 2 - 10);
+ }
+
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ float displayRatio = (float)(1 - mDelayTimer.TotalMilliseconds / sTotalDelay.TotalMilliseconds);
+ spriteBatch.Begin();
+ spriteBatch.Draw(mRectangle,
+ mVictoryRectangle,
+ Color.Black * 0.8f * displayRatio);
+
+ spriteBatch.DrawString(mVictoryFont,
+ "Die Rache ist euer!",
+ mCenter,
+ Color.DarkGoldenrod * displayRatio, 0, mFontCenter, 1.0f, SpriteEffects.None, 0.5f);
+ spriteBatch.End();
+ }
+ }
+ }