move to github
This commit is contained in:
31
SpacePew/Arena.cs
Normal file
31
SpacePew/Arena.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace SpacePew
|
||||
{
|
||||
public class Arena : DrawableGameComponent
|
||||
{
|
||||
public Arena(Game game) : base(game)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void LoadContent()
|
||||
{
|
||||
base.LoadContent();
|
||||
}
|
||||
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
base.Update(gameTime);
|
||||
}
|
||||
|
||||
public override void Draw(GameTime gameTime)
|
||||
{
|
||||
base.Draw(gameTime);
|
||||
}
|
||||
}
|
||||
}
|
93
SpacePew/Camera/Camera2D.cs
Normal file
93
SpacePew/Camera/Camera2D.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace SpacePew.Camera
|
||||
{
|
||||
public class Camera2D : GameComponent, ICamera2D
|
||||
{
|
||||
private Vector2 _position;
|
||||
protected float _viewportHeight;
|
||||
protected float _viewportWidth;
|
||||
|
||||
public Camera2D(Game game) : base(game) { }
|
||||
|
||||
#region Properties
|
||||
|
||||
public Vector2 Position
|
||||
{
|
||||
get { return _position; }
|
||||
set { _position = value; }
|
||||
}
|
||||
|
||||
public float Rotation { get; set; }
|
||||
public Vector2 Origin { get; set; }
|
||||
public float Scale { get; set; }
|
||||
public Vector2 ScreenCenter { get; protected set; }
|
||||
public Matrix Transform { get; set; }
|
||||
public IFocusable Focus { get; set; }
|
||||
public float MoveSpeed { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Called when the GameComponent needs to be initialized.
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
_viewportWidth = Game.GraphicsDevice.Viewport.Width;
|
||||
_viewportHeight = Game.GraphicsDevice.Viewport.Height;
|
||||
|
||||
ScreenCenter = new Vector2(_viewportWidth / 2, _viewportHeight / 2);
|
||||
Scale = 1;
|
||||
MoveSpeed = 5f;
|
||||
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
// Create the Transform used by any
|
||||
// spritebatch process
|
||||
Transform = Matrix.Identity *
|
||||
Matrix.CreateTranslation(-Position.X, -Position.Y, 0) *
|
||||
Matrix.CreateRotationZ(Rotation) *
|
||||
Matrix.CreateTranslation(Origin.X, Origin.Y, 0) *
|
||||
Matrix.CreateScale(new Vector3(Scale, Scale, Scale));
|
||||
|
||||
Origin = ScreenCenter / Scale;
|
||||
|
||||
// Move the Camera to the position that it needs to go
|
||||
var delta = (float)gameTime.ElapsedGameTime.TotalSeconds;
|
||||
|
||||
_position.X += (Focus.Position.X - Position.X) * MoveSpeed * delta;
|
||||
_position.Y += (Focus.Position.Y - Position.Y) * MoveSpeed * delta;
|
||||
|
||||
base.Update(gameTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the target is in view given the specified position.
|
||||
/// This can be used to increase performance by not drawing objects
|
||||
/// directly in the viewport
|
||||
/// </summary>
|
||||
/// <param name="position">The position.</param>
|
||||
/// <param name="texture">The texture.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if [is in view] [the specified position]; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public bool IsInView(Vector2 position, Texture2D texture)
|
||||
{
|
||||
// If the object is not within the horizontal bounds of the screen
|
||||
|
||||
if ((position.X + texture.Width) < (Position.X - Origin.X) || (position.X) > (Position.X + Origin.X))
|
||||
return false;
|
||||
|
||||
// If the object is not within the vertical bounds of the screen
|
||||
if ((position.Y + texture.Height) < (Position.Y - Origin.Y) || (position.Y) > (Position.Y + Origin.Y))
|
||||
return false;
|
||||
|
||||
// In View
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
72
SpacePew/Camera/ICamera2D.cs
Normal file
72
SpacePew/Camera/ICamera2D.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace SpacePew.Camera
|
||||
{
|
||||
public interface ICamera2D
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the camera
|
||||
/// </summary>
|
||||
/// <value>The position.</value>
|
||||
Vector2 Position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the move speed of the camera.
|
||||
/// The camera will tween to its destination.
|
||||
/// </summary>
|
||||
/// <value>The move speed.</value>
|
||||
float MoveSpeed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rotation of the camera.
|
||||
/// </summary>
|
||||
/// <value>The rotation.</value>
|
||||
float Rotation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the origin of the viewport (accounts for Scale)
|
||||
/// </summary>
|
||||
/// <value>The origin.</value>
|
||||
Vector2 Origin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scale of the Camera
|
||||
/// </summary>
|
||||
/// <value>The scale.</value>
|
||||
float Scale { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the screen center (does not account for Scale)
|
||||
/// </summary>
|
||||
/// <value>The screen center.</value>
|
||||
Vector2 ScreenCenter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform that can be applied to
|
||||
/// the SpriteBatch Class.
|
||||
/// </summary>
|
||||
/// <see cref="SpriteBatch"/>
|
||||
/// <value>The transform.</value>
|
||||
Matrix Transform { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the focus of the Camera.
|
||||
/// </summary>
|
||||
/// <seealso cref="IFocusable"/>
|
||||
/// <value>The focus.</value>
|
||||
IFocusable Focus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the target is in view given the specified position.
|
||||
/// This can be used to increase performance by not drawing objects
|
||||
/// directly in the viewport
|
||||
/// </summary>
|
||||
/// <param name="position">The position.</param>
|
||||
/// <param name="texture">The texture.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the target is in view at the specified position; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
bool IsInView(Vector2 position, Texture2D texture);
|
||||
}
|
||||
}
|
9
SpacePew/Camera/IFocusable.cs
Normal file
9
SpacePew/Camera/IFocusable.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace SpacePew.Camera
|
||||
{
|
||||
public interface IFocusable
|
||||
{
|
||||
Vector2 Position { get; }
|
||||
}
|
||||
}
|
BIN
SpacePew/Content/Skins/Blue.skin
Normal file
BIN
SpacePew/Content/Skins/Blue.skin
Normal file
Binary file not shown.
BIN
SpacePew/Content/Skins/Default.skin
Normal file
BIN
SpacePew/Content/Skins/Default.skin
Normal file
Binary file not shown.
BIN
SpacePew/Content/Skins/Green.skin
Normal file
BIN
SpacePew/Content/Skins/Green.skin
Normal file
Binary file not shown.
BIN
SpacePew/Content/Skins/Magenta.skin
Normal file
BIN
SpacePew/Content/Skins/Magenta.skin
Normal file
Binary file not shown.
BIN
SpacePew/Content/Skins/Purple.skin
Normal file
BIN
SpacePew/Content/Skins/Purple.skin
Normal file
Binary file not shown.
0
SpacePew/Downloads/Levels/dummy.txt
Normal file
0
SpacePew/Downloads/Levels/dummy.txt
Normal file
88
SpacePew/EntityFactory.cs
Normal file
88
SpacePew/EntityFactory.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using SpacePew.Models;
|
||||
|
||||
namespace SpacePew
|
||||
{
|
||||
public class EntityFactory
|
||||
{
|
||||
public static EntityFactory Instance { get; private set; }
|
||||
|
||||
private MainGame _game;
|
||||
private readonly List<IEntity> _entities = new List<IEntity>();
|
||||
|
||||
public IList<IEntity> Entities
|
||||
{
|
||||
get { return _entities; }
|
||||
}
|
||||
|
||||
public void RemoveEntities(IEnumerable<IEntity> entities)
|
||||
{
|
||||
Action<IEntity> a = e =>
|
||||
{
|
||||
e.Collide(null);
|
||||
_entities.Remove(e);
|
||||
};
|
||||
|
||||
entities.ToList().ForEach(e =>
|
||||
{
|
||||
if (e is IKillable)
|
||||
(e as IKillable).Kill();
|
||||
|
||||
_entities.Remove(e);
|
||||
});
|
||||
}
|
||||
|
||||
public void RemoveEntity(IEntity entity)
|
||||
{
|
||||
_entities.Remove(entity);
|
||||
}
|
||||
|
||||
public EntityFactory(MainGame game)
|
||||
{
|
||||
Instance = this;
|
||||
this._game = game;
|
||||
}
|
||||
|
||||
public T CreateEntity<T>(string owner, Vector2 position, Vector2 velocity, float angle) where T : IEntity, new()
|
||||
{
|
||||
var entity = new T();
|
||||
|
||||
var tex = TextureManager.LoadTexture(entity.TextureName);
|
||||
entity.Texture = tex;
|
||||
entity.Position = position;
|
||||
entity.Velocity = velocity;
|
||||
entity.Angle = angle;
|
||||
|
||||
entity.Owner = owner;
|
||||
|
||||
this._entities.Add(entity);
|
||||
|
||||
entity.Created();
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public T CreateEntity<T>(Type entityType, string owner, Vector2 position, Vector2 velocity, float angle) where T : IEntity
|
||||
{
|
||||
T entity = (T)entityType.Assembly.CreateInstance(entityType.FullName);
|
||||
|
||||
Texture2D tex = TextureManager.LoadTexture(entity.TextureName);
|
||||
entity.Texture = tex;
|
||||
entity.Position = position;
|
||||
entity.Velocity = velocity;
|
||||
entity.Angle = angle;
|
||||
|
||||
entity.Owner = owner;
|
||||
|
||||
this._entities.Add(entity);
|
||||
|
||||
entity.Created();
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
}
|
294
SpacePew/Extensions/LidgrenExtensions.cs
Normal file
294
SpacePew/Extensions/LidgrenExtensions.cs
Normal file
@@ -0,0 +1,294 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics.PackedVector;
|
||||
|
||||
using Lidgren.Network;
|
||||
|
||||
namespace SpacePew.Extensions
|
||||
{
|
||||
public static class LidgrenExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Write a Point
|
||||
/// </summary>
|
||||
public static void Write(this NetBuffer message, Point value)
|
||||
{
|
||||
message.Write(value.X);
|
||||
message.Write(value.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Point
|
||||
/// </summary>
|
||||
public static Point ReadPoint(this NetBuffer message)
|
||||
{
|
||||
return new Point(message.ReadInt32(), message.ReadInt32());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Single with half precision (16 bits)
|
||||
/// </summary>
|
||||
public static void WriteHalfPrecision(this NetBuffer message, float value)
|
||||
{
|
||||
message.Write(new HalfSingle(value).PackedValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a half precision Single written using WriteHalfPrecision(float)
|
||||
/// </summary>
|
||||
public static float ReadHalfPrecisionSingle(this NetBuffer message)
|
||||
{
|
||||
HalfSingle h = new HalfSingle();
|
||||
h.PackedValue = message.ReadUInt16();
|
||||
return h.ToSingle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a Vector2
|
||||
/// </summary>
|
||||
public static void Write(this NetBuffer message, Vector2 vector)
|
||||
{
|
||||
message.Write(vector.X);
|
||||
message.Write(vector.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a Vector2
|
||||
/// </summary>
|
||||
public static Vector2 ReadVector2(this NetBuffer message)
|
||||
{
|
||||
Vector2 retval;
|
||||
retval.X = message.ReadSingle();
|
||||
retval.Y = message.ReadSingle();
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a Vector3
|
||||
/// </summary>
|
||||
public static void Write(this NetBuffer message, Vector3 vector)
|
||||
{
|
||||
message.Write(vector.X);
|
||||
message.Write(vector.Y);
|
||||
message.Write(vector.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a Vector3 at half precision
|
||||
/// </summary>
|
||||
public static void WriteHalfPrecision(this NetBuffer message, Vector3 vector)
|
||||
{
|
||||
message.Write(new HalfSingle(vector.X).PackedValue);
|
||||
message.Write(new HalfSingle(vector.Y).PackedValue);
|
||||
message.Write(new HalfSingle(vector.Z).PackedValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a Vector3
|
||||
/// </summary>
|
||||
public static Vector3 ReadVector3(this NetBuffer message)
|
||||
{
|
||||
Vector3 retval;
|
||||
retval.X = message.ReadSingle();
|
||||
retval.Y = message.ReadSingle();
|
||||
retval.Z = message.ReadSingle();
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a Vector3 at half precision
|
||||
/// </summary>
|
||||
public static Vector3 ReadHalfPrecisionVector3(this NetBuffer message)
|
||||
{
|
||||
HalfSingle hx = new HalfSingle();
|
||||
hx.PackedValue = message.ReadUInt16();
|
||||
|
||||
HalfSingle hy = new HalfSingle();
|
||||
hy.PackedValue = message.ReadUInt16();
|
||||
|
||||
HalfSingle hz = new HalfSingle();
|
||||
hz.PackedValue = message.ReadUInt16();
|
||||
|
||||
Vector3 retval;
|
||||
retval.X = hx.ToSingle();
|
||||
retval.Y = hy.ToSingle();
|
||||
retval.Z = hz.ToSingle();
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a Vector4
|
||||
/// </summary>
|
||||
public static void Write(this NetBuffer message, Vector4 vector)
|
||||
{
|
||||
message.Write(vector.X);
|
||||
message.Write(vector.Y);
|
||||
message.Write(vector.Z);
|
||||
message.Write(vector.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a Vector4
|
||||
/// </summary>
|
||||
public static Vector4 ReadVector4(this NetBuffer message)
|
||||
{
|
||||
Vector4 retval;
|
||||
retval.X = message.ReadSingle();
|
||||
retval.Y = message.ReadSingle();
|
||||
retval.Z = message.ReadSingle();
|
||||
retval.W = message.ReadSingle();
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Writes a unit vector (ie. a vector of length 1.0, for example a surface normal)
|
||||
/// using specified number of bits
|
||||
/// </summary>
|
||||
public static void WriteUnitVector3(this NetBuffer message, Vector3 unitVector, int numberOfBits)
|
||||
{
|
||||
float x = unitVector.X;
|
||||
float y = unitVector.Y;
|
||||
float z = unitVector.Z;
|
||||
double invPi = 1.0 / Math.PI;
|
||||
float phi = (float)(Math.Atan2(x, y) * invPi);
|
||||
float theta = (float)(Math.Atan2(z, Math.Sqrt(x * x + y * y)) * (invPi * 2));
|
||||
|
||||
int halfBits = numberOfBits / 2;
|
||||
message.WriteSignedSingle(phi, halfBits);
|
||||
message.WriteSignedSingle(theta, numberOfBits - halfBits);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a unit vector written using WriteUnitVector3(numberOfBits)
|
||||
/// </summary>
|
||||
public static Vector3 ReadUnitVector3(this NetBuffer message, int numberOfBits)
|
||||
{
|
||||
int halfBits = numberOfBits / 2;
|
||||
float phi = message.ReadSignedSingle(halfBits) * (float)Math.PI;
|
||||
float theta = message.ReadSignedSingle(numberOfBits - halfBits) * (float)(Math.PI * 0.5);
|
||||
|
||||
Vector3 retval;
|
||||
retval.X = (float)(Math.Sin(phi) * Math.Cos(theta));
|
||||
retval.Y = (float)(Math.Cos(phi) * Math.Cos(theta));
|
||||
retval.Z = (float)Math.Sin(theta);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a unit quaternion using the specified number of bits per element
|
||||
/// for a total of 4 x bitsPerElements bits. Suggested value is 8 to 24 bits.
|
||||
/// </summary>
|
||||
public static void WriteRotation(this NetBuffer message, Quaternion quaternion, int bitsPerElement)
|
||||
{
|
||||
if (quaternion.X > 1.0f)
|
||||
quaternion.X = 1.0f;
|
||||
if (quaternion.Y > 1.0f)
|
||||
quaternion.Y = 1.0f;
|
||||
if (quaternion.Z > 1.0f)
|
||||
quaternion.Z = 1.0f;
|
||||
if (quaternion.W > 1.0f)
|
||||
quaternion.W = 1.0f;
|
||||
if (quaternion.X < -1.0f)
|
||||
quaternion.X = -1.0f;
|
||||
if (quaternion.Y < -1.0f)
|
||||
quaternion.Y = -1.0f;
|
||||
if (quaternion.Z < -1.0f)
|
||||
quaternion.Z = -1.0f;
|
||||
if (quaternion.W < -1.0f)
|
||||
quaternion.W = -1.0f;
|
||||
|
||||
message.WriteSignedSingle(quaternion.X, bitsPerElement);
|
||||
message.WriteSignedSingle(quaternion.Y, bitsPerElement);
|
||||
message.WriteSignedSingle(quaternion.Z, bitsPerElement);
|
||||
message.WriteSignedSingle(quaternion.W, bitsPerElement);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a unit quaternion written using WriteRotation(... ,bitsPerElement)
|
||||
/// </summary>
|
||||
public static Quaternion ReadRotation(this NetBuffer message, int bitsPerElement)
|
||||
{
|
||||
Quaternion retval;
|
||||
retval.X = message.ReadSignedSingle(bitsPerElement);
|
||||
retval.Y = message.ReadSignedSingle(bitsPerElement);
|
||||
retval.Z = message.ReadSignedSingle(bitsPerElement);
|
||||
retval.W = message.ReadSignedSingle(bitsPerElement);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an orthonormal matrix (rotation, translation but not scaling or projection)
|
||||
/// </summary>
|
||||
public static void WriteMatrix(this NetBuffer message, ref Matrix matrix)
|
||||
{
|
||||
Quaternion rot = Quaternion.CreateFromRotationMatrix(matrix);
|
||||
WriteRotation(message, rot, 24);
|
||||
message.Write(matrix.M41);
|
||||
message.Write(matrix.M42);
|
||||
message.Write(matrix.M43);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an orthonormal matrix (rotation, translation but no scaling or projection)
|
||||
/// </summary>
|
||||
public static void WriteMatrix(this NetBuffer message, Matrix matrix)
|
||||
{
|
||||
Quaternion rot = Quaternion.CreateFromRotationMatrix(matrix);
|
||||
WriteRotation(message, rot, 24);
|
||||
message.Write(matrix.M41);
|
||||
message.Write(matrix.M42);
|
||||
message.Write(matrix.M43);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a matrix written using WriteMatrix()
|
||||
/// </summary>
|
||||
public static Matrix ReadMatrix(this NetBuffer message)
|
||||
{
|
||||
Quaternion rot = ReadRotation(message, 24);
|
||||
Matrix retval = Matrix.CreateFromQuaternion(rot);
|
||||
retval.M41 = message.ReadSingle();
|
||||
retval.M42 = message.ReadSingle();
|
||||
retval.M43 = message.ReadSingle();
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a matrix written using WriteMatrix()
|
||||
/// </summary>
|
||||
public static void ReadMatrix(this NetBuffer message, ref Matrix destination)
|
||||
{
|
||||
Quaternion rot = ReadRotation(message, 24);
|
||||
destination = Matrix.CreateFromQuaternion(rot);
|
||||
destination.M41 = message.ReadSingle();
|
||||
destination.M42 = message.ReadSingle();
|
||||
destination.M43 = message.ReadSingle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a bounding sphere
|
||||
/// </summary>
|
||||
public static void Write(this NetBuffer message, BoundingSphere bounds)
|
||||
{
|
||||
message.Write(bounds.Center.X);
|
||||
message.Write(bounds.Center.Y);
|
||||
message.Write(bounds.Center.Z);
|
||||
message.Write(bounds.Radius);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a bounding sphere written using Write(message, BoundingSphere)
|
||||
/// </summary>
|
||||
public static BoundingSphere ReadBoundingSphere(this NetBuffer message)
|
||||
{
|
||||
BoundingSphere retval;
|
||||
retval.Center.X = message.ReadSingle();
|
||||
retval.Center.Y = message.ReadSingle();
|
||||
retval.Center.Z = message.ReadSingle();
|
||||
retval.Radius = message.ReadSingle();
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
42
SpacePew/Extensions/RenderExtensions.cs
Normal file
42
SpacePew/Extensions/RenderExtensions.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using SpacePew.Models;
|
||||
|
||||
namespace SpacePew.Extensions
|
||||
{
|
||||
public static class RenderExtensions
|
||||
{
|
||||
|
||||
public static Vector2 AsVector2D(this Point p)
|
||||
{
|
||||
return new Vector2(p.X, p.Y);
|
||||
}
|
||||
|
||||
public static void Draw(this SpriteBatch batch, TiledTexture texture, Rectangle destination, Color color)
|
||||
{
|
||||
Draw(batch, texture, destination, new Rectangle(0, 0, texture.Width, texture.Height), color);
|
||||
}
|
||||
|
||||
public static void Draw(this SpriteBatch batch, TiledTexture texture, Rectangle dstRect, Rectangle srcRect, Color color)
|
||||
{
|
||||
//TODO: kolla om dom ens syns innan man renderar.. hur man nu ska kunna göra det..
|
||||
var wratio = dstRect.Width / (double)srcRect.Width;
|
||||
var hratio = dstRect.Height / (double)srcRect.Height;
|
||||
var pos = new Point(dstRect.X, dstRect.Y);
|
||||
dstRect = new Rectangle(0, 0, (int)(wratio * texture.TileWidth), (int)(hratio * texture.TileHeight));
|
||||
|
||||
for (var j = 0; j < texture.XTiles; j++)
|
||||
{
|
||||
for (var i = 0; i < texture.YTiles; i++)
|
||||
{
|
||||
//if srcRect.Intersects tile
|
||||
var tile = texture[i * texture.XTiles + j];
|
||||
dstRect.X = pos.X + (int)(tile.Position.X * wratio);
|
||||
dstRect.Y = pos.Y + (int)(tile.Position.Y * hratio);
|
||||
|
||||
batch.Draw(tile.Texture, dstRect, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
BIN
SpacePew/Game.ico
Normal file
BIN
SpacePew/Game.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
BIN
SpacePew/GameThumbnail.png
Normal file
BIN
SpacePew/GameThumbnail.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.6 KiB |
112
SpacePew/Hud.cs
Normal file
112
SpacePew/Hud.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using SpacePew.Models;
|
||||
|
||||
|
||||
namespace SpacePew
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a game component that implements IUpdateable.
|
||||
/// </summary>
|
||||
public class Hud : GameComponent, IDrawable
|
||||
{
|
||||
SpriteBatch _spriteBatch;
|
||||
readonly MainGame _game;
|
||||
|
||||
SpriteFont _consoleFont;
|
||||
|
||||
public Hud(MainGame game)
|
||||
: base(game)
|
||||
{
|
||||
_game = game;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows the game component to perform any initialization it needs to before starting
|
||||
/// to run. This is where it can query for any required services and load content.
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
_spriteBatch = new SpriteBatch(_game.GraphicsDevice);
|
||||
_consoleFont = _game.Content.Load<SpriteFont>("Fonts\\ConsoleFont");
|
||||
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
public void Draw(GameTime gameTime)
|
||||
{
|
||||
if (_game.NetworkClient.LocalPlayer != null)
|
||||
{
|
||||
_spriteBatch.Begin();
|
||||
_spriteBatch.DrawString(_consoleFont,
|
||||
string.Format("Health: {0}",
|
||||
_game.NetworkClient.LocalPlayer.Health),
|
||||
new Vector2(300, _game.GraphicsDevice.Viewport.Height - 100),
|
||||
Color.White);
|
||||
|
||||
_spriteBatch.DrawString(_consoleFont,
|
||||
string.Format("Fuel: {0}",
|
||||
_game.NetworkClient.LocalPlayer.Fuel),
|
||||
new Vector2(300, _game.GraphicsDevice.Viewport.Height - 75),
|
||||
Color.White);
|
||||
|
||||
_spriteBatch.DrawString(_consoleFont,
|
||||
string.Format("Heat #1: {0}",
|
||||
_game.NetworkClient.LocalPlayer.Weapon.Heat),
|
||||
new Vector2(500, _game.GraphicsDevice.Viewport.Height - 100),
|
||||
Color.White);
|
||||
|
||||
_spriteBatch.DrawString(_consoleFont,
|
||||
string.Format("Heat #2: {0}",
|
||||
_game.NetworkClient.LocalPlayer.SecondaryWeapon.Heat),
|
||||
new Vector2(500, _game.GraphicsDevice.Viewport.Height - 75),
|
||||
Color.White);
|
||||
|
||||
_spriteBatch.DrawString(_consoleFont,
|
||||
string.Format("Entities: {0}",
|
||||
((List<IEntity>)EntityFactory.Instance.Entities).Count),
|
||||
new Vector2(500, _game.GraphicsDevice.Viewport.Height - 50),
|
||||
Color.White);
|
||||
|
||||
_spriteBatch.DrawString(_consoleFont,
|
||||
string.Format("Pos X: {0}",
|
||||
_game.NetworkClient.LocalPlayer.Position.X),
|
||||
new Vector2(700, _game.GraphicsDevice.Viewport.Height - 100),
|
||||
Color.White);
|
||||
|
||||
_spriteBatch.DrawString(_consoleFont,
|
||||
string.Format("Pos Y: {0}",
|
||||
_game.NetworkClient.LocalPlayer.Position.Y),
|
||||
new Vector2(700, _game.GraphicsDevice.Viewport.Height - 75),
|
||||
Color.White);
|
||||
|
||||
_spriteBatch.End();
|
||||
}
|
||||
}
|
||||
|
||||
public int DrawOrder
|
||||
{
|
||||
get { return 2; }
|
||||
}
|
||||
|
||||
public bool Visible
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
event EventHandler<EventArgs> IDrawable.DrawOrderChanged
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
event EventHandler<EventArgs> IDrawable.VisibleChanged
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
}
|
||||
}
|
BIN
SpacePew/Icon.ico
Normal file
BIN
SpacePew/Icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
663
SpacePew/KeyboardHelper.cs
Normal file
663
SpacePew/KeyboardHelper.cs
Normal file
@@ -0,0 +1,663 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace SpacePew
|
||||
{
|
||||
[Flags]
|
||||
public enum KbModifiers
|
||||
{
|
||||
None = 0,
|
||||
Ctrl = 1,
|
||||
Shift = 2,
|
||||
Alt = 4,
|
||||
}
|
||||
|
||||
public static class KeyboardHelper
|
||||
{
|
||||
public static bool TreadNumpadAsNumeric = true;
|
||||
private static readonly string[] _unShiftedKeysString = new string[256]
|
||||
{
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
" ",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
"d",
|
||||
"e",
|
||||
"f",
|
||||
"g",
|
||||
"h",
|
||||
"i",
|
||||
"j",
|
||||
"k",
|
||||
"l",
|
||||
"m",
|
||||
"n",
|
||||
"o",
|
||||
"p",
|
||||
"q",
|
||||
"r",
|
||||
"s",
|
||||
"t",
|
||||
"u",
|
||||
"v",
|
||||
"w",
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"*",
|
||||
"+",
|
||||
"",
|
||||
"-",
|
||||
".",
|
||||
"/",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
";",
|
||||
"=",
|
||||
",",
|
||||
"-",
|
||||
".",
|
||||
"/",
|
||||
"`",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"[",
|
||||
"\\",
|
||||
"]",
|
||||
"'",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
};
|
||||
private static string[] shiftedkeysstring = new string[256]
|
||||
{
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
" ",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
")",
|
||||
"!",
|
||||
"@",
|
||||
"#",
|
||||
"$",
|
||||
"%",
|
||||
"^",
|
||||
"&",
|
||||
"*",
|
||||
"(",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
"I",
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
"M",
|
||||
"N",
|
||||
"O",
|
||||
"P",
|
||||
"Q",
|
||||
"R",
|
||||
"S",
|
||||
"T",
|
||||
"U",
|
||||
"V",
|
||||
"W",
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"*",
|
||||
"+",
|
||||
"",
|
||||
"-",
|
||||
".",
|
||||
"/",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
":",
|
||||
"+",
|
||||
"<",
|
||||
"_",
|
||||
">",
|
||||
"?",
|
||||
"~",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"{",
|
||||
"|",
|
||||
"}",
|
||||
"\"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
};
|
||||
|
||||
static KeyboardHelper()
|
||||
{
|
||||
}
|
||||
|
||||
public static bool IsKeyAlpha(Keys k)
|
||||
{
|
||||
return k >= Keys.A && k <= Keys.Z;
|
||||
}
|
||||
|
||||
public static bool IsKeyNumber(Keys k)
|
||||
{
|
||||
return k >= Keys.D0 && k <= Keys.D9;
|
||||
}
|
||||
|
||||
public static bool IsKeyNumberpad(Keys k)
|
||||
{
|
||||
return k >= Keys.NumPad0 && k <= Keys.NumPad9;
|
||||
}
|
||||
|
||||
public static bool IsKeyNumeric(Keys k)
|
||||
{
|
||||
return KeyboardHelper.IsKeyNumber(k) || KeyboardHelper.TreadNumpadAsNumeric && KeyboardHelper.IsKeyNumberpad(k);
|
||||
}
|
||||
|
||||
public static bool IsKeyAlphanumeric(Keys k)
|
||||
{
|
||||
return KeyboardHelper.IsKeyAlpha(k) || KeyboardHelper.IsKeyNumeric(k);
|
||||
}
|
||||
|
||||
public static bool IsFkey(Keys k)
|
||||
{
|
||||
return k >= Keys.F1 && k <= Keys.F12;
|
||||
}
|
||||
|
||||
public static bool IsKeySpace(Keys k)
|
||||
{
|
||||
return k == Keys.Space;
|
||||
}
|
||||
|
||||
public static bool IsShift(Keys k)
|
||||
{
|
||||
return k == Keys.LeftShift || k == Keys.RightShift;
|
||||
}
|
||||
|
||||
public static bool IsCtrl(Keys k)
|
||||
{
|
||||
return k == Keys.LeftControl || k == Keys.RightControl;
|
||||
}
|
||||
|
||||
public static bool IsAlt(Keys k)
|
||||
{
|
||||
return k == Keys.LeftAlt || k == Keys.RightAlt;
|
||||
}
|
||||
|
||||
public static bool IsShiftDown(KbModifiers m)
|
||||
{
|
||||
return (KbModifiers.Shift & m) == KbModifiers.Shift;
|
||||
}
|
||||
|
||||
public static bool IsCtrlDown(KbModifiers m)
|
||||
{
|
||||
return (KbModifiers.Ctrl & m) == KbModifiers.Ctrl;
|
||||
}
|
||||
|
||||
public static bool IsAltDown(KbModifiers m)
|
||||
{
|
||||
return (KbModifiers.Alt & m) == KbModifiers.Alt;
|
||||
}
|
||||
|
||||
public static bool IsMod(Keys k)
|
||||
{
|
||||
return KeyboardHelper.IsShift(k) || KeyboardHelper.IsAlt(k) || KeyboardHelper.IsCtrl(k);
|
||||
}
|
||||
|
||||
public static KbModifiers IsShiftM(Keys k)
|
||||
{
|
||||
return k == Keys.LeftShift || k == Keys.RightShift ? KbModifiers.Shift : KbModifiers.None;
|
||||
}
|
||||
|
||||
public static KbModifiers IsCtrlM(Keys k)
|
||||
{
|
||||
return k == Keys.LeftControl || k == Keys.RightControl ? KbModifiers.Ctrl : KbModifiers.None;
|
||||
}
|
||||
|
||||
public static KbModifiers IsAltM(Keys k)
|
||||
{
|
||||
return k == Keys.LeftAlt || k == Keys.RightAlt ? KbModifiers.Alt : KbModifiers.None;
|
||||
}
|
||||
|
||||
public static string ToPrintableString(Keys k, KbModifiers m)
|
||||
{
|
||||
return KeyboardHelper.ToPrintableString(k, m, true, true, true, false);
|
||||
}
|
||||
|
||||
public static string ToPrintableString(Keys k, KbModifiers m, bool selectspecials)
|
||||
{
|
||||
return KeyboardHelper.ToPrintableString(k, m, selectspecials, true, true, false);
|
||||
}
|
||||
|
||||
public static string ToPrintableString(Keys k, KbModifiers m, bool selectspecials, bool selectalphas, bool selectnumerics)
|
||||
{
|
||||
return KeyboardHelper.ToPrintableString(k, m, selectspecials, selectalphas, selectnumerics, false);
|
||||
}
|
||||
|
||||
public static string ToPrintableString(Keys k, KbModifiers m, bool selectspecials, bool selectalphas, bool selectnumerics, bool suppressspace)
|
||||
{
|
||||
if (KeyboardHelper.IsKeySpace(k) && !suppressspace)
|
||||
return " ";
|
||||
if (KeyboardHelper.IsKeyAlpha(k) && selectalphas || KeyboardHelper.IsKeyNumber(k) && selectnumerics || KeyboardHelper.TreadNumpadAsNumeric && KeyboardHelper.IsKeyNumberpad(k) && selectnumerics || selectspecials && (!KeyboardHelper.IsKeyAlpha(k) && !KeyboardHelper.IsKeyNumeric(k) || KeyboardHelper.IsKeyNumber(k) && KeyboardHelper.IsShiftDown(m)))
|
||||
{
|
||||
if (!KeyboardHelper.IsShiftDown(m))
|
||||
return KeyboardHelper._unShiftedKeysString[k.GetHashCode()];
|
||||
if (selectspecials || !KeyboardHelper.IsKeyNumber(k))
|
||||
return KeyboardHelper.shiftedkeysstring[k.GetHashCode()];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static KbModifiers GetModifiers(KeyboardState ks)
|
||||
{
|
||||
return ks.GetPressedKeys().Aggregate(KbModifiers.None, (current, k) => current | KeyboardHelper.IsShiftM(k) | KeyboardHelper.IsAltM(k) | KeyboardHelper.IsCtrlM(k));
|
||||
}
|
||||
}
|
||||
}
|
181
SpacePew/LevelLoader.cs
Normal file
181
SpacePew/LevelLoader.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Media;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Audio;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using Microsoft.Xna.Framework.Media;
|
||||
using Microsoft.Xna.Framework.Content;
|
||||
using SpacePew.Models;
|
||||
using Rectangle = System.Drawing.Rectangle;
|
||||
|
||||
namespace SpacePew
|
||||
{
|
||||
static class LevelLoader
|
||||
{
|
||||
public static Level LoadLevel(string filePath, ContentManager cm, GraphicsDevice device)
|
||||
{
|
||||
var levelResources = GetResourceList(filePath);
|
||||
if (!levelResources.ContainsKey("leveldata.xml"))
|
||||
throw new InvalidOperationException("Level: " + filePath + " doesnt contain any leveldata.xml");
|
||||
|
||||
var levelData = GetXmlFile(levelResources["leveldata.xml"]);
|
||||
string baseTexture = levelData.SelectSingleNode("//baseTexture").InnerText.ToLower();
|
||||
string indestructibleTexture = levelData.SelectSingleNode("//indestructibleTexture").InnerText.ToLower();
|
||||
|
||||
var spriteBatch = new SpriteBatch(device);
|
||||
var level = new Level();
|
||||
level.FilePath = filePath;
|
||||
level.Texture = LoadTextureTiles(levelResources[baseTexture], device, spriteBatch);
|
||||
level.IndestructibleTexture = LoadTextureTiles(levelResources[indestructibleTexture], device, spriteBatch);
|
||||
level.DeformedTexture = new TiledTexture
|
||||
{
|
||||
Width = level.Texture.Width,
|
||||
Height = level.Texture.Height,
|
||||
TileHeight = level.Texture.TileHeight,
|
||||
TileWidth = level.Texture.TileWidth,
|
||||
XTiles = level.Texture.XTiles,
|
||||
YTiles = level.Texture.YTiles
|
||||
};
|
||||
|
||||
level.Texture.ForEach(t => level.DeformedTexture.Add(new Tile(
|
||||
new RenderTarget2D(device, level.Texture.TileWidth, level.Texture.TileHeight, false,
|
||||
SurfaceFormat.Color, DepthFormat.Depth24,
|
||||
device.PresentationParameters.MultiSampleCount,
|
||||
RenderTargetUsage.PreserveContents
|
||||
),
|
||||
t.Position)));
|
||||
|
||||
if (levelData.SelectSingleNode("//song") != null)
|
||||
{
|
||||
string songName = levelData.SelectSingleNode("//song").InnerText.ToLower();
|
||||
level.OggVorbisSong = levelResources[songName];
|
||||
}
|
||||
|
||||
level.Initialize();
|
||||
return level;
|
||||
}
|
||||
|
||||
private static XmlDocument GetXmlFile(byte[] rawData)
|
||||
{
|
||||
var d = new XmlDocument();
|
||||
d.LoadXml(System.Text.Encoding.UTF8.GetString(rawData));
|
||||
return d;
|
||||
}
|
||||
|
||||
public static TiledTexture LoadTextureTiles(Byte[] bitmapData, GraphicsDevice device, SpriteBatch spriteBatch, int tileWidth = 0, int tileHeight = 0)
|
||||
{
|
||||
using (var ms = new MemoryStream(bitmapData))
|
||||
{
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
using (var image = Image.FromStream(ms))
|
||||
{
|
||||
return LoadTextureTiles(image, device, spriteBatch, tileWidth, tileHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TiledTexture LoadTextureTiles(string fileName, GraphicsDevice device, SpriteBatch spriteBatch, int tileWidth = 0, int tileHeight = 0)
|
||||
{
|
||||
using (var image = Image.FromFile(AppDomain.CurrentDomain.BaseDirectory + fileName.Replace("/", "\\")))
|
||||
{
|
||||
return LoadTextureTiles(image, device, spriteBatch, tileWidth, tileHeight);
|
||||
}
|
||||
}
|
||||
|
||||
public static TiledTexture LoadTextureTiles(Image image, GraphicsDevice device, SpriteBatch spriteBatch, int tileWidth = 0, int tileHeight = 0)
|
||||
{
|
||||
//OBS!!! S<>tt kartans bredd el h<>jd till ett primtal o det <20>r k<>rt iom att d<> kan man inte hitta en j<>mn multiple och en oj<6F>mn kommer att anv<6E>ndas
|
||||
//och det kommer att pricka i framtiden.
|
||||
if (tileWidth == 0)
|
||||
tileWidth = FindNearbyMultiple(image.Width, 1000);
|
||||
if (tileHeight == 0)
|
||||
tileHeight = FindNearbyMultiple(image.Height, 1000);
|
||||
|
||||
|
||||
int tilesX = (image.Width % tileWidth == 0)
|
||||
? (int)Math.Floor(image.Width / (double)tileWidth)
|
||||
: (int)Math.Ceiling(image.Width / (double)tileWidth);
|
||||
|
||||
int tilesY = (image.Height % tileHeight == 0)
|
||||
? (int)Math.Floor(image.Height / (double)tileHeight)
|
||||
: (int)Math.Ceiling(image.Height / (double)tileHeight);
|
||||
|
||||
var ret = new TiledTexture
|
||||
{
|
||||
TileWidth = tileWidth,
|
||||
TileHeight = tileHeight,
|
||||
XTiles = tilesX,
|
||||
YTiles = tilesY,
|
||||
Width = image.Width,
|
||||
Height = image.Height
|
||||
};
|
||||
for (var j = 0; j < tilesX; j++)
|
||||
{
|
||||
for (var i = 0; i < tilesY; i++)
|
||||
{
|
||||
var part = new Bitmap(tileWidth, tileHeight, image.PixelFormat);
|
||||
var graphics = Graphics.FromImage(part);
|
||||
var srcRect = new Rectangle(j * tileWidth, i * tileHeight, tileWidth, tileHeight);
|
||||
graphics.DrawImage(image, 0, 0, srcRect, GraphicsUnit.Pixel);
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
//TODO: n<>got vettigare <20>n s<> h<>r f<>r att f<> fram bytesen... tar ju <20>r och dagar att ladda nu.
|
||||
part.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
ret.Add(new Tile(Texture2D.FromStream(device, ms), new Microsoft.Xna.Framework.Point(j * tileWidth, i * tileHeight)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static int FindNearbyMultiple(int totLen, int chunkLen)
|
||||
{
|
||||
chunkLen = totLen / (int)Math.Ceiling(totLen / (double)chunkLen);
|
||||
|
||||
var step = 0;
|
||||
while (step < 100)
|
||||
{
|
||||
var t = chunkLen - step;
|
||||
if ((totLen % t) == 0)
|
||||
return t;
|
||||
t = chunkLen + step;
|
||||
if ((totLen % t) == 0)
|
||||
return t;
|
||||
step++;
|
||||
}
|
||||
return FindNearbyMultiple(totLen - 1, chunkLen);
|
||||
}
|
||||
|
||||
private static Dictionary<string, byte[]> GetResourceList(string file)
|
||||
{
|
||||
var resources = new Dictionary<string, byte[]>();
|
||||
using (var stream = new ZipInputStream(File.OpenRead(file)))
|
||||
{
|
||||
ZipEntry entry = null;
|
||||
while ((entry = stream.GetNextEntry()) != null)
|
||||
{
|
||||
if (entry.Name.Length > 0)
|
||||
{
|
||||
var buf = new byte[(int)entry.Size];
|
||||
stream.Read(buf, 0, buf.Length);
|
||||
resources.Add(entry.Name.ToLower().Trim(), buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resources;
|
||||
}
|
||||
}
|
||||
}
|
BIN
SpacePew/Levels/hippie.zip
Normal file
BIN
SpacePew/Levels/hippie.zip
Normal file
Binary file not shown.
483
SpacePew/MainGame.cs
Normal file
483
SpacePew/MainGame.cs
Normal file
@@ -0,0 +1,483 @@
|
||||
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;
|
||||
using System.Windows.Forms;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
111
SpacePew/Minimap.cs
Normal file
111
SpacePew/Minimap.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using SpacePew.Extensions;
|
||||
using SpacePew.Models;
|
||||
|
||||
namespace SpacePew
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a game component that implements IUpdateable.
|
||||
/// </summary>
|
||||
public class Minimap : GameComponent, IDrawable
|
||||
{
|
||||
private const int MinimapWidth = 160;
|
||||
|
||||
private int _minimapModifier;
|
||||
|
||||
private readonly MainGame _game;
|
||||
private SpriteBatch _spriteBatch;
|
||||
private int _screenWidth;
|
||||
private int _screenHeight;
|
||||
private Texture2D _miniPlayer;
|
||||
|
||||
public Minimap(MainGame game)
|
||||
: base(game)
|
||||
{
|
||||
_game = game;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows the game component to perform any initialization it needs to before starting
|
||||
/// to run. This is where it can query for any required services and load content.
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
_spriteBatch = new SpriteBatch(_game.GraphicsDevice);
|
||||
|
||||
_screenWidth = _game.GraphicsDevice.PresentationParameters.BackBufferWidth;
|
||||
_screenHeight = _game.GraphicsDevice.PresentationParameters.BackBufferHeight;
|
||||
|
||||
_miniPlayer = TextureManager.LoadTexture("bullet");
|
||||
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
public void Draw(GameTime gameTime)
|
||||
{
|
||||
if (_game.NetworkClient.LocalPlayer != null)
|
||||
{
|
||||
_minimapModifier = _game.Level.Texture.Width / MinimapWidth;
|
||||
|
||||
_spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
|
||||
|
||||
int minimapHeight = _game.Level.Texture.Height / _minimapModifier > _screenHeight ?
|
||||
_screenHeight - 20 :
|
||||
_game.Level.Texture.Height / _screenHeight;
|
||||
|
||||
if (minimapHeight > _screenHeight)
|
||||
minimapHeight = _screenHeight - 20;
|
||||
|
||||
var rect = new Rectangle(10, _screenHeight - minimapHeight - 10,
|
||||
_game.Level.Texture.Width / _minimapModifier,
|
||||
minimapHeight);
|
||||
|
||||
|
||||
//Skriv ut spelarpluppen <20>ver hela ytan i svart f<>r att rensa
|
||||
_spriteBatch.Draw(_miniPlayer, rect, Color.Black);
|
||||
_spriteBatch.Draw(_game.Level.DeformedTexture, rect, Color.White);
|
||||
_spriteBatch.Draw(_game.Level.IndestructibleTexture, rect, Color.White);
|
||||
|
||||
float xScale = _game.Level.Texture.Width / (float)rect.Width;
|
||||
float yScale = _game.Level.Texture.Height / (float)rect.Height;
|
||||
float centerX = _screenWidth / 2;
|
||||
float centerY = _screenHeight / 2;
|
||||
|
||||
foreach (Player player in _game.NetworkClient.Players)
|
||||
{
|
||||
var miniMapPosition = new Vector2(rect.X - 1 + (player.Position.X + centerX) / xScale, rect.Y - 1 + (player.Position.Y + centerY) / yScale);
|
||||
|
||||
_spriteBatch.Draw(_miniPlayer, miniMapPosition, player.Color);
|
||||
}
|
||||
|
||||
_spriteBatch.End();
|
||||
}
|
||||
}
|
||||
|
||||
public int DrawOrder
|
||||
{
|
||||
get { return 2; }
|
||||
}
|
||||
|
||||
public bool Visible
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
|
||||
event EventHandler<EventArgs> IDrawable.DrawOrderChanged
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
event EventHandler<EventArgs> IDrawable.VisibleChanged
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
}
|
||||
}
|
197
SpacePew/Models/EntityBase.cs
Normal file
197
SpacePew/Models/EntityBase.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace SpacePew.Models
|
||||
{
|
||||
public abstract class EntityBase : IEntity
|
||||
{
|
||||
public const float OneRound = (float)Math.PI * 2;
|
||||
public const float VelocityModifier = .1f;
|
||||
public const float GravityModifier = 100f;
|
||||
|
||||
public Rectangle BoundingRectangle
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Rectangle((int)this.Position.X - (int)this.Origin.X, (int)this.Position.Y - (int)this.Origin.Y, this.Texture.Width, this.Texture.Height);
|
||||
}
|
||||
}
|
||||
|
||||
private Color[] _textureData;
|
||||
public Color[] GetTextureData()
|
||||
{
|
||||
if (_textureData == null)
|
||||
{
|
||||
this._textureData = new Color[this.Texture.Width * this.Texture.Height];
|
||||
this.Texture.GetData(this._textureData);
|
||||
}
|
||||
|
||||
return _textureData;
|
||||
}
|
||||
|
||||
// These should be settings in the start menu GUI later on
|
||||
|
||||
public bool IsCollisionWith(IEntity entity)
|
||||
{
|
||||
// never collide with oneself
|
||||
if (ReferenceEquals(entity, this))
|
||||
return false;
|
||||
|
||||
return entity.Collide(this);
|
||||
}
|
||||
|
||||
public virtual bool Collide(IEntity entity)
|
||||
{
|
||||
Matrix transform = Matrix.CreateTranslation(new Vector3(this.Position - this.Origin, 0.0f));
|
||||
|
||||
Matrix entityTransform =
|
||||
Matrix.CreateTranslation(new Vector3(-entity.Origin, 0.0f)) *
|
||||
Matrix.CreateRotationZ(entity.Angle) *
|
||||
Matrix.CreateTranslation(new Vector3(entity.Position, 0.0f));
|
||||
|
||||
Rectangle entityRectangle = CalculateBoundingRectangle(
|
||||
new Rectangle(0, 0, entity.Texture.Width, entity.Texture.Height),
|
||||
entityTransform);
|
||||
|
||||
if (entityRectangle.Intersects(this.BoundingRectangle))
|
||||
{
|
||||
if (IntersectPixels(transform, this.Texture.Width,
|
||||
this.Texture.Height, this.GetTextureData(),
|
||||
entityTransform, entity.Texture.Width,
|
||||
entity.Texture.Height, entity.GetTextureData()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IntersectPixels(Matrix transformA, int widthA, int heightA, Color[] dataA,
|
||||
Matrix transformB, int widthB, int heightB, Color[] dataB)
|
||||
{
|
||||
Matrix transformAtoB = transformA * Matrix.Invert(transformB);
|
||||
|
||||
Vector2 stepX = Vector2.TransformNormal(Vector2.UnitX, transformAtoB);
|
||||
Vector2 stepY = Vector2.TransformNormal(Vector2.UnitY, transformAtoB);
|
||||
|
||||
Vector2 yPosInB = Vector2.Transform(Vector2.Zero, transformAtoB);
|
||||
|
||||
for (int yA = 0; yA < heightA; yA++)
|
||||
{
|
||||
Vector2 posInB = yPosInB;
|
||||
|
||||
for (int xA = 0; xA < widthA; xA++)
|
||||
{
|
||||
var xB = (int)Math.Round(posInB.X);
|
||||
var yB = (int)Math.Round(posInB.Y);
|
||||
|
||||
if (0 <= xB && xB < widthB &&
|
||||
0 <= yB && yB < heightB)
|
||||
{
|
||||
Color colorA = dataA[xA + yA * widthA];
|
||||
Color colorB = dataB[xB + yB * widthB];
|
||||
|
||||
if (colorA.A != 0 && colorB.A != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
posInB += stepX;
|
||||
}
|
||||
|
||||
yPosInB += stepY;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Rectangle CalculateBoundingRectangle(Rectangle rectangle, Matrix transform)
|
||||
{
|
||||
var leftTop = new Vector2(rectangle.Left, rectangle.Top);
|
||||
var rightTop = new Vector2(rectangle.Right, rectangle.Top);
|
||||
var leftBottom = new Vector2(rectangle.Left, rectangle.Bottom);
|
||||
var rightBottom = new Vector2(rectangle.Right, rectangle.Bottom);
|
||||
|
||||
Vector2.Transform(ref leftTop, ref transform, out leftTop);
|
||||
Vector2.Transform(ref rightTop, ref transform, out rightTop);
|
||||
Vector2.Transform(ref leftBottom, ref transform, out leftBottom);
|
||||
Vector2.Transform(ref rightBottom, ref transform, out rightBottom);
|
||||
|
||||
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop), Vector2.Min(leftBottom, rightBottom));
|
||||
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop), Vector2.Max(leftBottom, rightBottom));
|
||||
|
||||
return new Rectangle((int)min.X, (int)min.Y, (int)(max.X - min.X), (int)(max.Y - min.Y));
|
||||
}
|
||||
|
||||
#region IEntity Members
|
||||
|
||||
public virtual string Owner
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public abstract int Health { get; set; }
|
||||
public abstract string TextureName { get; }
|
||||
|
||||
public virtual void CollideWithLevel(Level level) { }
|
||||
|
||||
public Texture2D Texture { get; set; }
|
||||
public virtual Color Color { get; set; }
|
||||
public Vector2 Position { get; set; }
|
||||
public Vector2 Velocity { get; set; }
|
||||
|
||||
public virtual Vector2 Origin
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Vector2(this.Texture.Width / 2, this.Texture.Height / 2);
|
||||
}
|
||||
}
|
||||
|
||||
private float _angle;
|
||||
public float Angle
|
||||
{
|
||||
get
|
||||
{
|
||||
return _angle;
|
||||
}
|
||||
set
|
||||
{
|
||||
_angle = value;
|
||||
|
||||
if (_angle > OneRound)
|
||||
{
|
||||
_angle -= OneRound;
|
||||
}
|
||||
else if (_angle < 0)
|
||||
{
|
||||
_angle += OneRound;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Created() { }
|
||||
|
||||
public virtual void ApplyGravity(GameTime time)
|
||||
{
|
||||
float timeSeconds = time.ElapsedGameTime.Milliseconds * .001f;
|
||||
Velocity = new Vector2(
|
||||
Velocity.X,
|
||||
Velocity.Y + (timeSeconds * GravityModifier));
|
||||
}
|
||||
|
||||
public virtual void Update(GameTime time)
|
||||
{
|
||||
ApplyGravity(time);
|
||||
|
||||
float timeSeconds = time.ElapsedGameTime.Milliseconds * .001f;
|
||||
Position += Velocity * timeSeconds;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
28
SpacePew/Models/Explosion.cs
Normal file
28
SpacePew/Models/Explosion.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace SpacePew.Models
|
||||
{
|
||||
public class Explosion : EntityBase
|
||||
{
|
||||
public Explosion()
|
||||
{
|
||||
_health = 10;
|
||||
}
|
||||
|
||||
private int _health;
|
||||
public override int Health
|
||||
{
|
||||
get
|
||||
{
|
||||
return _health;
|
||||
}
|
||||
set
|
||||
{
|
||||
_health = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override string TextureName
|
||||
{
|
||||
get { return "explosion_small"; }
|
||||
}
|
||||
}
|
||||
}
|
28
SpacePew/Models/IEntity.cs
Normal file
28
SpacePew/Models/IEntity.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace SpacePew.Models
|
||||
{
|
||||
public interface IEntity
|
||||
{
|
||||
string TextureName { get; }
|
||||
Texture2D Texture { get; set; }
|
||||
Color[] GetTextureData();
|
||||
Color Color { get; set; }
|
||||
|
||||
string Owner { get; set; }
|
||||
|
||||
int Health { get; set; }
|
||||
|
||||
Vector2 Origin { get; }
|
||||
Vector2 Position { get; set; }
|
||||
Vector2 Velocity { get; set; }
|
||||
float Angle { get; set; }
|
||||
|
||||
bool Collide(IEntity entity);
|
||||
void CollideWithLevel(Level level);
|
||||
void ApplyGravity(GameTime time);
|
||||
void Update(GameTime time);
|
||||
void Created();
|
||||
}
|
||||
}
|
12
SpacePew/Models/IKillable.cs
Normal file
12
SpacePew/Models/IKillable.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace SpacePew.Models
|
||||
{
|
||||
public interface IKillable
|
||||
{
|
||||
void Kill();
|
||||
}
|
||||
}
|
301
SpacePew/Models/Level.cs
Normal file
301
SpacePew/Models/Level.cs
Normal file
@@ -0,0 +1,301 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections;
|
||||
using Microsoft.Xna.Framework.Media;
|
||||
|
||||
namespace SpacePew.Models
|
||||
{
|
||||
public class Tile
|
||||
{
|
||||
public Point Position { get; set; }
|
||||
public Texture2D Texture { get; set; }
|
||||
public Tile(Texture2D texure, Point position)
|
||||
{
|
||||
Texture = texure;
|
||||
Position = position;
|
||||
}
|
||||
}
|
||||
|
||||
public class TiledTexture : List<Tile>
|
||||
{
|
||||
public int TileWidth { get; set; }
|
||||
public int TileHeight { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
public int XTiles { get; set; }
|
||||
public int YTiles { get; set; }
|
||||
public Vector2 Position { get; set; }
|
||||
|
||||
public void GetData(Color[] textureData)
|
||||
{
|
||||
var buffer = new Color[TileWidth * TileHeight];
|
||||
for (var j = 0; j < XTiles; j++)
|
||||
{
|
||||
for (var i = 0; i < YTiles; i++)
|
||||
{
|
||||
var tile = this[i * XTiles + j];
|
||||
tile.Texture.GetData(buffer);
|
||||
|
||||
for (var x = 0; x < TileWidth; x++)
|
||||
{
|
||||
for (var y = 0; y < TileHeight; y++)
|
||||
{
|
||||
var xpos = tile.Position.X + x;
|
||||
var ypos = tile.Position.Y + y;
|
||||
textureData[ypos * Width + xpos] = buffer[y * TileWidth + x];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetData(Color[] textureData)
|
||||
{
|
||||
var buffer = new Color[TileWidth * TileHeight];
|
||||
for (var j = 0; j < XTiles; j++)
|
||||
{
|
||||
for (var i = 0; i < YTiles; i++)
|
||||
{
|
||||
var tile = this[i * XTiles + j];
|
||||
for (var x = 0; x < TileWidth; x++)
|
||||
{
|
||||
for (var y = 0; y < TileHeight; y++)
|
||||
{
|
||||
var xpos = tile.Position.X + x;
|
||||
var ypos = tile.Position.Y + y;
|
||||
buffer[y * TileWidth + x] = textureData[ypos * Width + xpos];
|
||||
}
|
||||
}
|
||||
|
||||
tile.Texture.SetData(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveAsPng(Stream s, int width, int height)
|
||||
{
|
||||
throw new NotImplementedException("Har inte hunnit med.. borde gå att kopiera nån av Get/Set ovan och skapa upp en bild.");
|
||||
}
|
||||
}
|
||||
|
||||
public class Level
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
public byte[] OggVorbisSong { get; set; }
|
||||
public TiledTexture Texture { get; set; }
|
||||
public TiledTexture IndestructibleTexture { get; set; }
|
||||
public TiledTexture DeformedTexture { get; set; }
|
||||
private Color[] _deformedTextureData;
|
||||
|
||||
public Queue<MapHit> Hits { get; set; }
|
||||
|
||||
public bool[] CollisionData
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool[] IndestructibleCollisionData
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private int _width;
|
||||
private int _height;
|
||||
|
||||
public Level(GraphicsDevice device)
|
||||
{
|
||||
}
|
||||
|
||||
public Level()
|
||||
{ }
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_width = Texture.Width;
|
||||
_height = Texture.Height;
|
||||
|
||||
Hits = new Queue<MapHit>();
|
||||
|
||||
var textureData = new Color[Texture.Width * Texture.Height];
|
||||
Texture.GetData(textureData);
|
||||
|
||||
var indestructibleTextureData = new Color[IndestructibleTexture.Width * IndestructibleTexture.Height];
|
||||
IndestructibleTexture.GetData(indestructibleTextureData);
|
||||
|
||||
CollisionData = new bool[Texture.Width * Texture.Height];
|
||||
|
||||
_deformedTextureData = new Color[DeformedTexture.Width * DeformedTexture.Height];
|
||||
for (int i = _deformedTextureData.Length - 1; i >= 0; i--)
|
||||
{
|
||||
_deformedTextureData[i] = textureData[i].A > 0 ? textureData[i] : Color.Transparent;
|
||||
}
|
||||
|
||||
DeformedTexture.SetData(_deformedTextureData);
|
||||
|
||||
IndestructibleCollisionData = new bool[IndestructibleTexture.Width * IndestructibleTexture.Height];
|
||||
for (int i = 0; i < indestructibleTextureData.Length; i++)
|
||||
{
|
||||
if (indestructibleTextureData[i].A == 0)
|
||||
{
|
||||
IndestructibleCollisionData[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void BuildLevelFromCollisionData()
|
||||
{
|
||||
for (int i = 0; i < CollisionData.Length; i++)
|
||||
{
|
||||
if (CollisionData[i])
|
||||
{
|
||||
_deformedTextureData[i] = Color.Transparent;
|
||||
}
|
||||
}
|
||||
|
||||
DeformedTexture.SetData(_deformedTextureData);
|
||||
}
|
||||
|
||||
public void BuildCollisionDataFromLevel()
|
||||
{
|
||||
for (int i = 0; i < _deformedTextureData.Length; i++)
|
||||
{
|
||||
if (_deformedTextureData[i] == Color.Transparent)
|
||||
{
|
||||
CollisionData[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of pixels of the entity that collides with the background
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="centerX"></param>
|
||||
/// <param name="centerY"></param>
|
||||
/// <returns></returns>
|
||||
public int Collide(IEntity entity, int centerX, int centerY)
|
||||
{
|
||||
int entityWidth = entity.Texture.Width;
|
||||
int entityHeight = entity.Texture.Height;
|
||||
|
||||
int posX = (int)entity.Position.X - (int)entity.Origin.X + centerX;
|
||||
int posY = (int)entity.Position.Y - (int)entity.Origin.Y + centerY;
|
||||
|
||||
if (posX < 1 || posY < 1 || posX > Texture.Width - entityWidth - 1 || posY > Texture.Height - entityHeight - 1)
|
||||
return 1000000000;
|
||||
|
||||
int hit = 0;
|
||||
|
||||
Color[] data = entity.GetTextureData();
|
||||
|
||||
var player = entity as Player;
|
||||
bool isLandingAngle = player != null && (MathHelper.ToDegrees(player.Angle) >= 345 || MathHelper.ToDegrees(player.Angle) <= 15) && player.Velocity.Y > 0;
|
||||
|
||||
for (int y = 0; y < entityHeight; y++)
|
||||
{
|
||||
for (int x = 0; x < entityWidth; x++)
|
||||
{
|
||||
Color colorInPixel = data[(x - entity.Texture.Bounds.X) + (y - entity.Texture.Bounds.Y) * entity.Texture.Bounds.Width];
|
||||
if(colorInPixel.A != 0)
|
||||
{
|
||||
if (CollisionData[(posX + x) + (posY + y) * _width] == false)
|
||||
{
|
||||
if (isLandingAngle)
|
||||
{
|
||||
if (CollisionData[(posX + x) + (posY + y + 1) * _width] == false &&
|
||||
CollisionData[(posX + x + player.Texture.Width) + (posY + y + 1) * _width] == false)
|
||||
{
|
||||
if (player.Velocity.Y > 200f)
|
||||
{
|
||||
var yVelocity = (int)player.Velocity.Y;
|
||||
SoundManager.Play("Audio/Waves/thump", player.Position);
|
||||
player.Velocity = new Vector2(player.Velocity.X / 3, -player.Velocity.Y / 3);
|
||||
return (yVelocity - 200) / 50;
|
||||
}
|
||||
|
||||
player.Land();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
entity.CollideWithLevel(this);
|
||||
|
||||
CollisionData[(posX + x) + (posY + y) * _width] = true;
|
||||
|
||||
Hits.Enqueue(new MapHit(entity));
|
||||
|
||||
hit++;
|
||||
}
|
||||
else if (IndestructibleCollisionData[(posX + x) + (posY + y) * _width] == false)
|
||||
{
|
||||
if (isLandingAngle)
|
||||
{
|
||||
if (IndestructibleCollisionData[(posX + x) + (posY + y + 1) * _width] == false &&
|
||||
IndestructibleCollisionData[(posX + x + player.Texture.Width) + (posY + y + 1) * _width] == false)
|
||||
{
|
||||
if (player.Velocity.Y > 200f)
|
||||
{
|
||||
var yVelocity = (int)player.Velocity.Y;
|
||||
SoundManager.Play("Audio/Waves/thump", player.Position);
|
||||
player.Velocity = new Vector2(player.Velocity.X / 3, -player.Velocity.Y / 3);
|
||||
return (yVelocity - 200) / 50;
|
||||
}
|
||||
|
||||
player.Land();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
entity.CollideWithLevel(this);
|
||||
|
||||
hit++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
return hit / 20;
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
public void StopLevelSong()
|
||||
{
|
||||
_stopMusic = true;
|
||||
}
|
||||
|
||||
private bool _stopMusic;
|
||||
public void PlayLevelSong()
|
||||
{
|
||||
using (var ms = new MemoryStream(this.OggVorbisSong))
|
||||
{
|
||||
using (var vorbis = new NVorbis.NAudioSupport.VorbisWaveReader(ms))
|
||||
{
|
||||
using (var waveOut = new NAudio.Wave.WaveOut())
|
||||
{
|
||||
waveOut.Init(vorbis);
|
||||
waveOut.Play();
|
||||
|
||||
while (!_stopMusic)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
SpacePew/Models/MapHit.cs
Normal file
21
SpacePew/Models/MapHit.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace SpacePew.Models
|
||||
{
|
||||
public class MapHit
|
||||
{
|
||||
public Texture2D Texture { get; private set; }
|
||||
public Vector2 Position { get; private set; }
|
||||
public Vector2 Origin { get; private set; }
|
||||
public float Angle { get; private set; }
|
||||
|
||||
public MapHit(IEntity entity)
|
||||
{
|
||||
Texture = entity.Texture;
|
||||
Position = entity.Position;
|
||||
Origin = entity.Origin;
|
||||
Angle = entity.Angle;
|
||||
}
|
||||
}
|
||||
}
|
295
SpacePew/Models/Player.cs
Normal file
295
SpacePew/Models/Player.cs
Normal file
@@ -0,0 +1,295 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Microsoft.Xna.Framework.Audio;
|
||||
using SpacePew.Networking;
|
||||
using SpacePew.Camera;
|
||||
using SpacePew.Models.Projectiles;
|
||||
using SpacePew.Models.Weapons;
|
||||
|
||||
namespace SpacePew.Models
|
||||
{
|
||||
public class Player : EntityBase, IFocusable
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
private SoundEffectInstance _thrustSound;
|
||||
public Player()
|
||||
{
|
||||
_weapon = new Cannon();
|
||||
_projectile = new Bullet();
|
||||
|
||||
_secondaryWeapon = new ClusterLauncher();
|
||||
_secondaryProjectile = new ClusterBomb();
|
||||
|
||||
Health = 100;
|
||||
|
||||
Fuel = 50000;
|
||||
|
||||
_lastCollide = DateTime.Now;
|
||||
|
||||
_thrustSound = SoundManager.GetSoundEffectInstance("Audio/Waves/engine");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly Vector2 _up = new Vector2(0, -1);
|
||||
private Matrix _rotationMatrix;
|
||||
|
||||
private Vector2 _direction;
|
||||
|
||||
private readonly IProjectile _projectile;
|
||||
private readonly IProjectile _secondaryProjectile;
|
||||
|
||||
private IWeapon _secondaryWeapon;
|
||||
public IWeapon SecondaryWeapon
|
||||
{
|
||||
get { return _secondaryWeapon; }
|
||||
set { _secondaryWeapon = value; }
|
||||
}
|
||||
|
||||
private IWeapon _weapon;
|
||||
public IWeapon Weapon
|
||||
{
|
||||
get { return _weapon; }
|
||||
set { _weapon = value; }
|
||||
}
|
||||
|
||||
public bool IsRemotePlayer { get; set; }
|
||||
public bool Landed { get; set; }
|
||||
public double Fuel { get; private set; }
|
||||
|
||||
#region Input handling
|
||||
|
||||
private bool _isThrusting;
|
||||
public void HandleKeyboard(KeyboardState state)
|
||||
{
|
||||
if (!MainGame.IsKeyboardInUse)
|
||||
{
|
||||
if (state.IsKeyDown(Keys.Right))
|
||||
{
|
||||
if (Landed)
|
||||
{
|
||||
Landed = false;
|
||||
}
|
||||
|
||||
this.MoveRight();
|
||||
}
|
||||
|
||||
if (state.IsKeyDown(Keys.Left))
|
||||
{
|
||||
if (Landed)
|
||||
{
|
||||
Landed = false;
|
||||
}
|
||||
|
||||
this.MoveLeft();
|
||||
}
|
||||
|
||||
if (state.IsKeyDown(Keys.Up))
|
||||
{
|
||||
if (Landed)
|
||||
{
|
||||
Landed = false;
|
||||
}
|
||||
|
||||
_isThrusting = true;
|
||||
|
||||
this.Thrust();
|
||||
}
|
||||
else
|
||||
{
|
||||
_isThrusting = false;
|
||||
}
|
||||
|
||||
if (state.IsKeyDown(Keys.Space))
|
||||
{
|
||||
this.Fire();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.Weapon.Heat > 0)
|
||||
{
|
||||
this.Weapon.Heat -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.IsKeyDown(Keys.LeftControl))
|
||||
{
|
||||
this._secondaryWeapon.Fire(this._secondaryProjectile, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this._secondaryWeapon.Heat > 0)
|
||||
{
|
||||
this._secondaryWeapon.Heat -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.IsKeyDown(Keys.R))
|
||||
{
|
||||
this.Health = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Fire()
|
||||
{
|
||||
this._weapon.Fire(_projectile, this);
|
||||
}
|
||||
|
||||
private void MoveRight()
|
||||
{
|
||||
Angle += 0.1f;
|
||||
}
|
||||
|
||||
private void MoveLeft()
|
||||
{
|
||||
Angle -= 0.1f;
|
||||
}
|
||||
|
||||
private void Thrust()
|
||||
{
|
||||
Fuel -= 1;
|
||||
|
||||
_rotationMatrix = Matrix.CreateRotationZ(Angle);
|
||||
_direction = Vector2.Transform(_up, _rotationMatrix);
|
||||
|
||||
Velocity += _direction / VelocityModifier;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEntity Members
|
||||
|
||||
public override string Owner { get; set; }
|
||||
public override sealed int Health { get; set; }
|
||||
|
||||
public override void ApplyGravity(GameTime time)
|
||||
{
|
||||
if (!Landed)
|
||||
{
|
||||
base.ApplyGravity(time);
|
||||
}
|
||||
}
|
||||
|
||||
public override Vector2 Origin
|
||||
{
|
||||
get { return new Vector2(Texture.Width / 2, Texture.Height / 2); }
|
||||
}
|
||||
|
||||
public override string TextureName
|
||||
{
|
||||
get { return "player"; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private DateTime _lastCollide;
|
||||
public void CollideWith(IEntity entity)
|
||||
{
|
||||
var projectile = entity as IProjectile;
|
||||
if (projectile != null)
|
||||
{
|
||||
this.Health -= projectile.Damage;
|
||||
return;
|
||||
}
|
||||
var player = entity as Player;
|
||||
if (player != null)
|
||||
{
|
||||
if (_lastCollide <= DateTime.Now.AddMilliseconds(-80))
|
||||
{
|
||||
_lastCollide = DateTime.Now;
|
||||
|
||||
if (Math.Abs(this.Velocity.Length()) > Math.Abs(entity.Velocity.Length()))
|
||||
{
|
||||
entity.Velocity += this.Velocity / 2;
|
||||
this.Velocity /= 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Velocity += entity.Velocity / 2;
|
||||
entity.Velocity /= 4;
|
||||
}
|
||||
}
|
||||
|
||||
this.Landed = false;
|
||||
player.Landed = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void CollideWithLevel(Level level)
|
||||
{
|
||||
Velocity -= (Velocity / 300);
|
||||
}
|
||||
|
||||
public static Player CreatePlayer(Vector2 startPosition)
|
||||
{
|
||||
return CreatePlayer(startPosition, string.Empty);
|
||||
}
|
||||
|
||||
public static Player CreatePlayer(Vector2 startPosition, string name)
|
||||
{
|
||||
return EntityFactory.Instance.CreateEntity<Player>(
|
||||
name,
|
||||
startPosition,
|
||||
new Vector2(),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
public void Land()
|
||||
{
|
||||
if (!Landed)
|
||||
{
|
||||
this.Velocity = new Vector2(0, 0);
|
||||
this.Position = new Vector2(this.Position.X, this.Position.Y - 1); // Need to adjust because per pixel collision
|
||||
// sometimes can't catch up with frame rate
|
||||
this.Angle = 0;
|
||||
|
||||
Landed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Kill()
|
||||
{
|
||||
SoundManager.Play("Audio/Waves/explosion", this.Position);
|
||||
this.Position = new Vector2(25, 25);
|
||||
this.Velocity = new Vector2(0, 0);
|
||||
this.Angle = 0;
|
||||
this.Health = 100;
|
||||
this.Landed = false;
|
||||
}
|
||||
|
||||
public override void Update(GameTime time)
|
||||
{
|
||||
if (_isThrusting)
|
||||
{
|
||||
if (_thrustSound.State != SoundState.Playing)
|
||||
{
|
||||
_thrustSound.Play();
|
||||
}
|
||||
|
||||
this.Texture = TextureManager.LoadTexture("player_thrusting");
|
||||
}
|
||||
else
|
||||
{
|
||||
_thrustSound.Pause();
|
||||
this.Texture = TextureManager.LoadTexture("player");
|
||||
}
|
||||
|
||||
base.Update(time);
|
||||
}
|
||||
|
||||
#region IFocusable Members
|
||||
|
||||
Vector2 IFocusable.Position
|
||||
{
|
||||
get { return this.Position; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
18
SpacePew/Models/Projectiles/BouncingBullet.cs
Normal file
18
SpacePew/Models/Projectiles/BouncingBullet.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace SpacePew.Models.Projectiles
|
||||
{
|
||||
public sealed class BouncingBullet : Bullet
|
||||
{
|
||||
public BouncingBullet()
|
||||
{
|
||||
Health = 30;
|
||||
}
|
||||
|
||||
public override CollisionType CollisionType
|
||||
{
|
||||
get
|
||||
{
|
||||
return CollisionType.Bounce;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
70
SpacePew/Models/Projectiles/Bullet.cs
Normal file
70
SpacePew/Models/Projectiles/Bullet.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace SpacePew.Models.Projectiles
|
||||
{
|
||||
public class Bullet : ProjectileBase
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
public Bullet()
|
||||
{
|
||||
this.Color = Color.White;
|
||||
this.Velocity = new Vector2();
|
||||
this.Angle = 0;
|
||||
this.Health = 15;
|
||||
}
|
||||
|
||||
public Bullet(Vector2 position)
|
||||
: this()
|
||||
{
|
||||
this.Position = position;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEntity Members
|
||||
|
||||
public override sealed int Health
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public override string TextureName
|
||||
{
|
||||
get { return "bullet"; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IProjectile Members
|
||||
|
||||
public override string FireSoundAssetName
|
||||
{
|
||||
get { return "Audio/Waves/bullet_sound"; }
|
||||
}
|
||||
|
||||
public override string HitSoundAssetName
|
||||
{
|
||||
get { return "Audio/Waves/bullet_hit"; }
|
||||
}
|
||||
|
||||
public override int Damage
|
||||
{
|
||||
get { return 5; }
|
||||
}
|
||||
|
||||
public override float Speed
|
||||
{
|
||||
get { return 400f; }
|
||||
}
|
||||
|
||||
public override CollisionType CollisionType
|
||||
{
|
||||
get { return CollisionType.Explode; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
93
SpacePew/Models/Projectiles/ClusterBomb.cs
Normal file
93
SpacePew/Models/Projectiles/ClusterBomb.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using SpacePew.Models.Weapons;
|
||||
|
||||
namespace SpacePew.Models.Projectiles
|
||||
{
|
||||
class ClusterBomb : Bullet, IKillable
|
||||
{
|
||||
private const int FuseTime = 7000; //time in milliseconds before it explodes
|
||||
private double _elapsed = 0;
|
||||
readonly DateTime _startTick;
|
||||
|
||||
public override int Damage
|
||||
{
|
||||
get { return 10; }
|
||||
}
|
||||
|
||||
public override float Speed
|
||||
{
|
||||
get { return 100; }
|
||||
}
|
||||
|
||||
public ClusterBomb()
|
||||
{
|
||||
_startTick = DateTime.Now;
|
||||
}
|
||||
|
||||
public override Color Color
|
||||
{
|
||||
get
|
||||
{
|
||||
var r = (byte)((_elapsed / FuseTime) * 255f);
|
||||
var g = (byte)(128 + Math.Sin(_elapsed / 150f) * 127f);
|
||||
var b = (byte)(255 - g);
|
||||
return new Color(r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(Microsoft.Xna.Framework.GameTime time)
|
||||
{
|
||||
_elapsed = (DateTime.Now - _startTick).TotalMilliseconds;
|
||||
|
||||
if (_elapsed >= FuseTime)
|
||||
{
|
||||
this.Health = 0;
|
||||
}
|
||||
|
||||
base.Update(time);
|
||||
}
|
||||
|
||||
private void CreateCluster(float angle, string owner)
|
||||
{
|
||||
var entity = EntityFactory.Instance.CreateEntity<ProjectileBase>(
|
||||
typeof(Bullet),
|
||||
owner,
|
||||
Position,
|
||||
Vector2.Zero,
|
||||
angle);
|
||||
|
||||
Matrix m = Matrix.CreateRotationZ(angle + (float)(WeaponBase.Randomizer.NextDouble() * .2f - .1f));
|
||||
Vector2 velocity = Vector2.Transform(-Vector2.UnitY, m);
|
||||
|
||||
entity.Position += velocity * 10;
|
||||
entity.Velocity = velocity * entity.Speed * (float)(WeaponBase.Randomizer.NextDouble() + 0.5f);
|
||||
|
||||
//TODO.. fix
|
||||
//FiredShots.Enqueue(entity);
|
||||
}
|
||||
|
||||
public override void ApplyGravity(GameTime time)
|
||||
{
|
||||
float timeSeconds = time.ElapsedGameTime.Milliseconds * .001f;
|
||||
Velocity = new Vector2(
|
||||
Velocity.X,
|
||||
Velocity.Y + (timeSeconds * GravityModifier * 0.5f));
|
||||
}
|
||||
|
||||
#region IKillable Members
|
||||
|
||||
public void Kill()
|
||||
{
|
||||
//TODO: make sure the clusters are only created on the client that created the cluster, and then let the server send them to all klients
|
||||
for (float i = 0; i < 45; i += .5f)
|
||||
CreateCluster(i, this.Owner);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
8
SpacePew/Models/Projectiles/CollisionType.cs
Normal file
8
SpacePew/Models/Projectiles/CollisionType.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace SpacePew.Models.Projectiles
|
||||
{
|
||||
public enum CollisionType
|
||||
{
|
||||
Explode,
|
||||
Bounce
|
||||
}
|
||||
}
|
91
SpacePew/Models/Projectiles/HomingBullet.cs
Normal file
91
SpacePew/Models/Projectiles/HomingBullet.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace SpacePew.Models.Projectiles
|
||||
{
|
||||
public class HomingBullet : Bullet
|
||||
{
|
||||
public HomingBullet()
|
||||
{
|
||||
this.Color = Color.PowderBlue;
|
||||
}
|
||||
|
||||
public override float Speed
|
||||
{
|
||||
get
|
||||
{
|
||||
return 200f;
|
||||
}
|
||||
}
|
||||
|
||||
public override int Damage
|
||||
{
|
||||
get
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
private Player _target;
|
||||
|
||||
const float TimeIncrementSpeed = 5;
|
||||
|
||||
public override void Update(GameTime time)
|
||||
{
|
||||
base.Update(time);
|
||||
|
||||
if (_target == null)
|
||||
{
|
||||
GetTarget();
|
||||
}
|
||||
|
||||
if (_target != null)
|
||||
{
|
||||
var delta = new Vector2(_target.Position.X - this.Position.X, _target.Position.Y - this.Position.Y);
|
||||
|
||||
if (delta.Length() > TimeIncrementSpeed)
|
||||
{
|
||||
delta.Normalize();
|
||||
delta.X *= TimeIncrementSpeed;
|
||||
delta.Y *= TimeIncrementSpeed;
|
||||
}
|
||||
|
||||
this.Position = new Vector2(Position.X + (int)delta.X, Position.Y + (int)delta.Y);
|
||||
}
|
||||
}
|
||||
|
||||
private void GetTarget()
|
||||
{
|
||||
var players = EntityFactory.Instance.Entities.OfType<Player>().Where(player => player.Owner != this.Owner).ToList();
|
||||
|
||||
if (players.Count > 0)
|
||||
{
|
||||
_target = FindClosestPlayer(players, this.Position);
|
||||
}
|
||||
}
|
||||
|
||||
private static Player FindClosestPlayer(IEnumerable<Player> list, Vector2 pointToCompare)
|
||||
{
|
||||
Player closestPlayer = list.ToList()[0];
|
||||
|
||||
float distance = float.PositiveInfinity;
|
||||
foreach (var player in list)
|
||||
{
|
||||
float dx = pointToCompare.X - player.Position.X;
|
||||
float dy = pointToCompare.Y - player.Position.Y;
|
||||
|
||||
float d = dx * dx - dy * dy;
|
||||
|
||||
if (d < distance)
|
||||
{
|
||||
distance = d;
|
||||
closestPlayer = player;
|
||||
}
|
||||
}
|
||||
|
||||
return closestPlayer;
|
||||
}
|
||||
}
|
||||
}
|
12
SpacePew/Models/Projectiles/IProjectile.cs
Normal file
12
SpacePew/Models/Projectiles/IProjectile.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace SpacePew.Models.Projectiles
|
||||
{
|
||||
public interface IProjectile : IEntity
|
||||
{
|
||||
string HitSoundAssetName { get; }
|
||||
string FireSoundAssetName { get; }
|
||||
|
||||
int Damage { get; }
|
||||
float Speed { get; }
|
||||
CollisionType CollisionType { get; }
|
||||
}
|
||||
}
|
29
SpacePew/Models/Projectiles/LongShot.cs
Normal file
29
SpacePew/Models/Projectiles/LongShot.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace SpacePew.Models.Projectiles
|
||||
{
|
||||
public class LongShot : Bullet
|
||||
{
|
||||
public override Vector2 Origin
|
||||
{
|
||||
get { return new Vector2(5, 12); }
|
||||
}
|
||||
|
||||
public override string TextureName
|
||||
{
|
||||
get { return "longshot"; }
|
||||
}
|
||||
|
||||
public override void ApplyGravity(GameTime time)
|
||||
{
|
||||
base.ApplyGravity(time);
|
||||
|
||||
//Sätt rotationen till lika som riktningen som pilen åker
|
||||
Vector2 vNormal = Velocity;
|
||||
vNormal.Normalize();
|
||||
Angle = (vNormal.X > 0 ? 1f : -1f) * (float)(Math.Acos(Vector2.Dot(-Vector2.UnitY, vNormal)));
|
||||
}
|
||||
}
|
||||
}
|
75
SpacePew/Models/Projectiles/Missile.cs
Normal file
75
SpacePew/Models/Projectiles/Missile.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace SpacePew.Models.Projectiles
|
||||
{
|
||||
public sealed class Missile : ProjectileBase
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
public Missile()
|
||||
{
|
||||
this.Color = Color.Red;
|
||||
this.Velocity = new Vector2();
|
||||
this.Angle = 0;
|
||||
this.Health = 10;
|
||||
}
|
||||
|
||||
public Missile(Vector2 position)
|
||||
: this()
|
||||
{
|
||||
this.Position = position;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEntity Members
|
||||
|
||||
public override void CollideWithLevel(Level level)
|
||||
{
|
||||
// blow stuff
|
||||
}
|
||||
|
||||
public override int Health
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public override string TextureName
|
||||
{
|
||||
get { return "bullet"; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IProjectile Members
|
||||
|
||||
public override string FireSoundAssetName
|
||||
{
|
||||
get { return "Audio/Waves/bullet_sound"; }
|
||||
}
|
||||
|
||||
public override string HitSoundAssetName
|
||||
{
|
||||
get { return "Audio/Waves/bullet_hit"; }
|
||||
}
|
||||
|
||||
public override int Damage
|
||||
{
|
||||
get { return 50; }
|
||||
}
|
||||
|
||||
public override float Speed
|
||||
{
|
||||
get { return 600f; }
|
||||
}
|
||||
|
||||
public override CollisionType CollisionType
|
||||
{
|
||||
get { return CollisionType.Explode; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
48
SpacePew/Models/Projectiles/ProjectileBase.cs
Normal file
48
SpacePew/Models/Projectiles/ProjectileBase.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
namespace SpacePew.Models.Projectiles
|
||||
{
|
||||
public abstract class ProjectileBase : EntityBase, IProjectile
|
||||
{
|
||||
#region IEntity Members
|
||||
|
||||
public override bool Collide(IEntity entity)
|
||||
{
|
||||
bool collided = base.Collide(entity);
|
||||
|
||||
if (collided)
|
||||
{
|
||||
SoundManager.Play(HitSoundAssetName, this.Position);
|
||||
}
|
||||
|
||||
return collided;
|
||||
}
|
||||
|
||||
public override void CollideWithLevel(Level level)
|
||||
{
|
||||
if (this.CollisionType == CollisionType.Explode)
|
||||
{
|
||||
// blow up somehow (particle system is there, but needs to trash the level a little more too)
|
||||
}
|
||||
else if (this.CollisionType == CollisionType.Bounce)
|
||||
{
|
||||
this.Velocity -= (this.Velocity * 1.9f);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Created()
|
||||
{
|
||||
SoundManager.Play(FireSoundAssetName, this.Position);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IProjectile Members
|
||||
|
||||
public abstract string HitSoundAssetName { get; }
|
||||
public abstract string FireSoundAssetName { get; }
|
||||
public abstract int Damage { get; }
|
||||
public abstract float Speed { get; }
|
||||
public abstract CollisionType CollisionType { get; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
20
SpacePew/Models/Weapons/Cannon.cs
Normal file
20
SpacePew/Models/Weapons/Cannon.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace SpacePew.Models.Weapons
|
||||
{
|
||||
public class Cannon : WeaponBase
|
||||
{
|
||||
public override float HeatGeneration
|
||||
{
|
||||
get { return 0.4f; }
|
||||
}
|
||||
|
||||
public override int Delay
|
||||
{
|
||||
get { return 35; }
|
||||
}
|
||||
|
||||
public override int Spread
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
}
|
||||
}
|
20
SpacePew/Models/Weapons/ClusterLauncher.cs
Normal file
20
SpacePew/Models/Weapons/ClusterLauncher.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace SpacePew.Models.Weapons
|
||||
{
|
||||
public class ClusterLauncher : WeaponBase
|
||||
{
|
||||
public override float HeatGeneration
|
||||
{
|
||||
get { return 100f; }
|
||||
}
|
||||
|
||||
public override int Delay
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public override int Spread
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
}
|
||||
}
|
13
SpacePew/Models/Weapons/IWeapon.cs
Normal file
13
SpacePew/Models/Weapons/IWeapon.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using SpacePew.Models.Projectiles;
|
||||
|
||||
namespace SpacePew.Models.Weapons
|
||||
{
|
||||
public interface IWeapon
|
||||
{
|
||||
int Delay { get; }
|
||||
int Spread { get; }
|
||||
float Heat { get; set; }
|
||||
float HeatGeneration { get; }
|
||||
void Fire(IProjectile projectile, Player player);
|
||||
}
|
||||
}
|
20
SpacePew/Models/Weapons/Launcher.cs
Normal file
20
SpacePew/Models/Weapons/Launcher.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace SpacePew.Models.Weapons
|
||||
{
|
||||
public class Launcher : WeaponBase
|
||||
{
|
||||
public override float HeatGeneration
|
||||
{
|
||||
get { return 100f; }
|
||||
}
|
||||
|
||||
public override int Delay
|
||||
{
|
||||
get { return 200; }
|
||||
}
|
||||
|
||||
public override int Spread
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
}
|
||||
}
|
24
SpacePew/Models/Weapons/SecondaryCannon.cs
Normal file
24
SpacePew/Models/Weapons/SecondaryCannon.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace SpacePew.Models.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// A slower version of the cannon, should be used for powerful projectiles like the homing bullets to even things out
|
||||
/// </summary>
|
||||
public class SecondaryCannon : Cannon
|
||||
{
|
||||
public override float HeatGeneration
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1f;
|
||||
}
|
||||
}
|
||||
|
||||
public override int Delay
|
||||
{
|
||||
get
|
||||
{
|
||||
return 80;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
64
SpacePew/Models/Weapons/TriCannon.cs
Normal file
64
SpacePew/Models/Weapons/TriCannon.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using SpacePew.Models.Projectiles;
|
||||
|
||||
namespace SpacePew.Models.Weapons
|
||||
{
|
||||
public class TriCannon : WeaponBase
|
||||
{
|
||||
public override float HeatGeneration
|
||||
{
|
||||
get { return 0.8f; }
|
||||
}
|
||||
|
||||
public override int Delay
|
||||
{
|
||||
get { return 40; }
|
||||
}
|
||||
|
||||
public override int Spread
|
||||
{
|
||||
get { return 3; }
|
||||
}
|
||||
|
||||
public override void Fire(IProjectile projectile, Player player)
|
||||
{
|
||||
if (Heat + this.HeatGeneration > MaxHeat)
|
||||
return;
|
||||
|
||||
Heat += 1f * this.HeatGeneration;
|
||||
|
||||
if (_lastShot <= DateTime.Now.AddMilliseconds(-this.Delay - (Heat >= HeatStartAffectAt ? Heat : 0)))
|
||||
{
|
||||
float angle = player.Angle + (float)(Randomizer.Next(-Spread * 100, Spread * 100) / 9000.0);
|
||||
|
||||
InternalFire(projectile, player, angle - 0.15f);
|
||||
InternalFire(projectile, player, angle);
|
||||
InternalFire(projectile, player, angle + 0.15f);
|
||||
|
||||
_lastShot = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
private static void InternalFire(IProjectile projectile, IEntity player, float angle)
|
||||
{
|
||||
var entity = EntityFactory.Instance.CreateEntity<ProjectileBase>(
|
||||
projectile.GetType(),
|
||||
player.Owner,
|
||||
player.Position,
|
||||
new Vector2(),
|
||||
angle);
|
||||
|
||||
Matrix m = Matrix.CreateRotationZ(angle);
|
||||
Vector2 velocity = Vector2.Transform(-Vector2.UnitY, m);
|
||||
|
||||
entity.Position += velocity * (player.Origin.Y + entity.Origin.Y);
|
||||
entity.Velocity = velocity * projectile.Speed;
|
||||
|
||||
entity.Velocity = player.Velocity + entity.Velocity;
|
||||
|
||||
FiredShots.Enqueue(entity);
|
||||
}
|
||||
}
|
||||
}
|
82
SpacePew/Models/Weapons/WeaponBase.cs
Normal file
82
SpacePew/Models/Weapons/WeaponBase.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using SpacePew.Models.Projectiles;
|
||||
|
||||
namespace SpacePew.Models.Weapons
|
||||
{
|
||||
public abstract class WeaponBase : IWeapon
|
||||
{
|
||||
protected const int MaxHeat = 100;
|
||||
protected const int HeatStartAffectAt = MaxHeat / 2;
|
||||
|
||||
static WeaponBase()
|
||||
{
|
||||
FiredShots = new Queue<IProjectile>();
|
||||
}
|
||||
|
||||
public static Queue<IProjectile> FiredShots;
|
||||
|
||||
protected DateTime _lastShot;
|
||||
|
||||
private static Random _randomizer;
|
||||
|
||||
public static Random Randomizer
|
||||
{
|
||||
get { return _randomizer ?? (_randomizer = new Random()); }
|
||||
}
|
||||
|
||||
private float _heat;
|
||||
|
||||
protected WeaponBase()
|
||||
{
|
||||
_lastShot = DateTime.Now;
|
||||
}
|
||||
|
||||
public virtual float Heat
|
||||
{
|
||||
get { return _heat; }
|
||||
set { _heat = value; }
|
||||
}
|
||||
|
||||
#region IWeapon Members
|
||||
|
||||
public abstract float HeatGeneration { get; }
|
||||
public abstract int Delay { get; }
|
||||
public abstract int Spread { get; }
|
||||
|
||||
public virtual void Fire(IProjectile projectile, Player player)
|
||||
{
|
||||
if (_heat + this.HeatGeneration > MaxHeat)
|
||||
return;
|
||||
|
||||
_heat += 1f * this.HeatGeneration;
|
||||
|
||||
if (_lastShot <= DateTime.Now.AddMilliseconds(-this.Delay - (_heat >= HeatStartAffectAt ? _heat : 0)))
|
||||
{
|
||||
float angle = player.Angle + (float)(Randomizer.Next(-Spread * 100, Spread * 100) / 9000.0);
|
||||
|
||||
var entity = EntityFactory.Instance.CreateEntity<ProjectileBase>(
|
||||
projectile.GetType(),
|
||||
player.Owner,
|
||||
player.Position,
|
||||
new Vector2(),
|
||||
angle);
|
||||
|
||||
Matrix m = Matrix.CreateRotationZ(angle);
|
||||
Vector2 velocity = Vector2.Transform(-Vector2.UnitY, m);
|
||||
|
||||
entity.Position += velocity * (player.Origin.Y + entity.Origin.Y);
|
||||
entity.Velocity = velocity * projectile.Speed;
|
||||
|
||||
entity.Velocity = player.Velocity + entity.Velocity;
|
||||
FiredShots.Enqueue(entity);
|
||||
|
||||
_lastShot = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
19
SpacePew/Networking/NetworkMessage.cs
Normal file
19
SpacePew/Networking/NetworkMessage.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using SpacePew.Models;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace SpacePew.Networking
|
||||
{
|
||||
public class NetworkMessage
|
||||
{
|
||||
// TODO: Add SoundAssetName to make NetworkMessenger play custom sounds upon different messages
|
||||
public string Message { get; set; }
|
||||
public DateTime Sent { get; set; }
|
||||
public Color Color { get; set; }
|
||||
public bool IsChatMessage { get; set; }
|
||||
}
|
||||
}
|
374
SpacePew/Networking/NetworkMessenger.cs
Normal file
374
SpacePew/Networking/NetworkMessenger.cs
Normal file
@@ -0,0 +1,374 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Audio;
|
||||
using Microsoft.Xna.Framework.Content;
|
||||
using Microsoft.Xna.Framework.GamerServices;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Microsoft.Xna.Framework.Media;
|
||||
using Microsoft.Xna.Framework.Net;
|
||||
using Microsoft.Xna.Framework.Storage;
|
||||
using SpacePew.Models;
|
||||
|
||||
|
||||
namespace SpacePew.Networking
|
||||
{
|
||||
/// <summary>
|
||||
/// TODO: This is not really a network messenger yet, so we need to change the functionality a little (send all message over network in the loop down there)
|
||||
/// </summary>
|
||||
public class NetworkMessenger : Microsoft.Xna.Framework.GameComponent, IDrawable
|
||||
{
|
||||
private readonly MainGame _game;
|
||||
private SpriteBatch _spriteBatch;
|
||||
private SpriteFont _netFont;
|
||||
private Rectangle _chatRectangle;
|
||||
private Rectangle _textBounds;
|
||||
private static UdpClient _client;
|
||||
|
||||
private static List<NetworkMessage> _messages;
|
||||
|
||||
private const int DefaultMessageLifeTime = 10;
|
||||
|
||||
public NetworkMessenger(MainGame game, UdpClient client)
|
||||
: base(game)
|
||||
{
|
||||
_game = game;
|
||||
_client = client;
|
||||
_messages = new List<NetworkMessage>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows the game component to perform any initialization it needs to before starting
|
||||
/// to run. This is where it can query for any required services and load content.
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
_spriteBatch = new SpriteBatch(_game.GraphicsDevice);
|
||||
_netFont = _game.Content.Load<SpriteFont>("Fonts\\Default");
|
||||
_chatRectangle = new Rectangle(300, 20, 300, _game.GraphicsDevice.Viewport.Height - 40);
|
||||
_textBounds = new Rectangle();
|
||||
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
public static void AddDeath(IEntity entity)
|
||||
{
|
||||
_client.SendDeath(entity);
|
||||
}
|
||||
|
||||
public static void DisplayMessage(NetworkMessage message)
|
||||
{
|
||||
_messages.Add(message);
|
||||
}
|
||||
|
||||
public static void SendMessage(NetworkMessage message)
|
||||
{
|
||||
_messages.Add(message);
|
||||
_client.SendMessage(message);
|
||||
|
||||
if (message.IsChatMessage)
|
||||
{
|
||||
SoundManager.Play("Audio/Waves/message", _client.LocalPlayer.Position);
|
||||
}
|
||||
}
|
||||
|
||||
private string _chatMessage = string.Empty;
|
||||
|
||||
private KeyboardState _oldKeyboardState;
|
||||
private KeyboardState _currentKeyboardState;
|
||||
|
||||
bool chatting = false;
|
||||
|
||||
/// <summary>
|
||||
/// Allows the game component to update itself.
|
||||
/// </summary>
|
||||
/// <param name="gameTime">Provides a snapshot of timing values.</param>
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
MainGame.IsKeyboardInUse = chatting;
|
||||
|
||||
_oldKeyboardState = _currentKeyboardState;
|
||||
|
||||
_currentKeyboardState = Keyboard.GetState();
|
||||
Keys[] pressedKeys = _currentKeyboardState.GetPressedKeys();
|
||||
|
||||
bool isShiftDown = pressedKeys.Any(KeyboardHelper.IsShift);
|
||||
|
||||
foreach (Keys key in pressedKeys)
|
||||
{
|
||||
if (!_oldKeyboardState.IsKeyUp(key)) continue;
|
||||
|
||||
if (key == Keys.T && !chatting)
|
||||
{
|
||||
chatting = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!chatting) continue;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case Keys.Back:
|
||||
_chatMessage = _chatMessage.Remove(_chatMessage.Length - 1, 1);
|
||||
break;
|
||||
case Keys.Space:
|
||||
_chatMessage = _chatMessage + " ";
|
||||
break;
|
||||
default:
|
||||
if (key == Keys.Enter && chatting)
|
||||
{
|
||||
chatting = false;
|
||||
SendMessage(new NetworkMessage()
|
||||
{
|
||||
Color = _client.LocalPlayer.Color,
|
||||
Message =
|
||||
string.Format("{0}: {1}", _client.LocalPlayer.Owner,
|
||||
_chatMessage),
|
||||
Sent = DateTime.Now,
|
||||
IsChatMessage = true
|
||||
});
|
||||
|
||||
_chatMessage = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isShiftDown)
|
||||
{
|
||||
_chatMessage += KeyboardHelper.ToPrintableString(key, KbModifiers.None).ToUpper();
|
||||
}
|
||||
else
|
||||
{
|
||||
_chatMessage += KeyboardHelper.ToPrintableString(key, KbModifiers.None);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = _messages.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_messages[i].Sent.AddSeconds(DefaultMessageLifeTime) <= DateTime.Now)
|
||||
{
|
||||
_messages.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
base.Update(gameTime);
|
||||
}
|
||||
|
||||
public void Draw(GameTime gameTime)
|
||||
{
|
||||
_spriteBatch.Begin();
|
||||
|
||||
int yOffset = 0;
|
||||
|
||||
if (chatting)
|
||||
{
|
||||
_spriteBatch.DrawString(_netFont, "Chat: " + _chatMessage,
|
||||
new Vector2(300, _game.GraphicsDevice.Viewport.Height - 140), Color.White);
|
||||
}
|
||||
|
||||
foreach (var message in _messages)
|
||||
{
|
||||
DrawString(_spriteBatch,
|
||||
_netFont,
|
||||
message.Message,
|
||||
_chatRectangle,
|
||||
message.Color,
|
||||
TextAlignment.TopLeft,
|
||||
true,
|
||||
new Vector2(0, (_messages.IndexOf(message) * 15) + yOffset),
|
||||
out _textBounds);
|
||||
|
||||
yOffset += _textBounds.Height;
|
||||
}
|
||||
|
||||
_spriteBatch.End();
|
||||
}
|
||||
|
||||
public int DrawOrder
|
||||
{
|
||||
get { return 5; }
|
||||
}
|
||||
|
||||
public bool Visible
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
private enum TextAlignment
|
||||
{
|
||||
Top,
|
||||
Left,
|
||||
Middle,
|
||||
Right,
|
||||
Bottom,
|
||||
TopLeft,
|
||||
TopRight,
|
||||
BottomLeft,
|
||||
BottomRight
|
||||
}
|
||||
|
||||
private static void DrawString(SpriteBatch sb, SpriteFont fnt, string text, Rectangle r,
|
||||
Color col, TextAlignment align, bool performWordWrap, Vector2 offsett, out Rectangle textBounds)
|
||||
{
|
||||
textBounds = r;
|
||||
if (text == null) return;
|
||||
if (text == string.Empty) return;
|
||||
|
||||
var lines = new StringCollection();
|
||||
lines.AddRange(text.Split(new string[] { "\\n" }, StringSplitOptions.RemoveEmptyEntries));
|
||||
|
||||
Rectangle tmprect = ProcessLines(fnt, r, performWordWrap, lines);
|
||||
|
||||
var pos = new Vector2(r.X, r.Y);
|
||||
int aStyle = 0;
|
||||
|
||||
switch (align)
|
||||
{
|
||||
case TextAlignment.Bottom:
|
||||
pos.Y = r.Bottom - tmprect.Height;
|
||||
aStyle = 1;
|
||||
break;
|
||||
case TextAlignment.BottomLeft:
|
||||
pos.Y = r.Bottom - tmprect.Height;
|
||||
aStyle = 0;
|
||||
break;
|
||||
case TextAlignment.BottomRight:
|
||||
pos.Y = r.Bottom - tmprect.Height;
|
||||
aStyle = 2;
|
||||
break;
|
||||
case TextAlignment.Left:
|
||||
pos.Y = r.Y + ((r.Height / 2) - (tmprect.Height / 2));
|
||||
aStyle = 0;
|
||||
break;
|
||||
case TextAlignment.Middle:
|
||||
pos.Y = r.Y + ((r.Height / 2) - (tmprect.Height / 2));
|
||||
aStyle = 1;
|
||||
break;
|
||||
case TextAlignment.Right:
|
||||
pos.Y = r.Y + ((r.Height / 2) - (tmprect.Height / 2));
|
||||
aStyle = 2;
|
||||
break;
|
||||
case TextAlignment.Top:
|
||||
aStyle = 1;
|
||||
break;
|
||||
case TextAlignment.TopLeft:
|
||||
aStyle = 0;
|
||||
break;
|
||||
case TextAlignment.TopRight:
|
||||
aStyle = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (string txt in lines)
|
||||
{
|
||||
Vector2 size = fnt.MeasureString(txt);
|
||||
switch (aStyle)
|
||||
{
|
||||
case 0:
|
||||
pos.X = r.X;
|
||||
break;
|
||||
case 1:
|
||||
pos.X = r.X + ((r.Width / 2) - (size.X / 2));
|
||||
break;
|
||||
case 2:
|
||||
pos.X = r.Right - size.X;
|
||||
break;
|
||||
}
|
||||
|
||||
sb.DrawString(fnt, txt, pos + offsett, col);
|
||||
pos.Y += fnt.LineSpacing;
|
||||
}
|
||||
|
||||
textBounds = tmprect;
|
||||
}
|
||||
|
||||
private static Rectangle ProcessLines(SpriteFont fnt, Rectangle r, bool performWordWrap, StringCollection lines)
|
||||
{
|
||||
Rectangle bounds = r;
|
||||
bounds.Width = 0;
|
||||
bounds.Height = 0;
|
||||
int index = 0;
|
||||
float Width;
|
||||
bool lineInserted = false;
|
||||
while (index < lines.Count)
|
||||
{
|
||||
string linetext = lines[index];
|
||||
|
||||
Vector2 size = fnt.MeasureString(linetext);
|
||||
|
||||
if (performWordWrap && size.X > r.Width)
|
||||
{
|
||||
string endspace = string.Empty;
|
||||
if (linetext.EndsWith(" "))
|
||||
{
|
||||
endspace = " ";
|
||||
linetext = linetext.TrimEnd();
|
||||
}
|
||||
|
||||
int i = linetext.LastIndexOf(" ", StringComparison.InvariantCulture);
|
||||
if (i != -1)
|
||||
{
|
||||
string lastword = linetext.Substring(i + 1);
|
||||
|
||||
if (index == lines.Count - 1)
|
||||
{
|
||||
lines.Add(lastword);
|
||||
lineInserted = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lineInserted)
|
||||
{
|
||||
lines[index + 1] = lastword + endspace + lines[index + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
lines.Insert(index + 1, lastword);
|
||||
lineInserted = true;
|
||||
}
|
||||
}
|
||||
|
||||
lines[index] = linetext.Substring(0, i + 1);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
lineInserted = false;
|
||||
size = fnt.MeasureString(lines[index]);
|
||||
if (size.X > bounds.Width) Width = size.X;
|
||||
bounds.Height += fnt.LineSpacing;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lineInserted = false;
|
||||
size = fnt.MeasureString(lines[index]);
|
||||
if (size.X > bounds.Width) bounds.Width = (int)size.X;
|
||||
bounds.Height += fnt.LineSpacing;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
|
||||
event EventHandler<EventArgs> IDrawable.DrawOrderChanged
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
event EventHandler<EventArgs> IDrawable.VisibleChanged
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
}
|
||||
}
|
135
SpacePew/Networking/ScoreBoard.cs
Normal file
135
SpacePew/Networking/ScoreBoard.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Audio;
|
||||
using Microsoft.Xna.Framework.Content;
|
||||
using Microsoft.Xna.Framework.GamerServices;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Microsoft.Xna.Framework.Media;
|
||||
using Microsoft.Xna.Framework.Net;
|
||||
using Microsoft.Xna.Framework.Storage;
|
||||
using SpacePew.Models;
|
||||
|
||||
|
||||
namespace SpacePew.Networking
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a game component that implements IUpdateable.
|
||||
/// </summary>
|
||||
public class ScoreBoard : Microsoft.Xna.Framework.GameComponent, IDrawable
|
||||
{
|
||||
public static List<ScoreBoardItem> CurrentScoreBoard { get; set; }
|
||||
|
||||
private readonly MainGame _game;
|
||||
private readonly UdpClient _client;
|
||||
private Texture2D _scoreBoardTexture;
|
||||
|
||||
private SpriteBatch _spriteBatch;
|
||||
|
||||
private SpriteFont _scoreFont;
|
||||
|
||||
public ScoreBoard(MainGame game, UdpClient client)
|
||||
: base(game)
|
||||
{
|
||||
_game = game;
|
||||
_client = client;
|
||||
|
||||
CurrentScoreBoard = new List<ScoreBoardItem>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows the game component to perform any initialization it needs to before starting
|
||||
/// to run. This is where it can query for any required services and load content.
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
_spriteBatch = new SpriteBatch(_game.GraphicsDevice);
|
||||
|
||||
_scoreFont = _game.Content.Load<SpriteFont>("Fonts\\Default");
|
||||
_scoreBoardTexture = _game.Content.Load<Texture2D>("scoreboard");
|
||||
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
bool requestedScoreBoard;
|
||||
|
||||
/// <summary>
|
||||
/// Allows the game component to update itself.
|
||||
/// </summary>
|
||||
/// <param name="gameTime">Provides a snapshot of timing values.</param>
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
if (Keyboard.GetState().IsKeyDown(Keys.Tab))
|
||||
{
|
||||
if (!requestedScoreBoard)
|
||||
{
|
||||
_client.RequestScoreBoard();
|
||||
}
|
||||
|
||||
requestedScoreBoard = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
requestedScoreBoard = false;
|
||||
}
|
||||
|
||||
base.Update(gameTime);
|
||||
}
|
||||
|
||||
public void Draw(GameTime gameTime)
|
||||
{
|
||||
if (Keyboard.GetState().IsKeyDown(Keys.Tab))
|
||||
{
|
||||
_spriteBatch.Begin();
|
||||
|
||||
_spriteBatch.Draw(_scoreBoardTexture, new Vector2(20, 20), Color.White);
|
||||
|
||||
foreach (ScoreBoardItem item in CurrentScoreBoard)
|
||||
{
|
||||
Player player = ((List<Player>)_client.Players).Find(p => p.Owner == item.Name);
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
Color color = player.Color;
|
||||
|
||||
int yOffset = (CurrentScoreBoard.IndexOf(item) * 15) + 70;
|
||||
|
||||
_spriteBatch.DrawString(_scoreFont, item.Name, new Vector2(35, yOffset), color);
|
||||
_spriteBatch.DrawString(_scoreFont, item.Kills.ToString(CultureInfo.InvariantCulture), new Vector2(185, yOffset), color);
|
||||
_spriteBatch.DrawString(_scoreFont, item.Deaths.ToString(CultureInfo.InvariantCulture), new Vector2(243, yOffset), color);
|
||||
_spriteBatch.DrawString(_scoreFont, (DateTime.Now - item.Joined).Minutes.ToString(CultureInfo.InvariantCulture),
|
||||
new Vector2(314, yOffset), color);
|
||||
_spriteBatch.DrawString(_scoreFont, item.Ping.ToString(CultureInfo.InvariantCulture), new Vector2(373, yOffset), color);
|
||||
}
|
||||
}
|
||||
|
||||
_spriteBatch.End();
|
||||
}
|
||||
}
|
||||
|
||||
public int DrawOrder
|
||||
{
|
||||
get { return 5; }
|
||||
}
|
||||
|
||||
public bool Visible
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
event EventHandler<EventArgs> IDrawable.DrawOrderChanged
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
event EventHandler<EventArgs> IDrawable.VisibleChanged
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
}
|
||||
}
|
16
SpacePew/Networking/ScoreBoardItem.cs
Normal file
16
SpacePew/Networking/ScoreBoardItem.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace SpacePew.Networking
|
||||
{
|
||||
public class ScoreBoardItem
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int Kills { get; set; }
|
||||
public int Deaths { get; set; }
|
||||
public DateTime Joined { get; set; }
|
||||
public long Ping { get; set; }
|
||||
}
|
||||
}
|
64
SpacePew/Networking/StreamingClient.cs
Normal file
64
SpacePew/Networking/StreamingClient.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System.Diagnostics;
|
||||
using Lidgren.Network;
|
||||
using System.IO;
|
||||
using SpacePew.Common;
|
||||
|
||||
namespace SpacePew.Networking
|
||||
{
|
||||
public class StreamingClient
|
||||
{
|
||||
private FileStream _inputStream;
|
||||
private int _sentOffset;
|
||||
private int _chunkLen;
|
||||
private byte[] _tmpBuffer;
|
||||
private NetConnection _connection;
|
||||
|
||||
public StreamingClient(NetConnection conn, string fileName)
|
||||
{
|
||||
_connection = conn;
|
||||
_inputStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
_chunkLen = _connection.Peer.Configuration.MaximumTransmissionUnit - 20;
|
||||
_tmpBuffer = new byte[_chunkLen];
|
||||
_sentOffset = 0;
|
||||
}
|
||||
|
||||
public void Heartbeat()
|
||||
{
|
||||
if (_inputStream == null)
|
||||
return;
|
||||
|
||||
if (_connection.CanSendImmediately(NetDeliveryMethod.ReliableOrdered, 1))
|
||||
{
|
||||
long remaining = _inputStream.Length - _sentOffset;
|
||||
int sendBytes = (remaining > _chunkLen ? _chunkLen : (int)remaining);
|
||||
|
||||
_inputStream.Read(_tmpBuffer, 0, sendBytes);
|
||||
|
||||
NetOutgoingMessage message;
|
||||
if (_sentOffset == 0)
|
||||
{
|
||||
message = _connection.Peer.CreateMessage(sendBytes + 8);
|
||||
message.Write((int)UdpNetworkPacketType.LevelResponse);
|
||||
message.Write((ulong)_inputStream.Length);
|
||||
message.Write(Path.GetFileName(_inputStream.Name));
|
||||
|
||||
_connection.SendMessage(message, NetDeliveryMethod.ReliableOrdered, 1);
|
||||
}
|
||||
|
||||
message = _connection.Peer.CreateMessage(sendBytes + 8);
|
||||
message.Write((int)UdpNetworkPacketType.LevelResponse);
|
||||
message.Write(_tmpBuffer, 0, sendBytes);
|
||||
|
||||
_connection.SendMessage(message, NetDeliveryMethod.ReliableOrdered, 1);
|
||||
_sentOffset += sendBytes;
|
||||
|
||||
if (remaining - sendBytes <= 0)
|
||||
{
|
||||
_inputStream.Close();
|
||||
_inputStream.Dispose();
|
||||
_inputStream = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
62
SpacePew/Networking/UdpBase.cs
Normal file
62
SpacePew/Networking/UdpBase.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Lidgren.Network;
|
||||
using SpacePew.Common;
|
||||
using System.Net;
|
||||
|
||||
namespace SpacePew.Networking
|
||||
{
|
||||
public abstract class UdpBase
|
||||
{
|
||||
protected Color[] _playerColors =
|
||||
{
|
||||
Color.Red,
|
||||
Color.Blue,
|
||||
Color.Green,
|
||||
Color.Yellow,
|
||||
Color.Purple,
|
||||
Color.White,
|
||||
Color.Red,
|
||||
Color.LightBlue,
|
||||
Color.Orange,
|
||||
Color.Gray
|
||||
};
|
||||
|
||||
protected byte[] ObjectToByteArray(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return null;
|
||||
|
||||
var memoryStream = new MemoryStream();
|
||||
var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress);
|
||||
var formatter = new BinaryFormatter();
|
||||
formatter.Serialize(gZipStream, obj);
|
||||
|
||||
gZipStream.Close();
|
||||
memoryStream.Close();
|
||||
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
|
||||
protected object ByteArrayToObject(byte[] bytes)
|
||||
{
|
||||
var memoryStream = new MemoryStream();
|
||||
var gzipStream = new GZipStream(memoryStream, CompressionMode.Decompress);
|
||||
|
||||
var formatter = new BinaryFormatter();
|
||||
|
||||
memoryStream.Write(bytes, 0, bytes.Length);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
var obj = formatter.Deserialize(gzipStream);
|
||||
|
||||
gzipStream.Close();
|
||||
memoryStream.Close();
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
586
SpacePew/Networking/UdpClient.cs
Normal file
586
SpacePew/Networking/UdpClient.cs
Normal file
@@ -0,0 +1,586 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
using SpacePew.Extensions;
|
||||
using Lidgren.Network;
|
||||
using System.Collections;
|
||||
using SpacePew.Models;
|
||||
using SpacePew.Common;
|
||||
using System.Net;
|
||||
|
||||
namespace SpacePew.Networking
|
||||
{
|
||||
public class UdpClient : UdpBase
|
||||
{
|
||||
private string _playerName;
|
||||
private readonly NetClient _client;
|
||||
private Level _level;
|
||||
|
||||
private readonly MainGame _game;
|
||||
|
||||
private int _colorIndex;
|
||||
|
||||
private IPEndPoint _masterServerEndpoint;
|
||||
|
||||
private UdpClient()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public UdpClient(MainGame game)
|
||||
{
|
||||
_game = game;
|
||||
_masterServerEndpoint = NetUtility.Resolve("spacepew.wodanaz.se", Constants.MasterServerPort); // TODO: Fixa upp masterserver någonstans
|
||||
EntitiesToSend = new List<IEntity>();
|
||||
|
||||
var configuration = new NetPeerConfiguration("SpacePew");
|
||||
|
||||
configuration.EnableMessageType(NetIncomingMessageType.DiscoveryRequest);
|
||||
configuration.EnableMessageType(NetIncomingMessageType.DiscoveryResponse);
|
||||
configuration.EnableMessageType(NetIncomingMessageType.UnconnectedData);
|
||||
configuration.EnableMessageType(NetIncomingMessageType.NatIntroductionSuccess);
|
||||
|
||||
_client = new NetClient(configuration);
|
||||
_client.Start();
|
||||
}
|
||||
|
||||
public List<IEntity> EntitiesToSend { get; set; }
|
||||
|
||||
public NetClient CurrentClient
|
||||
{
|
||||
get { return _client; }
|
||||
}
|
||||
|
||||
private Player _localPlayer;
|
||||
public Player LocalPlayer
|
||||
{
|
||||
get
|
||||
{
|
||||
return _localPlayer;
|
||||
}
|
||||
set
|
||||
{
|
||||
_localPlayer = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<Player> _players = new List<Player>();
|
||||
public IList<Player> Players
|
||||
{
|
||||
get
|
||||
{
|
||||
return _players;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSessionAlive
|
||||
{
|
||||
get
|
||||
{
|
||||
return _client != null && _client.ConnectionStatus == NetConnectionStatus.Connected;
|
||||
}
|
||||
}
|
||||
|
||||
public void JoinSession(string host, string playerName)
|
||||
{
|
||||
JoinSession(new IPEndPoint(System.Net.Dns.GetHostAddresses(host)[0], SpacePew.Common.Constants.GameServerPort), playerName);
|
||||
}
|
||||
|
||||
public void JoinSession(IPEndPoint endpoint, string playerName)
|
||||
{
|
||||
_playerName = playerName;
|
||||
|
||||
var hailMessage = _client.CreateMessage(playerName);
|
||||
_client.Connect(endpoint, hailMessage);
|
||||
|
||||
while (_client.ServerConnection == null)
|
||||
{
|
||||
// wait
|
||||
}
|
||||
|
||||
System.Threading.Thread.Sleep(500); // TODO: nya lidgren är dumt i huvet, grotta i vad skiten sätter i någon tråd se'n
|
||||
|
||||
System.Diagnostics.Trace.WriteLine("Got server connection...");
|
||||
WriteLevelRequest();
|
||||
WaitForLevel();
|
||||
WriteJoiningMessage();
|
||||
WaitForJoin();
|
||||
|
||||
if (_level.OggVorbisSong != null)
|
||||
{
|
||||
new Thread(_level.PlayLevelSong).Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void ExitSession(string reason)
|
||||
{
|
||||
var message = _client.CreateMessage();
|
||||
|
||||
message.Write((int)UdpNetworkPacketType.PlayerDisconnecting);
|
||||
message.Write(_playerName);
|
||||
|
||||
_client.SendMessage(message, NetDeliveryMethod.ReliableOrdered);
|
||||
|
||||
_client.Disconnect(reason);
|
||||
}
|
||||
|
||||
public void SendMessage(NetworkMessage message)
|
||||
{
|
||||
var netMessage = _client.CreateMessage();
|
||||
|
||||
netMessage.Write((int)UdpNetworkPacketType.MessageSent);
|
||||
netMessage.Write(message.Color.ToVector3());
|
||||
netMessage.Write(message.Sent.ToString(CultureInfo.InvariantCulture));
|
||||
netMessage.Write(message.Message);
|
||||
netMessage.Write(message.IsChatMessage);
|
||||
|
||||
_client.SendMessage(netMessage, NetDeliveryMethod.UnreliableSequenced);
|
||||
}
|
||||
|
||||
public void RequestScoreBoard()
|
||||
{
|
||||
var message = _client.CreateMessage();
|
||||
|
||||
message.Write((int)UdpNetworkPacketType.RequestingScoreboard);
|
||||
|
||||
_client.SendMessage(message, NetDeliveryMethod.UnreliableSequenced);
|
||||
}
|
||||
|
||||
private void ReadMessage(NetIncomingMessage netMessage)
|
||||
{
|
||||
var color = new Color(netMessage.ReadVector3());
|
||||
var sent = DateTime.Parse(netMessage.ReadString(), CultureInfo.InvariantCulture);
|
||||
string message = netMessage.ReadString();
|
||||
bool isChatMessage = netMessage.ReadBoolean();
|
||||
|
||||
NetworkMessenger.DisplayMessage(new NetworkMessage() { Color = color, Sent = sent, Message = message, IsChatMessage = isChatMessage});
|
||||
}
|
||||
|
||||
public void SendDeath(IEntity killedBy)
|
||||
{
|
||||
var message = _client.CreateMessage();
|
||||
|
||||
message.Write((int)UdpNetworkPacketType.PlayerDying);
|
||||
message.Write(_playerName);
|
||||
message.Write(killedBy.Owner); // TODO: server side score board
|
||||
|
||||
_client.SendMessage(message, NetDeliveryMethod.ReliableUnordered);
|
||||
}
|
||||
|
||||
private void WriteLevelRequest()
|
||||
{
|
||||
Trace.WriteLine("WriteLevelRequest");
|
||||
var message = _client.CreateMessage();
|
||||
|
||||
message.Write((int)UdpNetworkPacketType.LevelRequest);
|
||||
|
||||
_client.SendMessage(message, NetDeliveryMethod.ReliableUnordered);
|
||||
System.Diagnostics.Trace.WriteLine("Sent level request...");
|
||||
}
|
||||
|
||||
private void WriteJoiningMessage()
|
||||
{
|
||||
Trace.WriteLine("WriteJoiningMessage");
|
||||
|
||||
var message = _client.CreateMessage();
|
||||
|
||||
message.Write((int)UdpNetworkPacketType.PlayerJoining);
|
||||
message.Write(_playerName);
|
||||
|
||||
_client.SendMessage(message, NetDeliveryMethod.ReliableUnordered);
|
||||
}
|
||||
|
||||
private void WaitForJoin()
|
||||
{
|
||||
System.Diagnostics.Trace.WriteLine("WaitForJoin");
|
||||
bool hasAnswer = false;
|
||||
while (!hasAnswer) // wait for answer
|
||||
{
|
||||
NetIncomingMessage message;
|
||||
while ((message = _client.ReadMessage()) != null)
|
||||
{
|
||||
if (message.MessageType == NetIncomingMessageType.Data)
|
||||
{
|
||||
var packetType = (UdpNetworkPacketType)message.ReadInt32();
|
||||
if (packetType == UdpNetworkPacketType.PlayerJoined)
|
||||
{
|
||||
string owner = message.ReadString();
|
||||
|
||||
int colorIndex = message.ReadInt32();
|
||||
Color color = _playerColors[colorIndex];
|
||||
|
||||
_colorIndex = Array.IndexOf(_playerColors, color);
|
||||
|
||||
Vector2 pos = message.ReadVector2();
|
||||
Vector2 velocity = message.ReadVector2();
|
||||
float angle = message.ReadFloat();
|
||||
int collisionSize = message.ReadInt32();
|
||||
var collisionData = message.ReadBytes(collisionSize);
|
||||
|
||||
_level.CollisionData = (bool[])ByteArrayToObject(collisionData);
|
||||
_level.BuildLevelFromCollisionData();
|
||||
|
||||
Player p = Player.CreatePlayer(pos, owner);
|
||||
p.Color = color;
|
||||
p.Position = pos;
|
||||
p.Velocity = velocity;
|
||||
p.Angle = angle;
|
||||
|
||||
_localPlayer = p;
|
||||
_players.Add(p);
|
||||
|
||||
hasAnswer = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
NetIncomingMessage message;
|
||||
|
||||
while ((message = _client.ReadMessage()) != null)
|
||||
{
|
||||
ReadBuffer(message);
|
||||
}
|
||||
|
||||
if (EntitiesToSend.Count > 0)
|
||||
{
|
||||
WriteServerShots();
|
||||
}
|
||||
|
||||
WritePlayerUpdatePacket();
|
||||
}
|
||||
|
||||
private void WriteServerShots()
|
||||
{
|
||||
SendEntities(EntitiesToSend);
|
||||
}
|
||||
|
||||
private void WritePlayerUpdatePacket()
|
||||
{
|
||||
var message = _client.CreateMessage();
|
||||
|
||||
message.Write((int)UdpNetworkPacketType.PlayerUpdate);
|
||||
message.Write(_localPlayer.Owner);
|
||||
message.Write(_colorIndex);
|
||||
message.Write(_localPlayer.Position);
|
||||
message.Write(_localPlayer.Velocity);
|
||||
message.Write(_localPlayer.Angle);
|
||||
message.Write(_localPlayer.Health);
|
||||
|
||||
_client.SendMessage(message, NetDeliveryMethod.UnreliableSequenced);
|
||||
}
|
||||
|
||||
private void ReadBuffer(NetIncomingMessage message)
|
||||
{
|
||||
switch (message.MessageType)
|
||||
{
|
||||
case NetIncomingMessageType.Data:
|
||||
{
|
||||
var packetType = (UdpNetworkPacketType)message.ReadInt32();
|
||||
|
||||
switch (packetType)
|
||||
{
|
||||
case UdpNetworkPacketType.PlayerUpdate:
|
||||
ReadPlayers(message);
|
||||
break;
|
||||
case UdpNetworkPacketType.EntitiesCreated:
|
||||
ReadEntities(message);
|
||||
break;
|
||||
case UdpNetworkPacketType.MessageReceived:
|
||||
ReadMessage(message);
|
||||
break;
|
||||
case UdpNetworkPacketType.PlayerDied:
|
||||
ReadPlayerDied(message);
|
||||
break;
|
||||
case UdpNetworkPacketType.SendingScoreBoard:
|
||||
ReadScoreBoard(message);
|
||||
break;
|
||||
case UdpNetworkPacketType.EntityCreated:
|
||||
ReadEntity(message);
|
||||
break;
|
||||
case UdpNetworkPacketType.PlayerDisconnected:
|
||||
RemovePlayer(message);
|
||||
break;
|
||||
case UdpNetworkPacketType.LevelResponse:
|
||||
ReadLevel(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
_client.Recycle(message);
|
||||
}
|
||||
|
||||
private void WaitForLevel()
|
||||
{
|
||||
System.Diagnostics.Trace.WriteLine("WaitForLevel");
|
||||
while (_level == null) // wait for level
|
||||
{
|
||||
NetIncomingMessage message;
|
||||
while ((message = _client.ReadMessage()) != null)
|
||||
{
|
||||
if (message.MessageType == NetIncomingMessageType.Data)
|
||||
{
|
||||
var packetType = (UdpNetworkPacketType)message.ReadInt32();
|
||||
if (packetType == UdpNetworkPacketType.LevelResponse)
|
||||
{
|
||||
ReadLevel(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong _levelLength;
|
||||
private static ulong _levelReceived;
|
||||
private static FileStream _levelWriteStream;
|
||||
private static int _levelTimeStarted;
|
||||
string levelPath = string.Empty;
|
||||
|
||||
private void ReadLevel(NetIncomingMessage message)
|
||||
{
|
||||
int chunkLen = message.LengthBytes;
|
||||
if (_levelLength == 0)
|
||||
{
|
||||
_levelLength = message.ReadUInt64();
|
||||
string filename = message.ReadString();
|
||||
|
||||
var downloadLevelFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Downloads\\Levels\\");
|
||||
levelPath = Path.Combine(downloadLevelFolder, filename);
|
||||
|
||||
_levelWriteStream = new FileStream(levelPath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
_levelTimeStarted = Environment.TickCount;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] all = message.ReadBytes(message.LengthBytes - 4); // offset for UdpNetworkPacketType
|
||||
_levelReceived += (ulong)all.Length;
|
||||
_levelWriteStream.Write(all, 0, all.Length);
|
||||
|
||||
//int v = (int)(((float)_levelReceived / (float)_levelLength) * 100.0f);
|
||||
//int passed = Environment.TickCount - _levelTimeStarted;
|
||||
//double psec = (double)passed / 1000.0;
|
||||
//double bps = (double)_levelReceived / psec;
|
||||
|
||||
//var passedText = NetUtility.ToHumanReadable((long)bps) + " per second";
|
||||
|
||||
if (_levelReceived >= _levelLength)
|
||||
{
|
||||
Trace.WriteLine("Got level");
|
||||
_levelWriteStream.Flush();
|
||||
_levelWriteStream.Close();
|
||||
_levelWriteStream.Dispose();
|
||||
|
||||
_level = LevelLoader.LoadLevel(levelPath, _game.Content, _game.GraphicsDevice);
|
||||
_game.Level = _level;
|
||||
}
|
||||
}
|
||||
|
||||
private void RemovePlayer(NetIncomingMessage message)
|
||||
{
|
||||
string name = message.ReadString();
|
||||
|
||||
Player player = _players.Find(p => p.Owner == name);
|
||||
if (player != null)
|
||||
{
|
||||
EntityFactory.Instance.RemoveEntity(player);
|
||||
|
||||
NetworkMessenger.DisplayMessage(new NetworkMessage()
|
||||
{
|
||||
Color = player.Color,
|
||||
Message = string.Format("{0} has left.", player.Owner),
|
||||
Sent = DateTime.Now
|
||||
});
|
||||
|
||||
_players.Remove(player);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadScoreBoard(NetIncomingMessage message)
|
||||
{
|
||||
int count = message.ReadInt32();
|
||||
|
||||
var scoreBoard = new List<ScoreBoardItem>();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var item = new ScoreBoardItem
|
||||
{
|
||||
Name = message.ReadString(),
|
||||
Kills = message.ReadInt32(),
|
||||
Deaths = message.ReadInt32(),
|
||||
Joined = DateTime.Parse(message.ReadString(), CultureInfo.InvariantCulture),
|
||||
Ping = message.ReadInt64()
|
||||
};
|
||||
|
||||
scoreBoard.Add(item);
|
||||
}
|
||||
|
||||
ScoreBoard.CurrentScoreBoard = scoreBoard;
|
||||
}
|
||||
|
||||
private void ReadPlayerDied(NetIncomingMessage message)
|
||||
{
|
||||
string playerName = message.ReadString();
|
||||
|
||||
Player player = _players.Find(p => p.Owner == playerName);
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
_game.AddExplosion(player.Position, 1f);
|
||||
player.Kill();
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadEntities(NetIncomingMessage message)
|
||||
{
|
||||
int count = message.ReadInt32();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
string entityType = message.ReadString();
|
||||
string owner = message.ReadString();
|
||||
Vector2 pos = message.ReadVector2();
|
||||
Vector2 velocity = message.ReadVector2();
|
||||
float angle = message.ReadFloat();
|
||||
|
||||
EntityFactory.Instance.CreateEntity<IEntity>(Type.GetType(entityType), owner, pos, velocity, angle);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadEntity(NetIncomingMessage message)
|
||||
{
|
||||
string entityType = message.ReadString();
|
||||
string owner = message.ReadString();
|
||||
Vector2 pos = message.ReadVector2();
|
||||
Vector2 velocity = message.ReadVector2();
|
||||
float angle = message.ReadFloat();
|
||||
|
||||
EntityFactory.Instance.CreateEntity<IEntity>(Type.GetType(entityType), owner, pos, velocity, angle);
|
||||
}
|
||||
|
||||
private void ReadPlayers(NetIncomingMessage message)
|
||||
{
|
||||
string owner = message.ReadString();
|
||||
|
||||
int colorIndex = message.ReadInt32();
|
||||
Color color = _playerColors[colorIndex];
|
||||
|
||||
Vector2 pos = message.ReadVector2();
|
||||
Vector2 velocity = message.ReadVector2();
|
||||
float angle = message.ReadFloat();
|
||||
|
||||
if (owner == LocalPlayer.Owner)
|
||||
return;
|
||||
|
||||
Player player = _players.Find(p => p.Owner == owner);
|
||||
if (player == null)
|
||||
{
|
||||
Player p = Player.CreatePlayer(new Vector2(300, 280), owner);
|
||||
|
||||
p.Position = pos;
|
||||
p.Color = color;
|
||||
p.Velocity = velocity;
|
||||
p.Angle = angle;
|
||||
p.Owner = owner;
|
||||
|
||||
_players.Add(p);
|
||||
|
||||
NetworkMessenger.DisplayMessage(new NetworkMessage()
|
||||
{
|
||||
Color = p.Color,
|
||||
Message = string.Format("{0} has joined.", p.Owner),
|
||||
Sent = DateTime.Now
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = _players.IndexOf(player);
|
||||
player.Owner = owner;
|
||||
player.Color = color;
|
||||
player.Position = pos;
|
||||
player.Velocity = velocity;
|
||||
player.Angle = angle;
|
||||
|
||||
_players[index] = player;
|
||||
}
|
||||
}
|
||||
|
||||
public void SendEntities(List<IEntity> entities)
|
||||
{
|
||||
var message = _client.CreateMessage();
|
||||
|
||||
message.Write((int)UdpNetworkPacketType.EntitiesCreated);
|
||||
message.Write(entities.Count);
|
||||
|
||||
lock (EntitiesToSend)
|
||||
{
|
||||
for (int i = entities.Count - 1; i >= 0; i--)
|
||||
{
|
||||
message.Write(entities[i].GetType().ToString());
|
||||
message.Write(_localPlayer.Owner);
|
||||
message.Write(entities[i].Position);
|
||||
message.Write(entities[i].Velocity);
|
||||
message.Write(entities[i].Angle);
|
||||
}
|
||||
}
|
||||
|
||||
EntitiesToSend.Clear();
|
||||
|
||||
_client.SendMessage(message, NetDeliveryMethod.UnreliableSequenced);
|
||||
}
|
||||
|
||||
public void SendEntity(IEntity entity)
|
||||
{
|
||||
var message = _client.CreateMessage();
|
||||
|
||||
message.Write((int)UdpNetworkPacketType.EntityCreated);
|
||||
message.Write(entity.GetType().ToString());
|
||||
message.Write(_localPlayer.Owner);
|
||||
message.Write(entity.Position);
|
||||
message.Write(entity.Velocity);
|
||||
message.Write(entity.Angle);
|
||||
|
||||
_client.SendMessage(message, NetDeliveryMethod.UnreliableSequenced);
|
||||
}
|
||||
|
||||
public void GetServerList()
|
||||
{
|
||||
var listRequest = _client.CreateMessage();
|
||||
listRequest.Write((byte)UdpNetworkPacketType.RequestHostList);
|
||||
_client.SendUnconnectedMessage(listRequest, _masterServerEndpoint);
|
||||
}
|
||||
|
||||
public void RequestNATIntroduction(long hostid)
|
||||
{
|
||||
if (hostid == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_masterServerEndpoint == null)
|
||||
throw new Exception("Must connect to master server first!");
|
||||
|
||||
IPAddress mask;
|
||||
var message = _client.CreateMessage();
|
||||
message.Write((byte)UdpNetworkPacketType.RequestIntroduction);
|
||||
|
||||
message.Write(new IPEndPoint(NetUtility.GetMyAddress(out mask), _client.Port));
|
||||
message.Write(hostid);
|
||||
message.Write(_playerName);
|
||||
|
||||
_client.SendUnconnectedMessage(message, _masterServerEndpoint);
|
||||
}
|
||||
}
|
||||
}
|
361
SpacePew/Networking/UdpNetworkGui.cs
Normal file
361
SpacePew/Networking/UdpNetworkGui.cs
Normal file
@@ -0,0 +1,361 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Audio;
|
||||
using Microsoft.Xna.Framework.Content;
|
||||
using Microsoft.Xna.Framework.GamerServices;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Microsoft.Xna.Framework.Media;
|
||||
using Microsoft.Xna.Framework.Net;
|
||||
using Microsoft.Xna.Framework.Storage;
|
||||
using System.Threading;
|
||||
using SpacePew.Models;
|
||||
#if WINDOWS
|
||||
using TomShane.Neoforce.Controls;
|
||||
using Lidgren.Network;
|
||||
using System.Net;
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace SpacePew.Networking
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a game component that implements IUpdateable.
|
||||
/// </summary>
|
||||
#if WINDOWS
|
||||
public class UdpNetworkGui : Microsoft.Xna.Framework.DrawableGameComponent
|
||||
{
|
||||
private MainGame _game;
|
||||
|
||||
private UdpClient _client;
|
||||
private UdpServer _server;
|
||||
|
||||
private GraphicsDeviceManager _graphics;
|
||||
|
||||
private Manager _manager;
|
||||
private Window _window;
|
||||
private TabControl _tabControl;
|
||||
private TextBox _nameTextBox;
|
||||
private TextBox _nameTextBox2;
|
||||
private TextBox _ipTextBox;
|
||||
private Label _nameLabel;
|
||||
private Label _nameLabel2;
|
||||
private ListBox _localGamesListBox;
|
||||
|
||||
private Label _joinErrorLabel;
|
||||
|
||||
private Label _ipLabel;
|
||||
private Button _createButton;
|
||||
private Button _joinButton;
|
||||
private Button _refreshButton;
|
||||
|
||||
public UdpNetworkGui(MainGame game, GraphicsDeviceManager graphics, UdpClient client, UdpServer server)
|
||||
: base(game)
|
||||
{
|
||||
_game = game;
|
||||
_graphics = graphics;
|
||||
_client = client;
|
||||
_server = server;
|
||||
|
||||
_client.CurrentClient.Start();
|
||||
|
||||
_manager = new Manager(game, _graphics, "Default");
|
||||
_manager.Skin = new Skin(_manager, "Default");
|
||||
_manager.AutoCreateRenderTarget = true;
|
||||
_manager.TargetFrames = 60;
|
||||
_manager.LogUnhandledExceptions = false;
|
||||
_manager.ShowSoftwareCursor = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows the game component to perform any initialization it needs to before starting
|
||||
/// to run. This is where it can query for any required services and load content.
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_window = new Window(_manager);
|
||||
_window.Init();
|
||||
_window.Text = "Space, pew pew!";
|
||||
_window.Width = 480;
|
||||
_window.Height = 200;
|
||||
_window.Center();
|
||||
_window.CloseButtonVisible = false;
|
||||
_window.Resizable = false;
|
||||
_window.Visible = true;
|
||||
|
||||
_tabControl = new TabControl(_manager);
|
||||
_tabControl.Width = _window.Width;
|
||||
_tabControl.Height = _window.Height;
|
||||
_tabControl.Parent = _window;
|
||||
|
||||
_nameLabel = new Label(_manager);
|
||||
_nameLabel.Init();
|
||||
_nameLabel.Width = 100;
|
||||
_nameLabel.Height = 24;
|
||||
_nameLabel.Text = "Name";
|
||||
_nameLabel.Left = 10;
|
||||
_nameLabel.Top = 10;
|
||||
|
||||
_nameTextBox = new TextBox(_manager);
|
||||
_nameTextBox.Init();
|
||||
_nameTextBox.Width = 140;
|
||||
_nameTextBox.Height = 24;
|
||||
_nameTextBox.Left = 50;
|
||||
_nameTextBox.Top = 10;
|
||||
|
||||
_nameLabel2 = new Label(_manager);
|
||||
_nameLabel2.Init();
|
||||
_nameLabel2.Width = 100;
|
||||
_nameLabel2.Height = 24;
|
||||
_nameLabel2.Text = "Name";
|
||||
_nameLabel2.Left = 10;
|
||||
_nameLabel2.Top = 10;
|
||||
|
||||
_nameTextBox2 = new TextBox(_manager);
|
||||
_nameTextBox2.Init();
|
||||
_nameTextBox2.Width = 140;
|
||||
_nameTextBox2.Height = 24;
|
||||
_nameTextBox2.Left = 50;
|
||||
_nameTextBox2.Top = 10;
|
||||
|
||||
_createButton = new Button(_manager);
|
||||
_createButton.Init();
|
||||
_createButton.Text = "Create game";
|
||||
_createButton.Width = 140;
|
||||
_createButton.Height = 24;
|
||||
_createButton.Left = 50;
|
||||
_createButton.Top = 40;
|
||||
_createButton.Click += _createButton_Click;
|
||||
|
||||
_ipLabel = new Label(_manager);
|
||||
_ipLabel.Init();
|
||||
_ipLabel.Width = 100;
|
||||
_ipLabel.Height = 24;
|
||||
_ipLabel.Text = "Host";
|
||||
_ipLabel.Left = 10;
|
||||
_ipLabel.Top = 40;
|
||||
|
||||
_ipTextBox = new TextBox(_manager);
|
||||
_ipTextBox.Init();
|
||||
_ipTextBox.Width = 140;
|
||||
_ipTextBox.Height = 24;
|
||||
_ipTextBox.Left = 50;
|
||||
_ipTextBox.Top = 40;
|
||||
|
||||
_joinErrorLabel = new Label(_manager);
|
||||
_joinErrorLabel.Init();
|
||||
_joinErrorLabel.Width = 460;
|
||||
_joinErrorLabel.Height = 24;
|
||||
_joinErrorLabel.Left = 10;
|
||||
_joinErrorLabel.Top = 110;
|
||||
_joinErrorLabel.Text = string.Empty;
|
||||
_joinErrorLabel.TextColor = Color.DarkRed;
|
||||
|
||||
_joinButton = new Button(_manager);
|
||||
_joinButton.Init();
|
||||
_joinButton.Text = "Join game";
|
||||
_joinButton.Width = 140;
|
||||
_joinButton.Height = 24;
|
||||
_joinButton.Left = 50;
|
||||
_joinButton.Top = 70;
|
||||
_joinButton.Anchor = Anchors.Bottom;
|
||||
_joinButton.Click += _joinButton_Click;
|
||||
|
||||
_localGamesListBox = new ListBox(_manager);
|
||||
_localGamesListBox.Init();
|
||||
_localGamesListBox.Left = 200;
|
||||
_localGamesListBox.Top = 10;
|
||||
_localGamesListBox.Height = 84;
|
||||
_localGamesListBox.Width = 254;
|
||||
_localGamesListBox.ItemIndexChanged += new TomShane.Neoforce.Controls.EventHandler(_localGamesListBox_ItemIndexChanged);
|
||||
|
||||
_refreshButton = new Button(_manager);
|
||||
_refreshButton.Init();
|
||||
_refreshButton.Text = "Refresh";
|
||||
_refreshButton.Width = 140;
|
||||
_refreshButton.Height = 24;
|
||||
_refreshButton.Left = 314;
|
||||
_refreshButton.Top = 104;
|
||||
_refreshButton.Click += _refreshButton_Click;
|
||||
|
||||
_nameTextBox.Click += ChangeTextBoxColor;
|
||||
_nameTextBox2.Click += ChangeTextBoxColor;
|
||||
_ipTextBox.Click += ChangeTextBoxColor;
|
||||
|
||||
_tabControl.AddPage();
|
||||
_tabControl.AddPage();
|
||||
_tabControl.TabPages[0].Text = "Create";
|
||||
_tabControl.TabPages[0].Add(_nameLabel);
|
||||
_tabControl.TabPages[0].Add(_nameTextBox);
|
||||
_tabControl.TabPages[0].Add(_createButton);
|
||||
|
||||
_tabControl.TabPages[1].Text = "Join";
|
||||
_tabControl.TabPages[1].Add(_nameLabel2);
|
||||
_tabControl.TabPages[1].Add(_nameTextBox2);
|
||||
_tabControl.TabPages[1].Add(_ipLabel);
|
||||
_tabControl.TabPages[1].Add(_ipTextBox);
|
||||
_tabControl.TabPages[1].Add(_joinButton);
|
||||
_tabControl.TabPages[1].Add(_joinErrorLabel);
|
||||
_tabControl.TabPages[1].Add(_localGamesListBox);
|
||||
_tabControl.TabPages[1].Add(_refreshButton);
|
||||
|
||||
_manager.Add(_window);
|
||||
_manager.Initialize();
|
||||
|
||||
_client.CurrentClient.DiscoverLocalPeers(SpacePew.Common.Constants.GameServerPort);
|
||||
}
|
||||
|
||||
private void _refreshButton_Click(object sender, TomShane.Neoforce.Controls.EventArgs e)
|
||||
{
|
||||
_localGamesListBox.Items.Clear();
|
||||
_client.CurrentClient.DiscoverLocalPeers(SpacePew.Common.Constants.GameServerPort);
|
||||
}
|
||||
|
||||
private void ChangeTextBoxColor(object sender, TomShane.Neoforce.Controls.EventArgs e)
|
||||
{
|
||||
((TomShane.Neoforce.Controls.Control)sender).Color = Color.TransparentBlack;
|
||||
}
|
||||
|
||||
private void _localGamesListBox_ItemIndexChanged(object sender, TomShane.Neoforce.Controls.EventArgs e)
|
||||
{
|
||||
_ipTextBox.Text = _localGamesListBox.Items[_localGamesListBox.ItemIndex].ToString();
|
||||
}
|
||||
|
||||
private void _createButton_Click(object sender, TomShane.Neoforce.Controls.EventArgs e)
|
||||
{
|
||||
_nameTextBox.Color = string.IsNullOrEmpty(_nameTextBox.Text) ? Color.Pink : Color.TransparentBlack;
|
||||
|
||||
if (_nameTextBox.Color == Color.Pink)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string levelPath = AppDomain.CurrentDomain.BaseDirectory + "\\Levels\\hippie.zip"; // TODO: V<>lja
|
||||
var level = LevelLoader.LoadLevel(levelPath, _game.Content, GraphicsDevice);
|
||||
|
||||
_server.SetLevel(level);
|
||||
|
||||
_window.Close();
|
||||
Trace.WriteLine("CreateSession()");
|
||||
_server.CreateSession();
|
||||
|
||||
new Thread(_server.Listen).Start();
|
||||
|
||||
_client.JoinSession("127.0.0.1", _nameTextBox.Text);
|
||||
|
||||
_game.AddGameComponents();
|
||||
_game.Components.Remove(this);
|
||||
}
|
||||
|
||||
private void _joinButton_Click(object sender, TomShane.Neoforce.Controls.EventArgs e)
|
||||
{
|
||||
_ipTextBox.Color = string.IsNullOrEmpty(_ipTextBox.Text) ? Color.Pink : Color.TransparentBlack;
|
||||
_nameTextBox2.Color = string.IsNullOrEmpty(_nameTextBox2.Text) ? Color.Pink : Color.TransparentBlack;
|
||||
|
||||
if (_ipTextBox.Color == Color.Pink || _nameTextBox2.Color == Color.Pink)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var splits = _ipTextBox.Text.Split(' ');
|
||||
if (splits.Count() > 1)
|
||||
{
|
||||
var host = Int64.Parse(splits[0]);
|
||||
_client.RequestNATIntroduction(host);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
_client.JoinSession(_ipTextBox.Text, _nameTextBox2.Text);
|
||||
|
||||
_game.AddGameComponents();
|
||||
_game.Components.Remove(this);
|
||||
}
|
||||
catch (NetException ex)
|
||||
{
|
||||
_joinErrorLabel.Text = ex.Message;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime _lastUpdate = DateTime.Now.AddSeconds(-5);
|
||||
private static Dictionary<long, IPEndPoint[]> _hostList = new Dictionary<long, IPEndPoint[]>();
|
||||
|
||||
/// <summary>
|
||||
/// Allows the game component to update itself.
|
||||
/// </summary>
|
||||
/// <param name="gameTime">Provides a snapshot of timing values.</param>
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
_manager.Update(gameTime);
|
||||
|
||||
if (_lastUpdate <= DateTime.Now.AddSeconds(-5))
|
||||
{
|
||||
_lastUpdate = DateTime.Now;
|
||||
_client.CurrentClient.DiscoverLocalPeers(SpacePew.Common.Constants.GameServerPort);
|
||||
_client.GetServerList();
|
||||
}
|
||||
NetIncomingMessage message;
|
||||
while ((message = _client.CurrentClient.ReadMessage()) != null)
|
||||
{
|
||||
if (message.MessageType == NetIncomingMessageType.UnconnectedData)
|
||||
{
|
||||
var id = message.ReadInt64();
|
||||
var hostInternal = message.ReadIPEndPoint();
|
||||
var hostExternal = message.ReadIPEndPoint();
|
||||
|
||||
_hostList[id] = new IPEndPoint[] { hostInternal, hostExternal };
|
||||
|
||||
_localGamesListBox.Items.Clear();
|
||||
foreach (var kvp in _hostList)
|
||||
{
|
||||
_localGamesListBox.Items.Add(kvp.Key.ToString() + " (" + kvp.Value[1] + ")");
|
||||
}
|
||||
}
|
||||
else if (message.MessageType == NetIncomingMessageType.DiscoveryResponse)
|
||||
{
|
||||
IPEndPoint ep = message.ReadIPEndPoint();
|
||||
if (!_localGamesListBox.Items.Contains(ep.Address.ToString()))
|
||||
{
|
||||
_localGamesListBox.Items.Add(ep.Address.ToString());
|
||||
}
|
||||
}
|
||||
else if (message.MessageType == NetIncomingMessageType.NatIntroductionSuccess)
|
||||
{
|
||||
try
|
||||
{
|
||||
_client.JoinSession(message.SenderEndPoint, _nameTextBox2.Text);
|
||||
|
||||
_game.AddGameComponents();
|
||||
_game.Components.Remove(this);
|
||||
}
|
||||
catch (NetException ex)
|
||||
{
|
||||
_joinErrorLabel.Text = ex.Message;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.Update(gameTime);
|
||||
}
|
||||
|
||||
public override void Draw(GameTime gameTime)
|
||||
{
|
||||
_manager.BeginDraw(gameTime);
|
||||
|
||||
GraphicsDevice.Clear(Color.Black);
|
||||
|
||||
_manager.EndDraw();
|
||||
|
||||
base.Draw(gameTime);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
405
SpacePew/Networking/UdpServer.cs
Normal file
405
SpacePew/Networking/UdpServer.cs
Normal file
@@ -0,0 +1,405 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
using SpacePew.Extensions;
|
||||
|
||||
using Lidgren.Network;
|
||||
using System.Net;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Net.NetworkInformation;
|
||||
using SpacePew.Models;
|
||||
using SpacePew.Common;
|
||||
|
||||
namespace SpacePew.Networking
|
||||
{
|
||||
public class UdpServer : UdpBase
|
||||
{
|
||||
private NetPeerConfiguration _configuration;
|
||||
private NetServer _server;
|
||||
private Level _level;
|
||||
|
||||
private readonly List<ScoreBoardItem> _scoreBoard;
|
||||
private readonly List<string> _players;
|
||||
|
||||
private readonly Vector2 _defaultPosition = new Vector2(300, 280);
|
||||
|
||||
private IPEndPoint _masterServerEndpoint;
|
||||
|
||||
public UdpServer()
|
||||
: base()
|
||||
{
|
||||
_masterServerEndpoint = NetUtility.Resolve("spacepew.wodanaz.se", Constants.MasterServerPort); // TODO: Fixa upp masterserver någonstans
|
||||
_scoreBoard = new List<ScoreBoardItem>();
|
||||
_players = new List<string>();
|
||||
}
|
||||
|
||||
public void CreateSession()
|
||||
{
|
||||
if (_server == null)
|
||||
{
|
||||
_configuration = new NetPeerConfiguration("SpacePew")
|
||||
{
|
||||
MaximumConnections = 16,
|
||||
Port = SpacePew.Common.Constants.GameServerPort
|
||||
};
|
||||
|
||||
_configuration.EnableMessageType(NetIncomingMessageType.DiscoveryRequest);
|
||||
_configuration.EnableMessageType(NetIncomingMessageType.DiscoveryResponse);
|
||||
_configuration.EnableMessageType(NetIncomingMessageType.NatIntroductionSuccess);
|
||||
|
||||
_server = new NetServer(_configuration);
|
||||
_server.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLevel(Level level)
|
||||
{
|
||||
_level = level;
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
if (_server != null && _server.Status == NetPeerStatus.Running)
|
||||
{
|
||||
_server.Shutdown("Quitting");
|
||||
}
|
||||
}
|
||||
|
||||
public void Listen()
|
||||
{
|
||||
|
||||
System.Diagnostics.Trace.WriteLine("Server.Listen()");
|
||||
while (_server.Status == NetPeerStatus.Running)
|
||||
{
|
||||
NetIncomingMessage message;
|
||||
while ((message = _server.ReadMessage()) != null)
|
||||
{
|
||||
ReadBuffer(message, message.SenderConnection);
|
||||
}
|
||||
|
||||
if (NetTime.Now > lastRegistered + 60)
|
||||
{
|
||||
RegisterWithMasterServer();
|
||||
}
|
||||
|
||||
StreamLevelToClients();
|
||||
}
|
||||
}
|
||||
|
||||
private void StreamLevelToClients()
|
||||
{
|
||||
foreach (var conn in _server.Connections)
|
||||
{
|
||||
var client = conn.Tag as StreamingClient;
|
||||
if (client != null)
|
||||
client.Heartbeat();
|
||||
}
|
||||
}
|
||||
|
||||
float lastRegistered = -60.0f;
|
||||
private void RegisterWithMasterServer()
|
||||
{
|
||||
IPAddress mask;
|
||||
IPAddress localAddress = NetUtility.GetMyAddress(out mask);
|
||||
var message = _server.CreateMessage();
|
||||
message.Write((byte)UdpNetworkPacketType.RegisterHost);
|
||||
message.Write(_server.UniqueIdentifier);
|
||||
message.Write(new IPEndPoint(localAddress, Constants.GameServerPort));
|
||||
|
||||
_server.SendUnconnectedMessage(message, _masterServerEndpoint);
|
||||
|
||||
lastRegistered = (float)NetTime.Now;
|
||||
}
|
||||
|
||||
private void ReadBuffer(NetIncomingMessage message, NetConnection sender)
|
||||
{
|
||||
switch (message.MessageType)
|
||||
{
|
||||
case NetIncomingMessageType.Data:
|
||||
{
|
||||
var packetType = (UdpNetworkPacketType)message.ReadInt32();
|
||||
switch (packetType)
|
||||
{
|
||||
case UdpNetworkPacketType.PlayerUpdate:
|
||||
WritePlayerUpdate(message, sender);
|
||||
break;
|
||||
case UdpNetworkPacketType.EntitiesCreated:
|
||||
WriteEntities(message, sender);
|
||||
break;
|
||||
case UdpNetworkPacketType.MessageSent:
|
||||
WriteMessage(message, sender);
|
||||
break;
|
||||
case UdpNetworkPacketType.EntityCreated:
|
||||
WriteEntity(message, sender);
|
||||
break;
|
||||
case UdpNetworkPacketType.PlayerDying:
|
||||
WritePlayerDied(message, sender);
|
||||
break;
|
||||
case UdpNetworkPacketType.RequestingScoreboard:
|
||||
WriteScoreBoard(sender);
|
||||
break;
|
||||
case UdpNetworkPacketType.PlayerJoining:
|
||||
{
|
||||
string name = message.ReadString();
|
||||
_players.Add(name);
|
||||
sender.Tag = name;
|
||||
_scoreBoard.Add(new ScoreBoardItem() { Name = name, Joined = DateTime.Now });
|
||||
|
||||
WritePlayerJoinedPacket(sender, name);
|
||||
break;
|
||||
}
|
||||
case UdpNetworkPacketType.PlayerDisconnecting:
|
||||
{
|
||||
string name = message.ReadString();
|
||||
|
||||
WritePlayerDisconnectedPacket(sender, name);
|
||||
break;
|
||||
}
|
||||
case UdpNetworkPacketType.LevelRequest:
|
||||
{
|
||||
WriteLevelResponse(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NetIncomingMessageType.ConnectionApproval:
|
||||
AuthorizeConnection(sender);
|
||||
break;
|
||||
case NetIncomingMessageType.DiscoveryRequest:
|
||||
WriteDiscoveryResponse(sender);
|
||||
break;
|
||||
case NetIncomingMessageType.StatusChanged:
|
||||
{
|
||||
var status = (NetConnectionStatus)message.ReadByte();
|
||||
var msg = message.ReadString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_server.Recycle(message);
|
||||
}
|
||||
|
||||
private void WriteLevelResponse(NetIncomingMessage message)
|
||||
{
|
||||
message.SenderConnection.Tag = new StreamingClient(message.SenderConnection, _level.FilePath);
|
||||
}
|
||||
|
||||
private void WriteDiscoveryResponse(NetConnection sender)
|
||||
{
|
||||
if (sender == null)
|
||||
return;
|
||||
|
||||
var message = _server.CreateMessage();
|
||||
|
||||
// TODO: Skicka med lite stats
|
||||
_server.SendDiscoveryResponse(message, sender.RemoteEndPoint);
|
||||
}
|
||||
|
||||
private void WritePlayerDisconnectedPacket(NetConnection sender, string name)
|
||||
{
|
||||
var message = _server.CreateMessage();
|
||||
|
||||
message.Write((int)UdpNetworkPacketType.PlayerDisconnected);
|
||||
message.Write(name);
|
||||
|
||||
_server.SendToAll(message, sender, NetDeliveryMethod.ReliableUnordered, 0);
|
||||
|
||||
_players.Remove(name);
|
||||
}
|
||||
|
||||
private void WriteScoreBoard(NetConnection sender)
|
||||
{
|
||||
var message = _server.CreateMessage();
|
||||
|
||||
message.Write((int)UdpNetworkPacketType.SendingScoreBoard);
|
||||
|
||||
int count = _scoreBoard.Count;
|
||||
|
||||
message.Write(count);
|
||||
|
||||
var ping = new Ping();
|
||||
|
||||
foreach (var item in _scoreBoard)
|
||||
{
|
||||
message.Write(item.Name);
|
||||
message.Write(item.Kills);
|
||||
message.Write(item.Deaths);
|
||||
message.Write(item.Joined.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
//TODO: Fixa riktig ping
|
||||
//NetConnection connection = _server.Connections.Find(s => s.Tag.ToString() == item.Name);
|
||||
|
||||
//long pingTime = ping.Send(connection.RemoteEndpoint.Address).RoundtripTime;
|
||||
const long pinglong = 14;
|
||||
message.Write(pinglong);
|
||||
}
|
||||
|
||||
_server.SendMessage(message, sender, NetDeliveryMethod.UnreliableSequenced);
|
||||
}
|
||||
|
||||
private void WriteMessage(NetIncomingMessage incomingMessage, NetConnection sender)
|
||||
{
|
||||
Vector3 colorVector = incomingMessage.ReadVector3();
|
||||
string dateString = incomingMessage.ReadString();
|
||||
string message = incomingMessage.ReadString();
|
||||
bool isChatMessage = incomingMessage.ReadBoolean();
|
||||
|
||||
var outgoingMessage = _server.CreateMessage();
|
||||
|
||||
outgoingMessage.Write((int)UdpNetworkPacketType.MessageReceived);
|
||||
outgoingMessage.Write(colorVector);
|
||||
outgoingMessage.Write(dateString);
|
||||
outgoingMessage.Write(message);
|
||||
outgoingMessage.Write(isChatMessage);
|
||||
|
||||
_server.SendToAll(outgoingMessage, sender, NetDeliveryMethod.UnreliableSequenced, 1);
|
||||
}
|
||||
|
||||
private void WritePlayerDied(NetIncomingMessage incomingMessage, NetConnection sender)
|
||||
{
|
||||
string playerName = incomingMessage.ReadString();
|
||||
string killedBy = incomingMessage.ReadString();
|
||||
|
||||
if (playerName != killedBy)
|
||||
{
|
||||
AddKillScore(killedBy);
|
||||
}
|
||||
|
||||
AddDeathScore(playerName);
|
||||
|
||||
var message = _server.CreateMessage();
|
||||
|
||||
message.Write((int)UdpNetworkPacketType.PlayerDied);
|
||||
message.Write(playerName);
|
||||
|
||||
_server.SendToAll(message, sender, NetDeliveryMethod.ReliableUnordered, 0);
|
||||
}
|
||||
|
||||
private void AddKillScore(string name)
|
||||
{
|
||||
ScoreBoardItem item = _scoreBoard.Find(i => i.Name == name);
|
||||
item.Kills++;
|
||||
_scoreBoard[_scoreBoard.IndexOf(item)] = item;
|
||||
}
|
||||
|
||||
private void AddDeathScore(string name)
|
||||
{
|
||||
ScoreBoardItem item = _scoreBoard.Find(i => i.Name == name);
|
||||
item.Deaths++;
|
||||
_scoreBoard[_scoreBoard.IndexOf(item)] = item;
|
||||
}
|
||||
|
||||
private void WriteEntities(NetIncomingMessage incomingMessage, NetConnection sender)
|
||||
{
|
||||
int count = incomingMessage.ReadInt32();
|
||||
|
||||
var message = _server.CreateMessage();
|
||||
|
||||
message.Write((int)UdpNetworkPacketType.EntitiesCreated);
|
||||
message.Write(count);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
message.Write(incomingMessage.ReadString());
|
||||
message.Write(incomingMessage.ReadString());
|
||||
message.Write(incomingMessage.ReadVector2());
|
||||
message.Write(incomingMessage.ReadVector2());
|
||||
message.Write(incomingMessage.ReadFloat());
|
||||
}
|
||||
|
||||
_server.SendToAll(message, sender, NetDeliveryMethod.UnreliableSequenced, 1);
|
||||
}
|
||||
|
||||
private void AuthorizeConnection(NetConnection sender)
|
||||
{
|
||||
string name = Encoding.ASCII.GetString(sender.RemoteHailMessage.Data);
|
||||
if (!string.IsNullOrEmpty(name) && !_players.Contains(name))
|
||||
{
|
||||
sender.Approve();
|
||||
}
|
||||
else
|
||||
{
|
||||
sender.Deny("A player with that name is already playing.");
|
||||
}
|
||||
}
|
||||
|
||||
private void WritePlayerJoinedPacket(NetConnection sender, string name)
|
||||
{
|
||||
System.Diagnostics.Trace.WriteLine("In WritePlayerJoined()...");
|
||||
|
||||
var message = _server.CreateMessage();
|
||||
|
||||
message.Write((int)UdpNetworkPacketType.PlayerJoined);
|
||||
|
||||
message.Write(name);
|
||||
message.Write(_server.Connections.IndexOf(sender));
|
||||
message.Write(_defaultPosition); // TODO: positions should be stored on the level somehow
|
||||
message.Write(Vector2.Zero);
|
||||
message.Write(0f);
|
||||
|
||||
_level.BuildCollisionDataFromLevel();
|
||||
|
||||
byte[] collisionData = ObjectToByteArray(_level.CollisionData);
|
||||
message.Write(collisionData.Length);
|
||||
message.Write(collisionData);
|
||||
|
||||
_server.SendMessage(message, sender, NetDeliveryMethod.ReliableUnordered);
|
||||
System.Diagnostics.Trace.WriteLine("Sent joined...");
|
||||
}
|
||||
|
||||
private void WriteEntity(NetIncomingMessage message, NetConnection sender)
|
||||
{
|
||||
string entityType = message.ReadString();
|
||||
string owner = message.ReadString();
|
||||
Vector2 pos = message.ReadVector2();
|
||||
Vector2 velocity = message.ReadVector2();
|
||||
float angle = message.ReadFloat();
|
||||
|
||||
WriteEntityPacket(sender, entityType, owner, ref pos, ref velocity, angle);
|
||||
}
|
||||
|
||||
private void WriteEntityPacket(NetConnection connectionToExclude, string entityType, string owner, ref Vector2 pos, ref Vector2 velocity, float angle)
|
||||
{
|
||||
var message = _server.CreateMessage();
|
||||
|
||||
message.Write((int)UdpNetworkPacketType.EntityCreated);
|
||||
message.Write(entityType);
|
||||
message.Write(owner);
|
||||
message.Write(pos);
|
||||
message.Write(velocity);
|
||||
message.Write(angle);
|
||||
|
||||
_server.SendToAll(message, connectionToExclude, NetDeliveryMethod.UnreliableSequenced, 1);
|
||||
}
|
||||
|
||||
private void WritePlayerUpdate(NetIncomingMessage message, NetConnection sender)
|
||||
{
|
||||
string owner = message.ReadString();
|
||||
int colorIndex = message.ReadInt32();
|
||||
Vector2 pos = message.ReadVector2();
|
||||
Vector2 velocity = message.ReadVector2();
|
||||
float angle = message.ReadFloat();
|
||||
|
||||
WritePlayerUpdatePacket(sender, owner, colorIndex, ref pos, ref velocity, angle);
|
||||
}
|
||||
|
||||
private void WritePlayerUpdatePacket(NetConnection connectionToExclude, string owner, int colorIndex, ref Vector2 pos, ref Vector2 velocity, float angle)
|
||||
{
|
||||
var message = _server.CreateMessage();
|
||||
|
||||
message.Write((int)UdpNetworkPacketType.PlayerUpdate);
|
||||
message.Write(owner);
|
||||
message.Write(colorIndex);
|
||||
message.Write(pos);
|
||||
message.Write(velocity);
|
||||
message.Write(angle);
|
||||
|
||||
_server.SendToAll(message, connectionToExclude, NetDeliveryMethod.UnreliableSequenced, 1);
|
||||
}
|
||||
}
|
||||
}
|
46
SpacePew/ParticleSystem/ExplosionParticleSystem.cs
Normal file
46
SpacePew/ParticleSystem/ExplosionParticleSystem.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using SpacePew.Camera;
|
||||
|
||||
namespace SpacePew.ParticleSystem
|
||||
{
|
||||
public class ExplosionParticleSystem : ParticleSystem
|
||||
{
|
||||
public ExplosionParticleSystem(MainGame game, int howManyEffects, ICamera2D camera) : base(game, howManyEffects, camera) { }
|
||||
|
||||
protected override void InitializeConstants()
|
||||
{
|
||||
_textureFilename = "ParticleTextures\\explosion";
|
||||
|
||||
_minInitialSpeed = 40;
|
||||
_maxInitialSpeed = 500;
|
||||
|
||||
_minAcceleration = 0;
|
||||
_maxAcceleration = 0;
|
||||
|
||||
_minLifetime = .5f;
|
||||
_maxLifetime = 1.0f;
|
||||
|
||||
_minScale = .3f;
|
||||
_maxScale = 1.0f;
|
||||
|
||||
_minNumParticles = 20;
|
||||
_maxNumParticles = 25;
|
||||
|
||||
_minRotationSpeed = -MathHelper.PiOver4;
|
||||
_maxRotationSpeed = MathHelper.PiOver4;
|
||||
|
||||
_blendState = BlendState.Additive;
|
||||
|
||||
DrawOrder = AdditiveDrawOrder;
|
||||
}
|
||||
|
||||
protected override void InitializeParticle(Particle p, Vector2 where)
|
||||
{
|
||||
base.InitializeParticle(p, where);
|
||||
|
||||
p.Acceleration = -p.Velocity / p.Lifetime;
|
||||
}
|
||||
}
|
||||
}
|
41
SpacePew/ParticleSystem/ExplosionSmokeParticleSystem.cs
Normal file
41
SpacePew/ParticleSystem/ExplosionSmokeParticleSystem.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using SpacePew.Camera;
|
||||
|
||||
namespace SpacePew.ParticleSystem
|
||||
{
|
||||
public class ExplosionSmokeParticleSystem : ParticleSystem
|
||||
{
|
||||
public ExplosionSmokeParticleSystem(MainGame game, int howManyEffects, ICamera2D camera)
|
||||
: base(game, howManyEffects, camera)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void InitializeConstants()
|
||||
{
|
||||
_textureFilename = "ParticleTextures\\smoke";
|
||||
|
||||
_minInitialSpeed = 20;
|
||||
_maxInitialSpeed = 200;
|
||||
|
||||
_minAcceleration = -10;
|
||||
_maxAcceleration = -50;
|
||||
|
||||
_minLifetime = 1.0f;
|
||||
_maxLifetime = 2.5f;
|
||||
|
||||
_minScale = 1.0f;
|
||||
_maxScale = 2.0f;
|
||||
|
||||
_minNumParticles = 10;
|
||||
_maxNumParticles = 20;
|
||||
|
||||
_minRotationSpeed = -MathHelper.PiOver4;
|
||||
_maxRotationSpeed = MathHelper.PiOver4;
|
||||
|
||||
_blendState = BlendState.AlphaBlend;
|
||||
|
||||
DrawOrder = AlphaBlendDrawOrder;
|
||||
}
|
||||
}
|
||||
}
|
55
SpacePew/ParticleSystem/Particle.cs
Normal file
55
SpacePew/ParticleSystem/Particle.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace SpacePew.ParticleSystem
|
||||
{
|
||||
public class Particle
|
||||
{
|
||||
private static readonly Random _random = new Random();
|
||||
private float RandomBetween(float min, float max)
|
||||
{
|
||||
return min + (float)_random.NextDouble() * (max - min);
|
||||
}
|
||||
|
||||
public Vector2 Position;
|
||||
public Vector2 Velocity;
|
||||
public Vector2 Acceleration;
|
||||
|
||||
public float Scale { get; set; }
|
||||
|
||||
public float Lifetime { get; private set; }
|
||||
public float TimeSinceStart { get; private set; }
|
||||
public float Rotation { get; private set; }
|
||||
public float RotationSpeed { get; private set; }
|
||||
|
||||
public bool Active
|
||||
{
|
||||
get { return TimeSinceStart < Lifetime; }
|
||||
}
|
||||
|
||||
public void Initialize(Vector2 position, Vector2 velocity, Vector2 acceleration, float lifetime, float scale, float rotationSpeed)
|
||||
{
|
||||
this.Position = position;
|
||||
this.Velocity = velocity;// *this.Scale;
|
||||
this.Acceleration = acceleration;
|
||||
this.Lifetime = lifetime * this.Scale;
|
||||
this.Scale = scale * this.Scale;
|
||||
this.RotationSpeed = rotationSpeed;
|
||||
|
||||
this.TimeSinceStart = 0.0f;
|
||||
|
||||
this.Rotation = RandomBetween(0, MathHelper.TwoPi);
|
||||
}
|
||||
|
||||
public void Update(float dt)
|
||||
{
|
||||
Velocity += Acceleration * dt;
|
||||
Position += Velocity * dt;
|
||||
|
||||
Rotation += RotationSpeed * dt;
|
||||
|
||||
TimeSinceStart += dt;
|
||||
}
|
||||
}
|
||||
}
|
194
SpacePew/ParticleSystem/ParticleSystem.cs
Normal file
194
SpacePew/ParticleSystem/ParticleSystem.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using SpacePew.Camera;
|
||||
|
||||
namespace SpacePew.ParticleSystem
|
||||
{
|
||||
public abstract class ParticleSystem : DrawableGameComponent
|
||||
{
|
||||
private static readonly Random _random = new Random();
|
||||
protected Random Random
|
||||
{
|
||||
get { return _random; }
|
||||
}
|
||||
|
||||
protected float RandomBetween(float min, float max)
|
||||
{
|
||||
return min + (float)_random.NextDouble() * (max - min);
|
||||
}
|
||||
|
||||
public const int AlphaBlendDrawOrder = 100;
|
||||
public const int AdditiveDrawOrder = 200;
|
||||
|
||||
private readonly MainGame _game;
|
||||
private readonly int _howManyEffects;
|
||||
|
||||
private Texture2D _texture;
|
||||
private Vector2 _origin;
|
||||
|
||||
private Particle[] _particles;
|
||||
|
||||
private Queue<Particle> _freeParticles;
|
||||
|
||||
public int FreeParticleCount
|
||||
{
|
||||
get { return _freeParticles.Count; }
|
||||
}
|
||||
|
||||
protected int _minNumParticles;
|
||||
protected int _maxNumParticles;
|
||||
|
||||
protected string _textureFilename;
|
||||
|
||||
protected float _minInitialSpeed;
|
||||
protected float _maxInitialSpeed;
|
||||
|
||||
protected float _minAcceleration;
|
||||
protected float _maxAcceleration;
|
||||
|
||||
protected float _minRotationSpeed;
|
||||
protected float _maxRotationSpeed;
|
||||
|
||||
protected float _minLifetime;
|
||||
protected float _maxLifetime;
|
||||
|
||||
protected float _minScale;
|
||||
protected float _maxScale;
|
||||
|
||||
protected BlendState _blendState;
|
||||
|
||||
protected ICamera2D _camera;
|
||||
|
||||
protected ParticleSystem(MainGame game, int howManyEffects, ICamera2D camera)
|
||||
: base(game)
|
||||
{
|
||||
this._game = game;
|
||||
this._howManyEffects = howManyEffects;
|
||||
this._camera = camera;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
InitializeConstants();
|
||||
|
||||
_particles = new Particle[_howManyEffects * _maxNumParticles];
|
||||
_freeParticles = new Queue<Particle>(_howManyEffects * _maxNumParticles);
|
||||
|
||||
for (int i = 0; i < _particles.Length; i++)
|
||||
{
|
||||
_particles[i] = new Particle();
|
||||
_freeParticles.Enqueue(_particles[i]);
|
||||
}
|
||||
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
protected abstract void InitializeConstants();
|
||||
|
||||
protected override void LoadContent()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_textureFilename))
|
||||
{
|
||||
throw new ArgumentNullException("textureFilename");
|
||||
}
|
||||
|
||||
_texture = _game.Content.Load<Texture2D>(_textureFilename);
|
||||
|
||||
_origin.X = _texture.Width / 2;
|
||||
_origin.Y = _texture.Height / 2;
|
||||
|
||||
base.LoadContent();
|
||||
}
|
||||
|
||||
public void AddParticles(Vector2 where, float scale)
|
||||
{
|
||||
int numParticles = Random.Next(_minNumParticles, _maxNumParticles);
|
||||
|
||||
for (int i = 0; i < numParticles && _freeParticles.Count > 0; i++)
|
||||
{
|
||||
Particle p = _freeParticles.Dequeue();
|
||||
p.Scale = scale;
|
||||
InitializeParticle(p, where);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void InitializeParticle(Particle p, Vector2 where)
|
||||
{
|
||||
Vector2 direction = PickRandomDirection();
|
||||
|
||||
float velocity =
|
||||
RandomBetween(_minInitialSpeed, _maxInitialSpeed);
|
||||
float acceleration =
|
||||
RandomBetween(_minAcceleration, _maxAcceleration);
|
||||
float lifetime =
|
||||
RandomBetween(_minLifetime, _maxLifetime);
|
||||
float scale =
|
||||
RandomBetween(_minScale, _maxScale);
|
||||
float rotationSpeed =
|
||||
RandomBetween(_minRotationSpeed, _maxRotationSpeed);
|
||||
|
||||
p.Initialize(
|
||||
where, velocity * direction, acceleration * direction,
|
||||
lifetime, scale, rotationSpeed);
|
||||
}
|
||||
|
||||
protected virtual Vector2 PickRandomDirection()
|
||||
{
|
||||
float angle = RandomBetween(0, MathHelper.TwoPi);
|
||||
return new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
|
||||
}
|
||||
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
var dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
|
||||
|
||||
foreach (var p in _particles.Where(p => p.Active))
|
||||
{
|
||||
p.Update(dt);
|
||||
|
||||
if (!p.Active)
|
||||
{
|
||||
_freeParticles.Enqueue(p);
|
||||
}
|
||||
}
|
||||
|
||||
base.Update(gameTime);
|
||||
}
|
||||
|
||||
public override void Draw(GameTime gameTime)
|
||||
{
|
||||
_game.SpriteBatch.Begin(SpriteSortMode.Deferred, _blendState, null, null, null, null, _camera.Transform);
|
||||
|
||||
foreach (Particle p in _particles)
|
||||
{
|
||||
if (!p.Active)
|
||||
continue;
|
||||
|
||||
float normalizedLifetime = p.TimeSinceStart / p.Lifetime;
|
||||
|
||||
float alpha = 4 * normalizedLifetime * (1 - normalizedLifetime);
|
||||
var color = new Color(new Vector4(1, 1, 1, alpha));
|
||||
|
||||
float scale = p.Scale * (.75f + .25f * normalizedLifetime);
|
||||
|
||||
_game.SpriteBatch.Draw(_texture,
|
||||
p.Position,
|
||||
null,
|
||||
color,
|
||||
p.Rotation,
|
||||
_origin,
|
||||
scale,
|
||||
SpriteEffects.None,
|
||||
0.0f);
|
||||
}
|
||||
|
||||
_game.SpriteBatch.End();
|
||||
|
||||
base.Draw(gameTime);
|
||||
}
|
||||
}
|
||||
}
|
61
SpacePew/ParticleSystem/SmokePlumeParticleSystem.cs
Normal file
61
SpacePew/ParticleSystem/SmokePlumeParticleSystem.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using SpacePew.Camera;
|
||||
|
||||
namespace SpacePew.ParticleSystem
|
||||
{
|
||||
public class SmokePlumeParticleSystem : ParticleSystem
|
||||
{
|
||||
public SmokePlumeParticleSystem(MainGame game, int howManyEffects, ICamera2D camera)
|
||||
: base(game, howManyEffects, camera)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void InitializeConstants()
|
||||
{
|
||||
_textureFilename = "ParticleTextures\\smoke";
|
||||
|
||||
_minInitialSpeed = 20;
|
||||
_maxInitialSpeed = 100;
|
||||
|
||||
_minAcceleration = 0;
|
||||
_maxAcceleration = 0;
|
||||
|
||||
_minLifetime = 5.0f;
|
||||
_maxLifetime = 7.0f;
|
||||
|
||||
_minScale = .5f;
|
||||
_maxScale = 1.0f;
|
||||
|
||||
_minNumParticles = 7;
|
||||
_maxNumParticles = 15;
|
||||
|
||||
_minRotationSpeed = -MathHelper.PiOver4 / 2.0f;
|
||||
_maxRotationSpeed = MathHelper.PiOver4 / 2.0f;
|
||||
|
||||
_blendState = BlendState.AlphaBlend;
|
||||
|
||||
DrawOrder = AlphaBlendDrawOrder;
|
||||
}
|
||||
|
||||
protected override Vector2 PickRandomDirection()
|
||||
{
|
||||
float radians = RandomBetween(
|
||||
MathHelper.ToRadians(80), MathHelper.ToRadians(100));
|
||||
|
||||
Vector2 direction = Vector2.Zero;
|
||||
|
||||
direction.X = (float)Math.Cos(radians);
|
||||
direction.Y = -(float)Math.Sin(radians);
|
||||
return direction;
|
||||
}
|
||||
|
||||
protected override void InitializeParticle(Particle p, Vector2 where)
|
||||
{
|
||||
base.InitializeParticle(p, where);
|
||||
|
||||
p.Acceleration.X += RandomBetween(10, 50);
|
||||
}
|
||||
}
|
||||
}
|
26
SpacePew/Program.cs
Normal file
26
SpacePew/Program.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
#region Using Statements
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#endregion
|
||||
|
||||
namespace SpacePew
|
||||
{
|
||||
#if WINDOWS || LINUX
|
||||
/// <summary>
|
||||
/// The main class.
|
||||
/// </summary>
|
||||
public static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
using (var game = new MainGame())
|
||||
game.Run();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
33
SpacePew/Properties/AssemblyInfo.cs
Normal file
33
SpacePew/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("SpacePew")]
|
||||
[assembly: AssemblyProduct("SpacePew")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
|
||||
[assembly: AssemblyCopyright("Copyright © 2009")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("1588cda1-b2dd-4e65-bd68-1dcdcc35901d")]
|
||||
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
41
SpacePew/SoundManager.cs
Normal file
41
SpacePew/SoundManager.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Xna.Framework.Audio;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace SpacePew
|
||||
{
|
||||
public static class SoundManager
|
||||
{
|
||||
private static MainGame _game;
|
||||
|
||||
public static void Initialize(MainGame game)
|
||||
{
|
||||
_game = game;
|
||||
}
|
||||
|
||||
public static SoundEffectInstance GetSoundEffectInstance(string assetName)
|
||||
{
|
||||
var soundEffect = _game.Content.Load<SoundEffect>(assetName);
|
||||
var soundEffectInstance = soundEffect.CreateInstance();
|
||||
|
||||
return soundEffectInstance;
|
||||
}
|
||||
|
||||
private static int _playCalled = 0;
|
||||
public static void Play(string assetName, Vector2 position)
|
||||
{
|
||||
Debug.Print("Play called: " + ++_playCalled);
|
||||
|
||||
var soundEffect = _game.Content.Load<SoundEffect>(assetName);
|
||||
var soundEffectInstance = soundEffect.CreateInstance();
|
||||
var emitter = new AudioEmitter();
|
||||
var listener = new AudioListener();
|
||||
emitter.Position = new Vector3(position, 0);
|
||||
listener.Position = new Vector3(_game.NetworkClient.LocalPlayer.Position, 0);
|
||||
|
||||
soundEffectInstance.Apply3D(listener, emitter);
|
||||
soundEffectInstance.Play();
|
||||
}
|
||||
}
|
||||
}
|
236
SpacePew/SpacePew.csproj
Normal file
236
SpacePew/SpacePew.csproj
Normal file
@@ -0,0 +1,236 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{1C82F1B3-6F3C-47EC-901A-E656D037F862}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SpacePew</RootNamespace>
|
||||
<AssemblyName>SpacePew</AssemblyName>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile>
|
||||
</TargetFrameworkProfile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Windows\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;WINDOWS</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Windows\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;WINDOWS</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>Icon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Arena.cs" />
|
||||
<Compile Include="Camera\Camera2D.cs" />
|
||||
<Compile Include="Camera\ICamera2D.cs" />
|
||||
<Compile Include="Camera\IFocusable.cs" />
|
||||
<Compile Include="EntityFactory.cs" />
|
||||
<Compile Include="Extensions\LidgrenExtensions.cs" />
|
||||
<Compile Include="Extensions\RenderExtensions.cs" />
|
||||
<Compile Include="Hud.cs" />
|
||||
<Compile Include="KeyboardHelper.cs" />
|
||||
<Compile Include="LevelLoader.cs" />
|
||||
<Compile Include="MainGame.cs" />
|
||||
<Compile Include="Minimap.cs" />
|
||||
<Compile Include="Models\EntityBase.cs" />
|
||||
<Compile Include="Models\Explosion.cs" />
|
||||
<Compile Include="Models\IEntity.cs" />
|
||||
<Compile Include="Models\IKillable.cs" />
|
||||
<Compile Include="Models\Level.cs" />
|
||||
<Compile Include="Models\MapHit.cs" />
|
||||
<Compile Include="Models\Player.cs" />
|
||||
<Compile Include="Models\Projectiles\BouncingBullet.cs" />
|
||||
<Compile Include="Models\Projectiles\Bullet.cs" />
|
||||
<Compile Include="Models\Projectiles\ClusterBomb.cs" />
|
||||
<Compile Include="Models\Projectiles\CollisionType.cs" />
|
||||
<Compile Include="Models\Projectiles\HomingBullet.cs" />
|
||||
<Compile Include="Models\Projectiles\IProjectile.cs" />
|
||||
<Compile Include="Models\Projectiles\LongShot.cs" />
|
||||
<Compile Include="Models\Projectiles\Missile.cs" />
|
||||
<Compile Include="Models\Projectiles\ProjectileBase.cs" />
|
||||
<Compile Include="Models\Weapons\Cannon.cs" />
|
||||
<Compile Include="Models\Weapons\ClusterLauncher.cs" />
|
||||
<Compile Include="Models\Weapons\IWeapon.cs" />
|
||||
<Compile Include="Models\Weapons\Launcher.cs" />
|
||||
<Compile Include="Models\Weapons\SecondaryCannon.cs" />
|
||||
<Compile Include="Models\Weapons\TriCannon.cs" />
|
||||
<Compile Include="Models\Weapons\WeaponBase.cs" />
|
||||
<Compile Include="Networking\NetworkMessage.cs" />
|
||||
<Compile Include="Networking\NetworkMessenger.cs" />
|
||||
<Compile Include="Networking\ScoreBoard.cs" />
|
||||
<Compile Include="Networking\ScoreBoardItem.cs" />
|
||||
<Compile Include="Networking\StreamingClient.cs" />
|
||||
<Compile Include="Networking\UdpBase.cs" />
|
||||
<Compile Include="Networking\UdpClient.cs" />
|
||||
<Compile Include="Networking\UdpNetworkGui.cs" />
|
||||
<Compile Include="Networking\UdpServer.cs" />
|
||||
<Compile Include="ParticleSystem\ExplosionParticleSystem.cs" />
|
||||
<Compile Include="ParticleSystem\ExplosionSmokeParticleSystem.cs" />
|
||||
<Compile Include="ParticleSystem\Particle.cs" />
|
||||
<Compile Include="ParticleSystem\ParticleSystem.cs" />
|
||||
<Compile Include="ParticleSystem\SmokePlumeParticleSystem.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SoundManager.cs" />
|
||||
<Compile Include="TextureManager.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="ICSharpCode.SharpZipLib, Version=0.85.5.452, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\Library\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MonoGame.Framework, Version=3.1.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>C:\Program Files (x86)\MonoGame\v3.0\Assemblies\Windows\MonoGame.Framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NAudio">
|
||||
<HintPath>..\Library\NAudio.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NVorbis">
|
||||
<HintPath>..\Library\NVorbis.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NVorbis.NAudioSupport">
|
||||
<HintPath>..\Library\NVorbis.NAudioSupport.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SharpDX, Version=2.5.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>C:\Program Files (x86)\MonoGame\v3.0\Assemblies\Windows\SharpDX.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SharpDX.Direct3D11, Version=2.5.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>C:\Program Files (x86)\MonoGame\v3.0\Assemblies\Windows\SharpDX.Direct3D11.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SharpDX.MediaFoundation, Version=2.5.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>C:\Program Files (x86)\MonoGame\v3.0\Assemblies\Windows\SharpDX.MediaFoundation.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SharpDX.XAudio2, Version=2.5.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>C:\Program Files (x86)\MonoGame\v3.0\Assemblies\Windows\SharpDX.XAudio2.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="C:\Program Files %28x86%29\MonoGame\v3.0\Assemblies\WindowsGL\SDL.dll">
|
||||
<Link>SDL.dll</Link>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Downloads\Levels\dummy.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Game.ico" />
|
||||
<Content Include="GameThumbnail.png" />
|
||||
<Content Include="Icon.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Lidgren.Network\Lidgren.Network.csproj">
|
||||
<Project>{49ba1c69-6104-41ac-a5d8-b54fa9f696e8}</Project>
|
||||
<Name>Lidgren.Network</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Neoforce\TomShane.Neoforce.Controls.csproj">
|
||||
<Project>{ac5f1cd8-aa8e-4db5-814f-86c214175841}</Project>
|
||||
<Name>TomShane.Neoforce.Controls</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SpacePew.Common\SpacePew.Common.csproj">
|
||||
<Project>{ba98d4ca-718b-4e50-ad4d-f48e8ca67624}</Project>
|
||||
<Name>SpacePew.Common</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SpacePew.Content\SpacePew.Content\SpacePew.Content.csproj">
|
||||
<Project>{944baed2-53a4-47e9-ae89-7f6c5843de94}</Project>
|
||||
<Name>SpacePew.Content</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.0">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4 %28x86 and x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Windows.Installer.4.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Windows Installer 4.5</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="Content\Skins\Blue.skin">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Content\Skins\Default.skin">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Content\Skins\Green.skin">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Content\Skins\Magenta.skin">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Content\Skins\Purple.skin">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Levels\hippie.zip">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
29
SpacePew/TextureManager.cs
Normal file
29
SpacePew/TextureManager.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace SpacePew
|
||||
{
|
||||
public static class TextureManager
|
||||
{
|
||||
private static MainGame _game;
|
||||
|
||||
static readonly Dictionary<string, Texture2D> Textures = new Dictionary<string, Texture2D>();
|
||||
|
||||
public static void Initialize(MainGame game)
|
||||
{
|
||||
_game = game;
|
||||
}
|
||||
|
||||
public static Texture2D LoadTexture(string assetName)
|
||||
{
|
||||
if (!Textures.ContainsKey(assetName))
|
||||
{
|
||||
var texture = _game.Content.Load<Texture2D>(assetName);
|
||||
Textures.Add(assetName, texture);
|
||||
}
|
||||
|
||||
return Textures[assetName];
|
||||
}
|
||||
}
|
||||
}
|
3
SpacePew/app.config
Normal file
3
SpacePew/app.config
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/></startup></configuration>
|
Reference in New Issue
Block a user