using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using System; using System.IO; using Microsoft.Xna.Framework.Input; using System.Collections.Generic; using Microsoft.Xna.Framework.Audio; namespace LunaLightXMG { public class Level : Game, IDisposable { // Objects private Camera2D camera2D; private PlayerInput playerInput; // Native retro scale private int retroWidth = 320; private int retroHeight = 180; // Entities private List enemies; private int numberOfEnemies; public Player Player { get { return player; } } Player player; // Key Locations private Vector2 startPosition; private Point exit = InvalidPosition; private static readonly Point InvalidPosition = new Point(-1,-1); // Physical Structure private Platform[] platforms; private int numberOfPlatforms; // Game State private Random random = new Random(546452); public int Score { get { return score; } } int score; public ContentManager Content { get { return content; } } ContentManager content; // Music private SoundEffect sinkBeats; private SoundEffectInstance backgroundMusicInstance; // Test Render Target Handler private RenderHandler renderHandler; bool resizing; // Debugging private SpriteFont debugFont; private Texture2D debugGrid; // Keyboard input private KeyboardState prevKeyState; // Utility public Utilities utilities; // Textures Texture2D backgroundTexture; Texture2D platformTexture; #region Loading // Level Definition public Level(IServiceProvider serviceProvider,SpriteBatch spriteBatch, RenderHandler render_handler) { // Create content manager to load content used by just this level. content = new ContentManager(serviceProvider, "Content"); renderHandler = render_handler; // Initialize camera camera2D = new Camera2D(retroWidth, retroHeight); // camera test - set level bounds camera2D.SetLevelBounds(new Rectangle(0,0,480,270)); // Testing - default 320x180 // Initialize Render Handler // Load background layer textures. For now, all levels // will use the same texture. backgroundTexture = Content.Load("Sink area"); // Load platform layer textures. For now, all platforms // will use the same texture. platformTexture = Content.Load("platform"); // Load player sprite // Test player input and movement with abstracted player class Vector2 initPlayerPosition = new Vector2(163, 0); player = new Player(initPlayerPosition); playerInput = new PlayerInput(); playerInput.HasControl = true; // Misc. prevKeyState = Keyboard.GetState(); player.LoadContent(Content); // Init number of platforms / enemies numberOfPlatforms = 4; numberOfEnemies = 10; // Load enemies enemies = SpawnEnemies(numberOfEnemies); // Load Platforms LoadPlatforms(); // Debugging debugFont = Content.Load("fonts/debugFont"); debugGrid = Content.Load("debugGrid"); // Get utilities utilities = new Utilities(); } private void LoadPlatforms() { platforms = PlatformGenerator.CreatePlatforms(numberOfPlatforms); } private List SpawnEnemies(int numOfEnemies) { List newEnemies = new List(); for (int i = 0; i < numOfEnemies; i++) { Vector2 spawnPosition = new Vector2(16 + i * 3, -16); Enemy enemy = new(spawnPosition, random.Next(1,3)); enemy.LoadContent(Content); newEnemies.Add(enemy); } return newEnemies; } #endregion #region Update public void Update(GameTime gameTime, KeyboardState keyboardState) { // Temporary quit game if (keyboardState.IsKeyDown(Keys.Escape)) Exit(); // Check for game reset if (keyboardState.IsKeyDown(Keys.R)) ResetGame(); // Update player playerInput.Update(); player.Update(gameTime, playerInput, platforms); // Update Enemies foreach(var enemy in enemies) enemy.Update(gameTime, player, platforms); // Test render handler renderHandler.UpdateZoom(); //Update camera position camera2D.Follow(gameTime, player.position); camera2D.UpdateScreenShake(gameTime); // Camera testing TestCameraWhilePlaying(); ToggleBackgroundMusic(); // Update keyState prevKeyState = Keyboard.GetState(); } public void Dispose() { Content.Unload(); } #endregion #region Draw public void Draw(GameTime gameTime, SpriteBatch spriteBatch) { // // Test Render Target Handler - Draw sprites to the render (target renderHandler.BeginRenderTarget(camera2D.GetViewMatrix()); // Test draw tiles foreach (var platform in platforms) { platform.Draw(spriteBatch, platformTexture); // Assuming all tiles use the same texture for now } // Test player input and movement with abstracted player class player.Draw(spriteBatch); foreach (var enemy in enemies) { enemy.Draw(spriteBatch); } renderHandler.EndRenderTarget(); renderHandler.DrawRenderTarget(player, debugFont, camera2D); } #endregion #region Methods private void PlayBackgroundMusic(SoundEffect backgroundMusic) { if (backgroundMusicInstance == null) { backgroundMusicInstance = backgroundMusic.CreateInstance(); backgroundMusicInstance.IsLooped = true; } if (backgroundMusicInstance.State == SoundState.Playing) { backgroundMusicInstance.Stop(); } else { backgroundMusicInstance.Play(); } } public void ResetGame() { // reset cam camera2D.Rotation = 0; camera2D.Zoom = 1f; // reset render target zoom renderHandler.ZoomNormalize(); // Reset player position player.Reset(new Vector2(163, 0)); // Clear and respawn enemies enemies.Clear(); SpawnEnemies(numberOfEnemies); // Regenerate platforms LoadPlatforms(); } #endregion #region Testing private void DrawDebugInfo(SpriteBatch spriteBatch) { // Draw debug messages spriteBatch.DrawString(debugFont, $"VSP: {player.vsp}", new Vector2(10, 10), Color.White); spriteBatch.DrawString(debugFont, $"Grounded: {player.grounded}", new Vector2(10, 30), Color.White); spriteBatch.DrawString(debugFont, $"Walled: {player.walled}", new Vector2(10, 50), Color.White); spriteBatch.DrawString(debugFont, $"Position: {player.position}", new Vector2(10, 70), Color.White); spriteBatch.DrawString(debugFont, $"Camera Position: {camera2D.Position}", new Vector2(10, 90), Color.White); spriteBatch.DrawString(debugFont, $"Camera Rotation: {camera2D.Rotation}", new Vector2(10, 110), Color.White); spriteBatch.DrawString(debugFont, $"Render Target Zoom: {renderHandler.ZoomLevel}", new Vector2(10, 130), Color.White); spriteBatch.DrawString(debugFont, $"Key Pressed: {Keyboard.GetState().GetPressedKeys()}", new Vector2(10, 150), Color.White); spriteBatch.DrawString(debugFont, $"Camera Rotation Control - Rotate left: K, Rotate right: L", new Vector2(10, 190), Color.White); spriteBatch.DrawString(debugFont, $"Render Target Zoom Control - Zoom in: U, Zoom normal: I, Zoom out: O", new Vector2(10, 210), Color.White); spriteBatch.DrawString(debugFont, $"Press P to test screen shake.", new Vector2(10, 230), Color.White); spriteBatch.DrawString(debugFont, $"Press M to toggle game music.", new Vector2(10, 250), Color.White); spriteBatch.DrawString(debugFont, $"Press R to reset level.", new Vector2(10, 270), Color.White); } private void ToggleBackgroundMusic() { KeyboardState currKeyState = Keyboard.GetState(); if (prevKeyState.IsKeyUp(Keys.M) && currKeyState.IsKeyDown(Keys.M)) { PlayBackgroundMusic(sinkBeats); } } private void TestCameraWhilePlaying() { KeyboardState keyboardState = Keyboard.GetState(); var KeyK = keyboardState.IsKeyDown(Keys.K); var KeyL = keyboardState.IsKeyDown(Keys.L); var KeyU = keyboardState.IsKeyDown(Keys.U); var KeyI = keyboardState.IsKeyDown(Keys.I); var KeyO = keyboardState.IsKeyDown(Keys.O); var KeyP = keyboardState.IsKeyDown(Keys.P); if (KeyK) { camera2D.Rotation -= 0.01f; } if (KeyL) { camera2D.Rotation += 0.01f; } if (KeyU) { renderHandler.ZoomIn(); } if (KeyI) { renderHandler.ZoomNormalize(); } if (KeyO) { renderHandler.ZoomOut(); } if (KeyP) { camera2D.ScreenShake(0.5f, 5f); } } #endregion } }