diff options
Diffstat (limited to 'V3/Screens')
-rw-r--r-- | V3/Screens/AbstractScreen.cs | 57 | ||||
-rw-r--r-- | V3/Screens/AchievementsScreen.cs | 221 | ||||
-rw-r--r-- | V3/Screens/DeathScreen.cs | 140 | ||||
-rw-r--r-- | V3/Screens/DebugScreen.cs | 92 | ||||
-rw-r--r-- | V3/Screens/FpsCounter.cs | 46 | ||||
-rw-r--r-- | V3/Screens/GameScreen.cs | 306 | ||||
-rw-r--r-- | V3/Screens/HudScreen.cs | 349 | ||||
-rw-r--r-- | V3/Screens/IDrawable.cs | 19 | ||||
-rw-r--r-- | V3/Screens/IScreen.cs | 39 | ||||
-rw-r--r-- | V3/Screens/IScreenFactory.cs | 27 | ||||
-rw-r--r-- | V3/Screens/IScreenManager.cs | 35 | ||||
-rw-r--r-- | V3/Screens/IUpdatable.cs | 16 | ||||
-rw-r--r-- | V3/Screens/LoadScreen.cs | 124 | ||||
-rw-r--r-- | V3/Screens/MainScreen.cs | 195 | ||||
-rw-r--r-- | V3/Screens/MenuActions.cs | 208 | ||||
-rw-r--r-- | V3/Screens/OptionsScreen.cs | 197 | ||||
-rw-r--r-- | V3/Screens/PauseScreen.cs | 140 | ||||
-rw-r--r-- | V3/Screens/ScreenManager.cs | 164 | ||||
-rw-r--r-- | V3/Screens/StatisticsScreen.cs | 186 | ||||
-rw-r--r-- | V3/Screens/TechdemoScreen.cs | 325 | ||||
-rw-r--r-- | V3/Screens/VictoryScreen.cs | 76 |
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(); + } + } + } |