483 lines
12 KiB
C#
483 lines
12 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
using Microsoft.Xna.Framework;
|
||
using Microsoft.Xna.Framework.Audio;
|
||
using Microsoft.Xna.Framework.Graphics;
|
||
using Microsoft.Xna.Framework.Input;
|
||
using Microsoft.Xna.Framework.Media;
|
||
using SpacePew.Camera;
|
||
using SpacePew.Models;
|
||
using SpacePew.Models.Weapons;
|
||
using SpacePew.Networking;
|
||
using SpacePew.ParticleSystem;
|
||
using Color = Microsoft.Xna.Framework.Color;
|
||
using Rectangle = Microsoft.Xna.Framework.Rectangle;
|
||
using SpacePew.Extensions;
|
||
using System.Linq;
|
||
|
||
namespace SpacePew
|
||
{
|
||
/// <summary>
|
||
/// This is the main type for your game
|
||
/// </summary>
|
||
public class MainGame : Game
|
||
{
|
||
public static int CenterX;
|
||
public static int CenterY;
|
||
|
||
public UdpServer NetworkServer { get; private set; }
|
||
public UdpClient NetworkClient { get; private set; }
|
||
|
||
private Level _level;
|
||
public Level Level
|
||
{
|
||
get { return _level; }
|
||
set { _level = value; }
|
||
}
|
||
|
||
private SpriteBatch _spriteBatch;
|
||
public SpriteBatch SpriteBatch
|
||
{
|
||
get
|
||
{
|
||
return _spriteBatch;
|
||
}
|
||
}
|
||
|
||
readonly GraphicsDeviceManager _graphics;
|
||
readonly EntityFactory _entityFactory;
|
||
|
||
Hud _hud;
|
||
Minimap _minimap;
|
||
|
||
private readonly UdpNetworkGui _udpClientGui;
|
||
|
||
private KeyboardState _currentKeyboardState;
|
||
private GamePadState _currentGamePadState;
|
||
|
||
private Texture2D _backgroundTexture;
|
||
|
||
private ExplosionParticleSystem _explosion;
|
||
private ExplosionSmokeParticleSystem _smoke;
|
||
private SmokePlumeParticleSystem _smokePlume;
|
||
|
||
private NetworkMessenger _networkMessenger;
|
||
|
||
private Song _gameSong;
|
||
public Song GameSong
|
||
{
|
||
get { return _gameSong; }
|
||
}
|
||
|
||
private ICamera2D _camera;
|
||
|
||
private readonly Random _randomizer;
|
||
|
||
public static bool IsKeyboardInUse;
|
||
|
||
public void AddGameComponents()
|
||
{
|
||
_hud = new Hud(this);
|
||
Components.Add(_hud);
|
||
|
||
_minimap = new Minimap(this);
|
||
Components.Add(_minimap);
|
||
|
||
_camera = new Camera2D(this)
|
||
{
|
||
Focus = NetworkClient.LocalPlayer
|
||
};
|
||
|
||
Components.Add((IGameComponent)_camera);
|
||
|
||
_explosion = new ExplosionParticleSystem(this, 1, _camera);
|
||
Components.Add(_explosion);
|
||
|
||
_smoke = new ExplosionSmokeParticleSystem(this, 2, _camera);
|
||
Components.Add(_smoke);
|
||
|
||
_smokePlume = new SmokePlumeParticleSystem(this, 9, _camera);
|
||
Components.Add(_smokePlume);
|
||
|
||
_networkMessenger = new NetworkMessenger(this, NetworkClient);
|
||
Components.Add(_networkMessenger);
|
||
|
||
Components.Add(new ScoreBoard(this, NetworkClient));
|
||
}
|
||
|
||
public void AddExplosion(Vector2 position, float size)
|
||
{
|
||
_explosion.AddParticles(position, size);
|
||
_smoke.AddParticles(position, size);
|
||
}
|
||
|
||
public MainGame()
|
||
{
|
||
Trace.Listeners.Add(new TextWriterTraceListener("debug.log"));
|
||
Trace.AutoFlush = true;
|
||
Trace.Indent();
|
||
|
||
Trace.WriteLine(string.Empty);
|
||
Trace.WriteLine(DateTime.Now);
|
||
Trace.WriteLine("------------------------------------------------------------------------------------------------------------------");
|
||
|
||
Window.Title = "Space, pew pew!";
|
||
Content.RootDirectory = "Content";
|
||
|
||
NetworkServer = new UdpServer();
|
||
NetworkClient = new UdpClient(this);
|
||
|
||
_graphics = new GraphicsDeviceManager(this);
|
||
_entityFactory = new EntityFactory(this);
|
||
|
||
SoundManager.Initialize(this);
|
||
TextureManager.Initialize(this);
|
||
|
||
IsMouseVisible = true;
|
||
|
||
_graphics.CreateDevice();
|
||
|
||
//Cursor.Hide();
|
||
|
||
_graphics.PreferredBackBufferWidth = 1366;
|
||
_graphics.PreferredBackBufferHeight = 768;
|
||
|
||
//var screen = Screen.AllScreens.First(e => e.Primary);
|
||
|
||
//Window.IsBorderless = true;
|
||
//Window.Position = new Point(screen.Bounds.X, screen.Bounds.Y);
|
||
//_graphics.PreferredBackBufferWidth = screen.Bounds.Width;
|
||
//_graphics.PreferredBackBufferHeight = screen.Bounds.Height;
|
||
|
||
_graphics.ApplyChanges();
|
||
|
||
CenterX = _graphics.PreferredBackBufferWidth / 2;
|
||
CenterY = _graphics.PreferredBackBufferHeight / 2;
|
||
|
||
_randomizer = new Random();
|
||
|
||
_udpClientGui = new UdpNetworkGui(this, _graphics, (UdpClient)NetworkClient, (UdpServer)NetworkServer);
|
||
Components.Add(_udpClientGui);
|
||
}
|
||
|
||
/// <summary>
|
||
/// LoadContent will be called once per game and is the place to load
|
||
/// all of your content.
|
||
/// </summary>
|
||
protected override void LoadContent()
|
||
{
|
||
_udpClientGui.Initialize();
|
||
|
||
_spriteBatch = new SpriteBatch(GraphicsDevice);
|
||
|
||
_backgroundTexture = TextureManager.LoadTexture("stars");
|
||
}
|
||
|
||
/// <summary>
|
||
/// UnloadContent will be called once per game and is the place to unload
|
||
/// all content.
|
||
/// </summary>
|
||
protected override void UnloadContent()
|
||
{
|
||
}
|
||
|
||
protected override void OnExiting(object sender, EventArgs args)
|
||
{
|
||
if (_level != null)
|
||
{
|
||
_level.StopLevelSong();
|
||
}
|
||
|
||
if (NetworkClient.IsSessionAlive)
|
||
{
|
||
NetworkClient.ExitSession("Quitting...");
|
||
}
|
||
|
||
NetworkServer.Shutdown();
|
||
|
||
Trace.WriteLine("Exiting");
|
||
Trace.Unindent();
|
||
Trace.Flush();
|
||
|
||
base.OnExiting(sender, args);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Handles input.
|
||
/// </summary>
|
||
private void HandleInput()
|
||
{
|
||
_currentKeyboardState = Keyboard.GetState();
|
||
_currentGamePadState = GamePad.GetState(PlayerIndex.One);
|
||
|
||
// Check for exit.
|
||
if (IsActive && IsPressed(Microsoft.Xna.Framework.Input.Keys.Escape, Buttons.Back))
|
||
{
|
||
Exit();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Checks if the specified button is pressed on either keyboard or gamepad.
|
||
/// </summary>
|
||
bool IsPressed(Microsoft.Xna.Framework.Input.Keys key, Buttons button)
|
||
{
|
||
return (_currentKeyboardState.IsKeyDown(key) || _currentGamePadState.IsButtonDown(button));
|
||
}
|
||
|
||
/// <summary>
|
||
/// Allows the game to run logic such as updating the world,
|
||
/// checking for collisions, gathering input, and playing audio.
|
||
/// </summary>
|
||
/// <param name="gameTime">Provides a snapshot of timing values.</param>
|
||
protected override void Update(GameTime gameTime)
|
||
{
|
||
HandleInput();
|
||
|
||
if (!NetworkClient.IsSessionAlive || NetworkClient.LocalPlayer == null)
|
||
{
|
||
// If we are not in a network session, update the
|
||
// menu screen that will let us create or join one.
|
||
}
|
||
else
|
||
{
|
||
while (WeaponBase.FiredShots.Count > 0)
|
||
{
|
||
NetworkClient.EntitiesToSend.Add(WeaponBase.FiredShots.Dequeue());
|
||
}
|
||
|
||
NetworkClient.Update();
|
||
|
||
NetworkClient.LocalPlayer.HandleKeyboard(Keyboard.GetState());
|
||
|
||
var entitiesToKill = new HashSet<IEntity>();
|
||
|
||
//fixa en metod f<>r entity som sk<73>ter det h<>r..
|
||
foreach (var entity in _entityFactory.Entities)
|
||
{
|
||
entity.Update(gameTime);
|
||
|
||
foreach (var player in NetworkClient.Players)
|
||
{
|
||
if (player.IsCollisionWith(entity))
|
||
{
|
||
player.CollideWith(entity);
|
||
|
||
if (!(entity is Player))
|
||
{
|
||
AddExplosion(entity.Position, 0.05f);
|
||
|
||
entitiesToKill.Add(entity);
|
||
}
|
||
}
|
||
|
||
if (player == NetworkClient.LocalPlayer && player.Health <= 0)
|
||
{
|
||
AddExplosion(player.Position, 1f);
|
||
player.Kill();
|
||
|
||
NetworkClient.SendDeath(entity);
|
||
|
||
var message = new NetworkMessage
|
||
{
|
||
Sent = DateTime.Now
|
||
};
|
||
|
||
if (player.Owner == entity.Owner)
|
||
{
|
||
message.Color = player.Color;
|
||
message.Message = string.Format("- {0} commited suicide.", player.Owner);
|
||
}
|
||
else
|
||
{
|
||
var killer = ((List<Player>)NetworkClient.Players).Find(p => p.Owner == entity.Owner);
|
||
message.Message = string.Format("{0} killed {1}.", entity.Owner, player.Owner);
|
||
message.Color = killer.Color;
|
||
}
|
||
|
||
NetworkMessenger.SendMessage(message);
|
||
}
|
||
}
|
||
|
||
entity.Health -= _level.Collide(entity, CenterX, CenterY);
|
||
|
||
if (NetworkClient.LocalPlayer.Health <= 0)
|
||
{
|
||
AddExplosion(NetworkClient.LocalPlayer.Position, 1f);
|
||
NetworkClient.LocalPlayer.Kill();
|
||
|
||
NetworkMessenger.AddDeath(NetworkClient.LocalPlayer);
|
||
NetworkMessenger.SendMessage(new NetworkMessage
|
||
{
|
||
Color = NetworkClient.LocalPlayer.Color,
|
||
Message = GetRandomCrashMessage(),
|
||
Sent = DateTime.Now
|
||
});
|
||
}
|
||
|
||
if (!(entity is Player) && entity.Health <= 0)
|
||
{
|
||
entitiesToKill.Add(entity);
|
||
|
||
AddExplosion(entity.Position, 0.05f);
|
||
}
|
||
}
|
||
|
||
_entityFactory.RemoveEntities(entitiesToKill);
|
||
}
|
||
|
||
base.Update(gameTime);
|
||
}
|
||
|
||
private string GetRandomCrashMessage()
|
||
{
|
||
int random = _randomizer.Next(0, 3);
|
||
var crashMessages = new List<string>
|
||
{
|
||
"{0} is driving under influence.",
|
||
"{0} crashed.",
|
||
"{0} sent himself straight towards the wall."
|
||
};
|
||
|
||
return string.Format(crashMessages[random], NetworkClient.LocalPlayer.Owner);
|
||
}
|
||
|
||
/// <summary>
|
||
/// This is called when the game should draw itself.
|
||
/// </summary>
|
||
/// <param name="gameTime">Provides a snapshot of timing values.</param>
|
||
protected override void Draw(GameTime gameTime)
|
||
{
|
||
GraphicsDevice.Clear(Color.Black);
|
||
|
||
if (NetworkClient.IsSessionAlive)
|
||
{
|
||
if (!BeginDraw() || NetworkClient.LocalPlayer == null)
|
||
return;
|
||
|
||
_graphics.GraphicsDevice.Clear(Color.Black);
|
||
|
||
RenderDeformation();
|
||
RenderTiledBackground();
|
||
RenderLevel();
|
||
RenderEntities();
|
||
}
|
||
|
||
base.Draw(gameTime);
|
||
}
|
||
|
||
private static readonly BlendState
|
||
opaqueExceptAlpha = new BlendState
|
||
{
|
||
ColorSourceBlend = Blend.One,
|
||
AlphaSourceBlend = Blend.One,
|
||
ColorDestinationBlend = Blend.InverseDestinationAlpha,
|
||
AlphaDestinationBlend = Blend.InverseDestinationAlpha,
|
||
ColorWriteChannels = ColorWriteChannels.Alpha
|
||
};
|
||
|
||
void RenderDeformation()
|
||
{
|
||
var hits = new List<MapHit>();
|
||
while (_level.Hits.Count > 0)
|
||
{
|
||
hits.Add(_level.Hits.Dequeue());
|
||
}
|
||
|
||
foreach (var tile in _level.DeformedTexture)
|
||
{
|
||
_graphics.GraphicsDevice.SetRenderTarget((RenderTarget2D)tile.Texture);
|
||
_spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend); // TODO: fixa blendstaten d<>r uppe s<> att den tar h<>nsyn till alpha i source
|
||
foreach (var hit in hits)
|
||
{
|
||
_spriteBatch.Draw(hit.Texture,
|
||
hit.Position + _camera.ScreenCenter - tile.Position.AsVector2D(),
|
||
null,
|
||
Color.Black,
|
||
hit.Angle,
|
||
hit.Origin,
|
||
1,
|
||
SpriteEffects.None,
|
||
1);
|
||
}
|
||
|
||
_spriteBatch.End();
|
||
}
|
||
|
||
_graphics.GraphicsDevice.SetRenderTarget(null);
|
||
}
|
||
|
||
private void RenderTiledBackground()
|
||
{
|
||
_spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque);
|
||
|
||
var pos = Vector2.Zero;
|
||
var scrollPos = NetworkClient.LocalPlayer.Position / 3.0f;
|
||
|
||
int startX = ((int)scrollPos.X) % _backgroundTexture.Width;
|
||
int startY = ((int)scrollPos.Y) % _backgroundTexture.Height;
|
||
|
||
for (int y = -startY; y < GraphicsDevice.Viewport.Height; y += _backgroundTexture.Height)
|
||
{
|
||
for (int x = -startX; x < GraphicsDevice.Viewport.Width; x += _backgroundTexture.Width)
|
||
{
|
||
pos.X = x; pos.Y = y;
|
||
_spriteBatch.Draw(_backgroundTexture, pos, Color.White);
|
||
}
|
||
}
|
||
|
||
_spriteBatch.End();
|
||
}
|
||
|
||
void RenderLevel()
|
||
{
|
||
var bounds = new Rectangle(0, 0, _level.Texture.TileWidth, _level.Texture.TileHeight);
|
||
var viewPort = new Rectangle(
|
||
(int)_camera.Position.X - _graphics.GraphicsDevice.Viewport.Width / 2,
|
||
(int)_camera.Position.Y - _graphics.GraphicsDevice.Viewport.Height / 2,
|
||
_graphics.GraphicsDevice.Viewport.Width + _graphics.GraphicsDevice.Viewport.Width / 2,
|
||
_graphics.GraphicsDevice.Viewport.Height + _graphics.GraphicsDevice.Viewport.Height / 2);
|
||
|
||
for (var i = 0; i < _level.Texture.Count; i++)
|
||
{
|
||
var tile = _level.Texture[i];
|
||
var deformedTile = _level.DeformedTexture[i];
|
||
var indestructibleTile = _level.IndestructibleTexture[i];
|
||
|
||
bounds.X = tile.Position.X;
|
||
bounds.Y = tile.Position.Y;
|
||
if (viewPort.Intersects(bounds))
|
||
{
|
||
_spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
|
||
|
||
_spriteBatch.Draw(indestructibleTile.Texture, new Vector2(indestructibleTile.Position.X - _camera.Position.X, indestructibleTile.Position.Y - _camera.Position.Y));
|
||
_spriteBatch.Draw(deformedTile.Texture, new Vector2(deformedTile.Position.X - _camera.Position.X, deformedTile.Position.Y - _camera.Position.Y));
|
||
|
||
_spriteBatch.End();
|
||
}
|
||
}
|
||
}
|
||
|
||
private void RenderEntities()
|
||
{
|
||
_spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null, _camera.Transform);
|
||
|
||
foreach (var entity in _entityFactory.Entities)
|
||
{
|
||
_spriteBatch.Draw(entity.Texture,
|
||
entity.Position,
|
||
null,
|
||
entity.Color,
|
||
entity.Angle,
|
||
entity.Origin,
|
||
1,
|
||
SpriteEffects.None,
|
||
1);
|
||
}
|
||
|
||
_spriteBatch.End();
|
||
}
|
||
}
|
||
}
|