Reorganized project to start work on dedicated server

This commit is contained in:
juanjp600
2017-06-11 03:48:08 -03:00
parent 9b0d7c1020
commit 5636d1cdf9
702 changed files with 453 additions and 247 deletions
+265
View File
@@ -0,0 +1,265 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using System.Linq;
namespace Barotrauma
{
public class Camera
{
public static bool FollowSub = true;
const float DefaultZoom = 1.0f;
const float ZoomSmoothness = 8.0f;
const float MoveSmoothness = 8.0f;
private float zoom;
private float offsetAmount;
private Matrix transform, shaderTransform, viewMatrix;
private Vector2 position;
private float rotation;
private Vector2 prevPosition;
private float prevZoom;
public float Shake;
private Vector2 shakePosition;
private Vector2 shakeTargetPosition;
//the area of the world inside the camera view
private Rectangle worldView;
private Point resolution;
private Vector2 targetPos;
public float Zoom
{
get { return zoom; }
set
{
zoom = Math.Max(value, GameMain.DebugDraw ? 0.01f : 0.1f);
Vector2 center = WorldViewCenter;
float newWidth = resolution.X / zoom;
float newHeight = resolution.Y / zoom;
worldView = new Rectangle(
(int)(center.X - newWidth / 2.0f),
(int)(center.Y + newHeight / 2.0f),
(int)newWidth,
(int)newHeight);
//UpdateTransform();
}
}
public float Rotation
{
get { return rotation; }
set { rotation = value; }
}
public float OffsetAmount
{
get { return offsetAmount; }
set { offsetAmount = value; }
}
public Point Resolution
{
get { return resolution; }
}
public Rectangle WorldView
{
get { return worldView; }
}
public Vector2 WorldViewCenter
{
get
{
return new Vector2(
worldView.X + worldView.Width / 2.0f,
worldView.Y - worldView.Height / 2.0f);
}
}
public Matrix Transform
{
get { return transform; }
}
public Matrix ShaderTransform
{
get { return shaderTransform; }
}
public Camera()
{
zoom = 1.0f;
rotation = 0.0f;
position = Vector2.Zero;
worldView = new Rectangle(0,0,
GameMain.GraphicsWidth,
GameMain.GraphicsHeight);
resolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
viewMatrix =
Matrix.CreateTranslation(new Vector3(GameMain.GraphicsWidth / 2.0f, GameMain.GraphicsHeight / 2.0f, 0));
UpdateTransform();
}
public Vector2 TargetPos
{
get { return targetPos; }
set { targetPos = value; }
}
// Auxiliary function to move the camera
public void Translate(Vector2 amount)
{
position += amount;
}
public void UpdateTransform(bool interpolate = true, bool clampPos = false)
{
Vector2 interpolatedPosition = interpolate ? Timing.Interpolate(prevPosition, position) : position;
float interpolatedZoom = interpolate ? Timing.Interpolate(prevZoom, zoom) : zoom;
worldView.X = (int)(interpolatedPosition.X - worldView.Width / 2.0);
worldView.Y = (int)(interpolatedPosition.Y + worldView.Height / 2.0);
if (Level.Loaded != null && clampPos)
{
position.Y -= Math.Max(worldView.Y - Level.Loaded.Size.Y, 0.0f);
interpolatedPosition.Y -= Math.Max(worldView.Y - Level.Loaded.Size.Y, 0.0f);
worldView.Y = Math.Min((int)Level.Loaded.Size.Y, worldView.Y);
}
transform = Matrix.CreateTranslation(
new Vector3(-interpolatedPosition.X, interpolatedPosition.Y, 0)) *
Matrix.CreateScale(new Vector3(interpolatedZoom, interpolatedZoom, 1)) *
viewMatrix;
shaderTransform = Matrix.CreateTranslation(
new Vector3(
-interpolatedPosition.X - resolution.X / interpolatedZoom / 2.0f,
-interpolatedPosition.Y - resolution.Y / interpolatedZoom / 2.0f, 0)) *
Matrix.CreateScale(new Vector3(interpolatedZoom, interpolatedZoom, 1)) *
viewMatrix;
Sound.CameraPos = new Vector3(WorldViewCenter.X, WorldViewCenter.Y, 0.0f);
if (!interpolate)
{
prevPosition = position;
prevZoom = zoom;
}
}
public void MoveCamera(float deltaTime, bool allowMove = true, bool allowZoom = true)
{
prevPosition = position;
prevZoom = zoom;
float moveSpeed = 20.0f/zoom;
Vector2 moveCam = Vector2.Zero;
if (targetPos == Vector2.Zero)
{
if (allowMove && GUIComponent.KeyboardDispatcher.Subscriber == null)
{
if (PlayerInput.KeyDown(Keys.LeftShift)) moveSpeed *= 2.0f;
if (PlayerInput.KeyDown(Keys.LeftControl)) moveSpeed *= 0.5f;
if (GameMain.Config.KeyBind(InputType.Left).IsDown()) moveCam.X -= moveSpeed;
if (GameMain.Config.KeyBind(InputType.Right).IsDown()) moveCam.X += moveSpeed;
if (GameMain.Config.KeyBind(InputType.Down).IsDown()) moveCam.Y -= moveSpeed;
if (GameMain.Config.KeyBind(InputType.Up).IsDown()) moveCam.Y += moveSpeed;
}
if (Screen.Selected == GameMain.GameScreen && FollowSub)
{
var closestSub = Submarine.FindClosest(WorldViewCenter);
if (closestSub != null)
{
moveCam += FarseerPhysics.ConvertUnits.ToDisplayUnits(closestSub.Velocity * deltaTime);
}
}
moveCam = moveCam * deltaTime * 60.0f;
if (allowZoom)
{
Vector2 mouseInWorld = ScreenToWorld(PlayerInput.MousePosition);
Vector2 diffViewCenter;
diffViewCenter = ((mouseInWorld - Position) * Zoom);
Zoom = MathHelper.Clamp(zoom + (PlayerInput.ScrollWheelSpeed / 1000.0f) * zoom, GameMain.DebugDraw ? 0.01f : 0.1f, 2.0f);
if (!PlayerInput.KeyDown(Keys.F)) Position = mouseInWorld - (diffViewCenter / Zoom);
}
}
else
{
Vector2 mousePos = PlayerInput.MousePosition;
Vector2 offset = mousePos - new Vector2(resolution.X / 2.0f, resolution.Y / 2.0f);
offset.X = offset.X / (resolution.X * 0.4f);
offset.Y = -offset.Y / (resolution.Y * 0.3f);
if (offset.Length() > 1.0f) offset.Normalize();
offset = offset * offsetAmount;
float newZoom = Math.Min(DefaultZoom - Math.Min(offset.Length() / resolution.Y, 1.0f),1.0f);
Zoom += (newZoom - zoom) / ZoomSmoothness;
Vector2 diff = (targetPos + offset) - position;
moveCam = diff / MoveSmoothness;
}
shakeTargetPosition = Rand.Vector(Shake);
shakePosition = Vector2.Lerp(shakePosition, shakeTargetPosition, 0.5f);
Shake = MathHelper.Lerp(Shake, 0.0f, deltaTime * 2.0f);
Translate(moveCam + shakePosition);
}
public Vector2 Position
{
get { return position; }
set
{
if (!MathUtils.IsValid(value))
{
return;
}
position = value;
}
}
public Vector2 ScreenToWorld(Vector2 coords)
{
Vector2 worldCoords = Vector2.Transform(coords, Matrix.Invert(transform));
return new Vector2(worldCoords.X, -worldCoords.Y);
}
public Vector2 WorldToScreen(Vector2 coords)
{
coords.Y = -coords.Y;
//Vector2 screenCoords = Vector2.Transform(coords, transform);
return Vector2.Transform(coords, transform);
}
}
}
@@ -0,0 +1,232 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace EventInput
{
#if WINDOWS
public class KeyboardLayout
{
const uint KLF_ACTIVATE = 1; //activate the layout
const int KL_NAMELENGTH = 9; // length of the keyboard buffer
const string LANG_EN_US = "00000409";
const string LANG_HE_IL = "0001101A";
[DllImport("user32.dll")]
private static extern long LoadKeyboardLayout(
string pwszKLID, // input locale identifier
uint Flags // input locale identifier options
);
[DllImport("user32.dll")]
private static extern long GetKeyboardLayoutName(
StringBuilder pwszKLID //[out] string that receives the name of the locale identifier
);
public static string getName()
{
StringBuilder name = new StringBuilder(KL_NAMELENGTH);
GetKeyboardLayoutName(name);
return name.ToString();
}
}
#endif
public class CharacterEventArgs : EventArgs
{
private readonly char character;
private readonly int lParam;
public CharacterEventArgs(char character, int lParam)
{
this.character = character;
this.lParam = lParam;
}
public char Character
{
get { return character; }
}
public int Param
{
get { return lParam; }
}
public int RepeatCount
{
get { return lParam & 0xffff; }
}
public bool ExtendedKey
{
get { return (lParam & (1 << 24)) > 0; }
}
public bool AltPressed
{
get { return (lParam & (1 << 29)) > 0; }
}
public bool PreviousState
{
get { return (lParam & (1 << 30)) > 0; }
}
public bool TransitionState
{
get { return (lParam & (1 << 31)) > 0; }
}
}
public class KeyEventArgs : EventArgs
{
private Keys keyCode;
public KeyEventArgs(Keys keyCode)
{
this.keyCode = keyCode;
}
public Keys KeyCode
{
get { return keyCode; }
}
}
public delegate void CharEnteredHandler(object sender, CharacterEventArgs e);
public delegate void KeyEventHandler(object sender, KeyEventArgs e);
public static class EventInput
{
/// <summary>
/// Event raised when a Character has been entered.
/// </summary>
public static event CharEnteredHandler CharEntered;
/// <summary>
/// Event raised when a key has been pressed down. May fire multiple times due to keyboard repeat.
/// </summary>
public static event KeyEventHandler KeyDown;
/// <summary>
/// Event raised when a key has been released.
/// </summary>
public static event KeyEventHandler KeyUp;
static bool initialized;
#if WINDOWS
delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
static IntPtr prevWndProc;
static WndProc hookProcDelegate;
static IntPtr hIMC;
//various Win32 constants that we need
const int GWL_WNDPROC = -4;
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_CHAR = 0x102;
const int WM_IME_SETCONTEXT = 0x0281;
const int WM_INPUTLANGCHANGE = 0x51;
const int WM_GETDLGCODE = 0x87;
const int WM_IME_COMPOSITION = 0x10f;
const int DLGC_WANTALLKEYS = 4;
//Win32 functions that we're using
[DllImport("Imm32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr ImmGetContext(IntPtr hWnd);
[DllImport("Imm32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
#endif
/// <summary>
/// Initialize the TextInput with the given GameWindow.
/// </summary>
/// <param name="window">The XNA window to which text input should be linked.</param>
public static void Initialize(GameWindow window)
{
if (initialized)
{
return;
}
#if WINDOWS
hookProcDelegate = HookProc;
prevWndProc = (IntPtr)SetWindowLong(window.Handle, GWL_WNDPROC,
(int)Marshal.GetFunctionPointerForDelegate(hookProcDelegate));
hIMC = ImmGetContext(window.Handle);
#else
window.TextInput += ReceiveInput;
#endif
initialized = true;
}
private static void ReceiveInput(object sender, TextInputEventArgs e)
{
OnCharEntered(e.Character);
}
public static void OnCharEntered(char character)
{
if (CharEntered != null) CharEntered(null, new CharacterEventArgs(character, 0));
}
#if WINDOWS
static IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
IntPtr returnCode = CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam);
switch (msg)
{
case WM_GETDLGCODE:
returnCode = (IntPtr)(returnCode.ToInt32() | DLGC_WANTALLKEYS);
break;
case WM_KEYDOWN:
if (KeyDown != null)
KeyDown(null, new KeyEventArgs((Keys)wParam));
break;
case WM_KEYUP:
if (KeyUp != null)
KeyUp(null, new KeyEventArgs((Keys)wParam));
break;
case WM_CHAR:
if (CharEntered != null)
CharEntered(null, new CharacterEventArgs((char)wParam, lParam.ToInt32()));
break;
case WM_IME_SETCONTEXT:
if (wParam.ToInt32() == 1)
ImmAssociateContext(hWnd, hIMC);
break;
case WM_INPUTLANGCHANGE:
ImmAssociateContext(hWnd, hIMC);
returnCode = (IntPtr)1;
break;
}
return returnCode;
}
#endif
}
}
@@ -0,0 +1,92 @@
using System;
using System.Threading;
#if WINDOWS
using System.Windows;
#endif
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace EventInput
{
public interface IKeyboardSubscriber
{
void ReceiveTextInput(char inputChar);
void ReceiveTextInput(string text);
void ReceiveCommandInput(char command);
void ReceiveSpecialInput(Keys key);
bool Selected { get; set; } //or Focused
}
public class KeyboardDispatcher
{
public KeyboardDispatcher(GameWindow window)
{
EventInput.Initialize(window);
EventInput.CharEntered += EventInput_CharEntered;
EventInput.KeyDown += EventInput_KeyDown;
}
void EventInput_KeyDown(object sender, KeyEventArgs e)
{
if (_subscriber == null)
return;
_subscriber.ReceiveSpecialInput(e.KeyCode);
}
void EventInput_CharEntered(object sender, CharacterEventArgs e)
{
if (_subscriber == null)
return;
if (char.IsControl(e.Character))
{
//ctrl-v
if (e.Character == 0x16)
{
#if WINDOWS
//XNA runs in Multiple Thread Apartment state, which cannot recieve clipboard
Thread thread = new Thread(PasteThread);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
_subscriber.ReceiveTextInput(_pasteResult);
#endif
}
else
{
_subscriber.ReceiveCommandInput(e.Character);
}
}
else
{
_subscriber.ReceiveTextInput(e.Character);
}
}
IKeyboardSubscriber _subscriber;
public IKeyboardSubscriber Subscriber
{
get { return _subscriber; }
set
{
if (_subscriber != null)
_subscriber.Selected = false;
_subscriber = value;
if (value != null)
value.Selected = true;
}
}
#if WINDOWS
//Thread has to be in Single Thread Apartment state in order to receive clipboard
string _pasteResult = "";
[STAThread]
void PasteThread()
{
_pasteResult = Clipboard.ContainsText() ? Clipboard.GetText() : "";
}
#endif
}
}
@@ -0,0 +1,287 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpFont;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
{
public class ScalableFont
{
private static List<ScalableFont> FontList = new List<ScalableFont>();
private static Library Lib = null;
private string filename;
private Face face;
private uint size;
private int baseHeight;
//private int lineHeight;
private Dictionary<uint, GlyphData> texCoords;
private List<Texture2D> textures;
private GraphicsDevice graphicsDevice;
public uint Size
{
get
{
return size;
}
set
{
size = value;
if (graphicsDevice!=null) RenderAtlas(graphicsDevice, charRanges, texDims, baseChar);
}
}
private uint[] charRanges;
private int texDims;
private uint baseChar;
private struct GlyphData
{
public int texIndex;
public Vector2 drawOffset;
public float advance;
public Rectangle texCoords;
}
public ScalableFont(string filename, uint size, GraphicsDevice gd=null)
{
if (Lib == null) Lib = new Library();
this.filename = filename;
this.face = null;
foreach (ScalableFont font in FontList)
{
if (font.filename == filename)
{
this.face = font.face;
break;
}
}
if (this.face == null)
{
this.face = new Face(Lib, filename);
}
this.size = size;
this.textures = new List<Texture2D>();
this.texCoords = new Dictionary<uint, GlyphData>();
if (gd != null) RenderAtlas(gd);
FontList.Add(this);
}
/// <summary>
/// Renders the font into at least one texture atlas, which is simply a collection of all glyphs in the ranges defined by charRanges.
/// Don't call this too often or with very large sizes.
/// </summary>
/// <param name="gd">Graphics device, required to create textures.</param>
/// <param name="charRanges">Character ranges between each even element with their corresponding odd element. Default is 0x20 to 0xFFFF.</param>
/// <param name="texDims">Texture dimensions. Default is 512x512.</param>
/// <param name="baseChar">Base character used to shift all other characters downwards when rendering. Defaults to T.</param>
public void RenderAtlas(GraphicsDevice gd, uint[] charRanges = null, int texDims = 512, uint baseChar = 0x54)
{
if (charRanges == null)
{
charRanges = new uint[] { 0x20, 0xFFFF };
}
this.charRanges = charRanges;
this.texDims = texDims;
this.baseChar = baseChar;
face.SetPixelSizes(0, size);
textures.ForEach(t => t.Dispose());
textures.Clear();
texCoords.Clear();
uint[] pixelBuffer = new uint[texDims * texDims];
for (int i = 0; i < texDims * texDims; i++)
{
pixelBuffer[i] = 0;
}
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
int texIndex = 0;
Vector2 currentCoords = Vector2.Zero;
int nextY = 0;
face.LoadGlyph(face.GetCharIndex(baseChar), LoadFlags.Default, LoadTarget.Normal);
baseHeight = face.Glyph.Metrics.Height.ToInt32();
//lineHeight = baseHeight;
for (int i = 0; i < charRanges.Length; i += 2)
{
uint start = charRanges[i];
uint end = charRanges[i + 1];
for (uint j = start; j <= end; j++)
{
uint glyphIndex = face.GetCharIndex(j);
if (glyphIndex == 0) continue;
face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
if (face.Glyph.Metrics.Width == 0 || face.Glyph.Metrics.Height == 0)
{
if (face.Glyph.Metrics.HorizontalAdvance > 0)
{
//glyph is empty, but char still applies advance
GlyphData blankData = new GlyphData();
blankData.advance = (float)face.Glyph.Metrics.HorizontalAdvance;
blankData.texIndex = -1; //indicates no texture because the glyph is empty
texCoords.Add(j, blankData);
}
continue;
}
//stacktrace doesn't really work that well when RenderGlyph throws an exception
face.Glyph.RenderGlyph(RenderMode.Normal);
byte[] bitmap = face.Glyph.Bitmap.BufferData;
int glyphWidth = face.Glyph.Bitmap.Width;
int glyphHeight = bitmap.Length / glyphWidth;
//if (glyphHeight>lineHeight) lineHeight=glyphHeight;
if (glyphWidth > texDims - 1 || glyphHeight > texDims - 1)
{
throw new Exception(filename + ", " + size.ToString() + ", "+ (char)j + "; Glyph dimensions exceed texture atlas dimensions");
}
nextY = Math.Max(nextY, glyphHeight + 2);
if (currentCoords.X + glyphWidth + 2 > texDims - 1)
{
currentCoords.X = 0;
currentCoords.Y += nextY;
nextY = 0;
}
if (currentCoords.Y + glyphHeight + 2 > texDims - 1)
{
currentCoords.X = 0;
currentCoords.Y = 0;
textures[texIndex].SetData<uint>(pixelBuffer);
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
texIndex++;
for (int k = 0; k < texDims * texDims; k++)
{
pixelBuffer[k] = 0;
}
}
GlyphData newData = new GlyphData();
newData.advance = (float)face.Glyph.Metrics.HorizontalAdvance;
newData.texIndex = texIndex;
newData.texCoords = new Rectangle((int)currentCoords.X, (int)currentCoords.Y, glyphWidth, glyphHeight);
newData.drawOffset = new Vector2(face.Glyph.BitmapLeft, baseHeight*14/10 - face.Glyph.BitmapTop);
texCoords.Add(j, newData);
for (int y = 0; y < glyphHeight; y++)
{
for (int x = 0; x < glyphWidth; x++)
{
byte byteColor = bitmap[x + y * glyphWidth];
pixelBuffer[((int)currentCoords.X + x) + ((int)currentCoords.Y + y) * texDims] = (uint)(byteColor << 24 | byteColor << 16 | byteColor << 8 | byteColor);
}
}
currentCoords.X += glyphWidth + 2;
}
textures[texIndex].SetData<uint>(pixelBuffer);
}
graphicsDevice = gd;
}
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth)
{
if (textures.Count == 0) return;
int lineNum = 0;
Vector2 currentPos = position;
Vector2 advanceUnit = new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation));
for (int i = 0; i < text.Length; i++)
{
if (text[i]=='\n')
{
lineNum++;
currentPos = position;
currentPos.X -= baseHeight * 1.8f * lineNum * advanceUnit.Y * scale.Y;
currentPos.Y += baseHeight * 1.8f * lineNum * advanceUnit.X * scale.Y;
continue;
}
uint charIndex = text[i];
GlyphData gd;
if (texCoords.TryGetValue(charIndex, out gd))
{
if (gd.texIndex >= 0)
{
Texture2D tex = textures[gd.texIndex];
Vector2 drawOffset;
drawOffset.X = gd.drawOffset.X * advanceUnit.X * scale.X - gd.drawOffset.Y * advanceUnit.Y * scale.Y;
drawOffset.Y = gd.drawOffset.X * advanceUnit.Y * scale.Y + gd.drawOffset.Y * advanceUnit.X * scale.X;
sb.Draw(tex, currentPos + drawOffset, null, gd.texCoords, origin, rotation, scale, color, se, layerDepth);
}
currentPos += gd.advance * advanceUnit * scale.X;
}
}
}
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth)
{
DrawString(sb, text, position, color, rotation, origin, new Vector2(scale), se, layerDepth);
}
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color)
{
if (textures.Count == 0) return;
Vector2 currentPos = position;
for (int i = 0; i < text.Length; i++)
{
if (text[i] == '\n')
{
currentPos.X = position.X;
currentPos.Y += baseHeight * 1.8f;
continue;
}
uint charIndex = text[i];
GlyphData gd;
if (texCoords.TryGetValue(charIndex, out gd))
{
if (gd.texIndex >= 0)
{
Texture2D tex = textures[gd.texIndex];
sb.Draw(tex, currentPos + gd.drawOffset, gd.texCoords, color);
}
currentPos.X += gd.advance;
}
}
}
public Vector2 MeasureString(string text)
{
float currentLineX = 0.0f;
Vector2 retVal = Vector2.Zero;
retVal.Y = baseHeight*1.8f;
for (int i = 0; i < text.Length; i++)
{
if (text[i] == '\n')
{
currentLineX = 0.0f;
retVal.Y += baseHeight*18/10;
continue;
}
uint charIndex = text[i];
GlyphData gd;
if (texCoords.TryGetValue(charIndex, out gd))
{
currentLineX += gd.advance;
}
retVal.X = Math.Max(retVal.X,currentLineX);
}
return retVal;
}
}
}
@@ -0,0 +1,147 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma
{
public class UISprite
{
public Sprite Sprite
{
get;
private set;
}
public bool Tile
{
get;
private set;
}
public bool Slice
{
get;
set;
}
public Rectangle[] Slices
{
get;
set;
}
public bool MaintainAspectRatio
{
get;
private set;
}
public UISprite(Sprite sprite, bool tile, bool maintainAspectRatio)
{
Sprite = sprite;
Tile = tile;
MaintainAspectRatio = maintainAspectRatio;
}
}
public class GUIComponentStyle
{
public readonly Vector4 Padding;
public readonly Color Color;
public readonly Color textColor;
public readonly Color HoverColor;
public readonly Color SelectedColor;
public readonly Color OutlineColor;
public readonly Dictionary<GUIComponent.ComponentState, List<UISprite>> Sprites;
public Dictionary<string, GUIComponentStyle> ChildStyles;
public GUIComponentStyle(XElement element)
{
Sprites = new Dictionary<GUIComponent.ComponentState, List<UISprite>>();
foreach (GUIComponent.ComponentState state in Enum.GetValues(typeof(GUIComponent.ComponentState)))
{
Sprites[state] = new List<UISprite>();
}
ChildStyles = new Dictionary<string, GUIComponentStyle>();
Padding = ToolBox.GetAttributeVector4(element, "padding", Vector4.Zero);
Vector4 colorVector = ToolBox.GetAttributeVector4(element, "color", new Vector4(0.0f, 0.0f, 0.0f, 0.0f));
Color = new Color(colorVector.X, colorVector.Y, colorVector.Z, colorVector.W);
colorVector = ToolBox.GetAttributeVector4(element, "textcolor", new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
textColor = new Color(colorVector.X, colorVector.Y, colorVector.Z, colorVector.W);
colorVector = ToolBox.GetAttributeVector4(element, "hovercolor", new Vector4(0.0f, 0.0f, 0.0f, 0.0f));
HoverColor = new Color(colorVector.X, colorVector.Y, colorVector.Z, colorVector.W);
colorVector = ToolBox.GetAttributeVector4(element, "selectedcolor", new Vector4(0.0f, 0.0f, 0.0f, 0.0f));
SelectedColor = new Color(colorVector.X, colorVector.Y, colorVector.Z, colorVector.W);
colorVector = ToolBox.GetAttributeVector4(element, "outlinecolor", new Vector4(0.0f, 0.0f, 0.0f, 0.0f));
OutlineColor = new Color(colorVector.X, colorVector.Y, colorVector.Z, colorVector.W);
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "sprite":
Sprite sprite = new Sprite(subElement);
bool maintainAspect = ToolBox.GetAttributeBool(subElement, "maintainaspectratio",false);
bool tile = ToolBox.GetAttributeBool(subElement, "tile", true);
string stateStr = ToolBox.GetAttributeString(subElement, "state", "None");
GUIComponent.ComponentState spriteState = GUIComponent.ComponentState.None;
Enum.TryParse(stateStr, out spriteState);
UISprite newSprite = new UISprite(sprite, tile, maintainAspect);
Vector4 sliceVec = ToolBox.GetAttributeVector4(subElement, "slice", Vector4.Zero);
if (sliceVec != Vector4.Zero)
{
Rectangle slice = new Rectangle((int)sliceVec.X, (int)sliceVec.Y, (int)(sliceVec.Z - sliceVec.X), (int)(sliceVec.W - sliceVec.Y));
newSprite.Slice = true;
newSprite.Slices = new Rectangle[9];
//top-left
newSprite.Slices[0] = new Rectangle(newSprite.Sprite.SourceRect.Location, slice.Location - newSprite.Sprite.SourceRect.Location);
//top-mid
newSprite.Slices[1] = new Rectangle(slice.Location.X, newSprite.Slices[0].Y, slice.Width, newSprite.Slices[0].Height);
//top-right
newSprite.Slices[2] = new Rectangle(slice.Right, newSprite.Slices[0].Y, newSprite.Sprite.SourceRect.Right - slice.Right, newSprite.Slices[0].Height);
//mid-left
newSprite.Slices[3] = new Rectangle(newSprite.Slices[0].X, slice.Y, newSprite.Slices[0].Width, slice.Height);
//center
newSprite.Slices[4] = slice;
//mid-right
newSprite.Slices[5] = new Rectangle(newSprite.Slices[2].X, slice.Y, newSprite.Slices[2].Width, slice.Height);
//bottom-left
newSprite.Slices[6] = new Rectangle(newSprite.Slices[0].X, slice.Bottom, newSprite.Slices[0].Width, newSprite.Sprite.SourceRect.Bottom - slice.Bottom);
//bottom-mid
newSprite.Slices[7] = new Rectangle(newSprite.Slices[1].X, slice.Bottom, newSprite.Slices[1].Width, newSprite.Sprite.SourceRect.Bottom - slice.Bottom);
//bottom-right
newSprite.Slices[8] = new Rectangle(newSprite.Slices[2].X, slice.Bottom, newSprite.Slices[2].Width, newSprite.Sprite.SourceRect.Bottom - slice.Bottom);
}
Sprites[spriteState].Add(newSprite);
break;
default:
ChildStyles.Add(subElement.Name.ToString().ToLowerInvariant(), new GUIComponentStyle(subElement));
break;
}
}
}
}
}
+615
View File
@@ -0,0 +1,615 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
namespace Barotrauma
{
[Flags]
public enum Alignment
{
CenterX = 1, Left = 2, Right = 4, CenterY = 8, Top = 16, Bottom = 32,
TopLeft = (Top | Left), TopCenter = (CenterX | Top), TopRight = (Top | Right),
CenterLeft = (Left | CenterY), Center = (CenterX | CenterY), CenterRight = (Right | CenterY),
BottomLeft = (Bottom | Left), BottomCenter = (CenterX | Bottom), BottomRight = (Bottom | Right),
}
public enum GUISoundType
{
Message,
RadioMessage,
DeadMessage,
Click,
Inventory,
}
public class GUI
{
public static GUIStyle Style;
private static Texture2D t;
public static ScalableFont Font, SmallFont, LargeFont;
private static Sprite cursor;
private static GraphicsDevice graphicsDevice;
public static GraphicsDevice GraphicsDevice
{
get
{
return graphicsDevice;
}
set
{
graphicsDevice = value;
}
}
private static List<GUIMessage> messages = new List<GUIMessage>();
private static Sound[] sounds;
private static bool pauseMenuOpen, settingsMenuOpen;
private static GUIFrame pauseMenu;
public static Color ScreenOverlayColor;
private static Sprite submarineIcon, arrow;
public static Sprite SubmarineIcon
{
get { return submarineIcon; }
}
public static Sprite SpeechBubbleIcon
{
get;
private set;
}
public static Sprite Arrow
{
get { return arrow; }
}
public static bool DisableHUD;
public static void Init(ContentManager content)
{
Font = new ScalableFont("Content/Exo2-Medium.otf", 14, graphicsDevice);
SmallFont = new ScalableFont("Content/Exo2-Light.otf", 12, graphicsDevice);
LargeFont = new ScalableFont("Content/Code Pro Bold.otf", 22, graphicsDevice);
cursor = new Sprite("Content/UI/cursor.png", Vector2.Zero);
Style = new GUIStyle("Content/UI/style.xml");
}
public static bool PauseMenuOpen
{
get { return pauseMenuOpen; }
}
public static bool SettingsMenuOpen
{
get { return settingsMenuOpen; }
}
public static void LoadContent(bool loadSounds = true)
{
if (loadSounds)
{
sounds = new Sound[Enum.GetValues(typeof(GUISoundType)).Length];
sounds[(int)GUISoundType.Message] = Sound.Load("Content/Sounds/UI/UImsg.ogg", false);
sounds[(int)GUISoundType.RadioMessage] = Sound.Load("Content/Sounds/UI/radiomsg.ogg", false);
sounds[(int)GUISoundType.DeadMessage] = Sound.Load("Content/Sounds/UI/deadmsg.ogg", false);
sounds[(int)GUISoundType.Click] = Sound.Load("Content/Sounds/UI/beep-shinymetal.ogg", false);
sounds[(int)GUISoundType.Inventory] = Sound.Load("Content/Sounds/pickItem.ogg", false);
}
// create 1x1 texture for line drawing
t = new Texture2D(graphicsDevice, 1, 1);
t.SetData(new Color[] { Color.White });// fill the texture with white
submarineIcon = new Sprite("Content/UI/uiIcons.png", new Rectangle(0, 192, 64, 64), null);
submarineIcon.Origin = submarineIcon.size / 2;
arrow = new Sprite("Content/UI/uiIcons.png", new Rectangle(80, 240, 16, 16), null);
arrow.Origin = arrow.size / 2;
SpeechBubbleIcon = new Sprite("Content/UI/uiIcons.png", new Rectangle(0, 129, 65, 61), null);
SpeechBubbleIcon.Origin = SpeechBubbleIcon.size / 2;
}
public static void TogglePauseMenu()
{
if (Screen.Selected == GameMain.MainMenuScreen) return;
settingsMenuOpen = false;
TogglePauseMenu(null, null);
if (pauseMenuOpen)
{
pauseMenu = new GUIFrame(new Rectangle(0, 0, 200, 300), null, Alignment.Center, "");
int y = 0;
var button = new GUIButton(new Rectangle(0, y, 0, 30), "Resume", Alignment.CenterX, "", pauseMenu);
button.OnClicked = TogglePauseMenu;
y += 60;
button = new GUIButton(new Rectangle(0, y, 0, 30), "Settings", Alignment.CenterX, "", pauseMenu);
button.OnClicked = (btn, userData) =>
{
TogglePauseMenu();
settingsMenuOpen = !settingsMenuOpen;
return true;
};
y += 60;
if (Screen.Selected == GameMain.GameScreen && GameMain.GameSession != null)
{
SinglePlayerMode spMode = GameMain.GameSession.gameMode as SinglePlayerMode;
if (spMode != null)
{
button = new GUIButton(new Rectangle(0, y, 0, 30), "Load previous", Alignment.CenterX, "", pauseMenu);
button.OnClicked += TogglePauseMenu;
button.OnClicked += GameMain.GameSession.LoadPrevious;
y += 60;
}
}
if (Screen.Selected == GameMain.LobbyScreen)
{
SinglePlayerMode spMode = GameMain.GameSession.gameMode as SinglePlayerMode;
if (spMode != null)
{
button = new GUIButton(new Rectangle(0, y, 0, 30), "Save & quit", Alignment.CenterX, "", pauseMenu);
button.OnClicked += QuitClicked;
button.OnClicked += TogglePauseMenu;
button.UserData = "save";
y += 60;
}
}
button = new GUIButton(new Rectangle(0, y, 0, 30), "Quit", Alignment.CenterX, "", pauseMenu);
button.OnClicked += QuitClicked;
button.OnClicked += TogglePauseMenu;
}
}
private static bool TogglePauseMenu(GUIButton button, object obj)
{
pauseMenuOpen = !pauseMenuOpen;
return true;
}
private static bool QuitClicked(GUIButton button, object obj)
{
if (button.UserData as string == "save")
{
SaveUtil.SaveGame(GameMain.GameSession.SaveFile);
}
if (GameMain.NetworkMember != null)
{
GameMain.NetworkMember.Disconnect();
GameMain.NetworkMember = null;
}
CoroutineManager.StopCoroutines("EndCinematic");
GameMain.GameSession = null;
GameMain.MainMenuScreen.Select();
//Game1.MainMenuScreen.SelectTab(null, (int)MainMenuScreen.Tabs.Main);
return true;
}
public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, int width = 1)
{
Vector2 edge = end - start;
// calculate angle to rotate line
float angle = (float)Math.Atan2(edge.Y, edge.X);
sb.Draw(t,
new Rectangle(// rectangle defines shape of line and position of start of line
(int)start.X,
(int)start.Y,
(int)edge.Length(), //sb will strech the texture to fill this rectangle
width), //width of line, change this to make thicker line
null,
clr, //colour of line
angle, //angle of line (calulated above)
new Vector2(0, 0), // point in line about which to rotate
SpriteEffects.None,
depth);
}
public static void DrawString(SpriteBatch sb, Vector2 pos, string text, Color color, Color? backgroundColor=null, int backgroundPadding=0, ScalableFont font = null)
{
if (font == null) font = Font;
if (backgroundColor != null)
{
Vector2 textSize = font.MeasureString(text);
DrawRectangle(sb, pos - Vector2.One * backgroundPadding, textSize + Vector2.One * 2.0f * backgroundPadding, (Color)backgroundColor, true);
}
font.DrawString(sb, text, pos, color);
}
public static void DrawRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, bool isFilled = false, float depth = 0.0f, int thickness = 1)
{
if (size.X < 0)
{
start.X += size.X;
size.X = -size.X;
}
if (size.Y < 0)
{
start.Y += size.Y;
size.Y = -size.Y;
}
DrawRectangle(sb, new Rectangle((int)start.X, (int)start.Y, (int)size.X, (int)size.Y), clr, isFilled, depth, thickness);
}
public static void DrawRectangle(SpriteBatch sb, Rectangle rect, Color clr, bool isFilled = false, float depth = 0.0f, int thickness = 1)
{
if (isFilled)
{
sb.Draw(t, rect, null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth);
}
else
{
sb.Draw(t, new Rectangle(rect.X + thickness, rect.Y, rect.Width - thickness * 2, thickness), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth);
sb.Draw(t, new Rectangle(rect.X + thickness, rect.Y + rect.Height - thickness, rect.Width - thickness * 2, thickness), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth);
sb.Draw(t, new Rectangle(rect.X, rect.Y, thickness, rect.Height), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth);
sb.Draw(t, new Rectangle(rect.X + rect.Width - thickness, rect.Y, thickness, rect.Height), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth);
}
}
public static void DrawProgressBar(SpriteBatch sb, Vector2 start, Vector2 size, float progress, Color clr, float depth = 0.0f)
{
DrawProgressBar(sb, start, size, progress, clr, new Color(0.5f, 0.57f, 0.6f, 1.0f), depth);
}
public static void DrawProgressBar(SpriteBatch sb, Vector2 start, Vector2 size, float progress, Color clr, Color outlineColor, float depth = 0.0f)
{
DrawRectangle(sb, new Vector2(start.X, -start.Y), size, outlineColor, false, depth);
int padding = 2;
DrawRectangle(sb, new Rectangle((int)start.X + padding, -(int)(start.Y - padding), (int)((size.X - padding * 2) * progress), (int)size.Y - padding * 2),
clr, true, depth);
}
public static Texture2D CreateCircle(int radius)
{
int outerRadius = radius * 2 + 2; // So circle doesn't go out of bounds
Texture2D texture = new Texture2D(graphicsDevice, outerRadius, outerRadius);
Color[] data = new Color[outerRadius * outerRadius];
// Colour the entire texture transparent first.
for (int i = 0; i < data.Length; i++)
data[i] = Color.Transparent;
// Work out the minimum step necessary using trigonometry + sine approximation.
double angleStep = 1f / radius;
for (double angle = 0; angle < Math.PI * 2; angle += angleStep)
{
// Use the parametric definition of a circle: http://en.wikipedia.org/wiki/Circle#Cartesian_coordinates
int x = (int)Math.Round(radius + radius * Math.Cos(angle));
int y = (int)Math.Round(radius + radius * Math.Sin(angle));
data[y * outerRadius + x + 1] = Color.White;
}
texture.SetData(data);
return texture;
}
public static Texture2D CreateCapsule(int radius, int height)
{
int textureWidth = radius * 2, textureHeight = height + radius * 2;
Texture2D texture = new Texture2D(graphicsDevice, textureWidth, textureHeight);
Color[] data = new Color[textureWidth * textureHeight];
// Colour the entire texture transparent first.
for (int i = 0; i < data.Length; i++)
data[i] = Color.Transparent;
// Work out the minimum step necessary using trigonometry + sine approximation.
double angleStep = 1f / radius;
for (int i = 0; i < 2; i++ )
{
for (double angle = 0; angle < Math.PI * 2; angle += angleStep)
{
// Use the parametric definition of a circle: http://en.wikipedia.org/wiki/Circle#Cartesian_coordinates
int x = (int)Math.Round(radius + radius * Math.Cos(angle));
int y = (height-1)*i + (int)Math.Round(radius + radius * Math.Sin(angle));
data[y * textureWidth + x] = Color.White;
}
}
for (int y = radius; y<textureHeight-radius; y++)
{
data[y * textureWidth] = Color.White;
data[y * textureWidth + (textureWidth-1)] = Color.White;
}
texture.SetData(data);
return texture;
}
public static Texture2D CreateRectangle(int width, int height)
{
Texture2D texture = new Texture2D(graphicsDevice, width, height);
Color[] data = new Color[width * height];
for (int i = 0; i < data.Length; i++)
data[i] = Color.Transparent;
for (int y = 0; y < height; y++)
{
data[y * width] = Color.White;
data[y * width + (width-1)] = Color.White;
}
for (int x = 0; x < width; x++)
{
data[x] = Color.White;
data[(height - 1) * width + x] = Color.White;
}
texture.SetData(data);
return texture;
}
public static bool DrawButton(SpriteBatch sb, Rectangle rect, string text, Color color, bool isHoldable = false)
{
bool clicked = false;
if (rect.Contains(PlayerInput.MousePosition))
{
clicked = PlayerInput.LeftButtonHeld();
color = clicked ?
new Color((int)(color.R * 0.8f), (int)(color.G * 0.8f), (int)(color.B * 0.8f), color.A) :
new Color((int)(color.R * 1.2f), (int)(color.G * 1.2f), (int)(color.B * 1.2f), color.A);
if (!isHoldable) clicked = PlayerInput.LeftButtonClicked();
}
DrawRectangle(sb, rect, color, true);
Vector2 origin;
try
{
origin = Font.MeasureString(text)/2;
}
catch
{
origin = Vector2.Zero;
}
Font.DrawString(sb, text, new Vector2(rect.Center.X, rect.Center.Y) , Color.White, 0.0f, origin, 1.0f, SpriteEffects.None, 0.0f);
return clicked;
}
public static void Draw(float deltaTime, SpriteBatch spriteBatch, Camera cam)
{
if (ScreenOverlayColor.A>0.0f)
{
DrawRectangle(
spriteBatch,
new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight),
ScreenOverlayColor, true);
}
if (GameMain.DebugDraw)
{
DrawString(spriteBatch, new Vector2(10, 10),
"FPS: " + (int)GameMain.FrameCounter.AverageFramesPerSecond,
Color.White, Color.Black * 0.5f, 0, SmallFont);
DrawString(spriteBatch, new Vector2(10, 25),
"Physics: " + GameMain.World.UpdateTime,
Color.White, Color.Black * 0.5f, 0, SmallFont);
DrawString(spriteBatch, new Vector2(10, 40),
"Bodies: " + GameMain.World.BodyList.Count + " (" + GameMain.World.BodyList.FindAll(b => b.Awake && b.Enabled).Count + " awake)",
Color.White, Color.Black * 0.5f, 0, SmallFont);
if (Screen.Selected.Cam != null)
{
DrawString(spriteBatch, new Vector2(10, 55),
"Camera pos: " + Screen.Selected.Cam.Position.ToPoint(),
Color.White, Color.Black * 0.5f, 0, SmallFont);
}
if (Submarine.MainSub != null)
{
DrawString(spriteBatch, new Vector2(10, 70),
"Sub pos: " + Submarine.MainSub.Position.ToPoint(),
Color.White, Color.Black * 0.5f, 0, SmallFont);
}
for (int i = 1; i < Sounds.SoundManager.DefaultSourceCount; i++)
{
Color clr = Color.White;
string soundStr = i+": ";
var playingSound = Sounds.SoundManager.GetPlayingSound(i);
if (playingSound == null)
{
soundStr+= "none";
clr *= 0.5f;
}
else
{
soundStr += System.IO.Path.GetFileNameWithoutExtension(playingSound.FilePath);
if (Sounds.SoundManager.IsLooping(i))
{
soundStr += " (looping)";
clr = Color.Yellow;
}
}
GUI.DrawString(spriteBatch, new Vector2(200, i * 15), soundStr, clr, Color.Black * 0.5f, 0, GUI.SmallFont);
}
}
if (GameMain.NetworkMember != null) GameMain.NetworkMember.Draw(spriteBatch);
DrawMessages(spriteBatch, (float)deltaTime);
if (GUIMessageBox.VisibleBox != null)
{
GUIMessageBox.VisibleBox.Draw(spriteBatch);
}
if (pauseMenuOpen)
{
pauseMenu.Draw(spriteBatch);
}
if (settingsMenuOpen)
{
GameMain.Config.SettingsFrame.Draw(spriteBatch);
}
DebugConsole.Draw(spriteBatch);
if (GUIComponent.MouseOn != null && !string.IsNullOrWhiteSpace(GUIComponent.MouseOn.ToolTip)) GUIComponent.MouseOn.DrawToolTip(spriteBatch);
if (!GUI.DisableHUD)
cursor.Draw(spriteBatch, PlayerInput.LatestMousePosition);
}
public static void AddToGUIUpdateList()
{
if (pauseMenuOpen)
{
pauseMenu.AddToGUIUpdateList();
}
if (settingsMenuOpen)
{
GameMain.Config.SettingsFrame.AddToGUIUpdateList();
}
if (GUIMessageBox.VisibleBox != null)
{
GUIMessageBox.VisibleBox.AddToGUIUpdateList();
}
}
public static void Update(float deltaTime)
{
if (pauseMenuOpen)
{
pauseMenu.Update(deltaTime);
}
if (settingsMenuOpen)
{
GameMain.Config.SettingsFrame.Update(deltaTime);
}
if (GUIMessageBox.VisibleBox != null)
{
GUIMessageBox.VisibleBox.Update(deltaTime);
}
}
public static void AddMessage(string message, Color color, float lifeTime = 3.0f, bool playSound = true)
{
if (messages.Count>0 && messages[messages.Count-1].Text == message)
{
messages[messages.Count - 1].LifeTime = lifeTime;
return;
}
Vector2 currPos = new Vector2(GameMain.GraphicsWidth / 2.0f, GameMain.GraphicsHeight * 0.7f);
currPos.Y += messages.Count * 30;
messages.Add(new GUIMessage(message, color, currPos, lifeTime));
if (playSound) PlayUISound(GUISoundType.Message);
}
public static void PlayUISound(GUISoundType soundType)
{
if (sounds == null) return;
int soundIndex = (int)soundType;
if (soundIndex < 0 || soundIndex >= sounds.Length) return;
sounds[soundIndex].Play();
}
private static void DrawMessages(SpriteBatch spriteBatch, float deltaTime)
{
if (messages.Count == 0) return;
Vector2 currPos = new Vector2(GameMain.GraphicsWidth / 2.0f, GameMain.GraphicsHeight * 0.7f);
int i = 1;
foreach (GUIMessage msg in messages)
{
float alpha = 1.0f;
if (msg.LifeTime < 1.0f)
{
alpha -= 1.0f - msg.LifeTime;
}
msg.Pos = MathUtils.SmoothStep(msg.Pos, currPos, deltaTime*20.0f);
Font.DrawString(spriteBatch, msg.Text,
new Vector2((int)msg.Pos.X - 1, (int)msg.Pos.Y - 1),
Color.Black * alpha*0.5f, 0.0f,
new Vector2((int)(0.5f * msg.Size.X), (int)(0.5f * msg.Size.Y)), 1.0f, SpriteEffects.None, 0.0f);
Font.DrawString(spriteBatch, msg.Text,
new Vector2((int)msg.Pos.X, (int)msg.Pos.Y),
msg.Color * alpha, 0.0f,
new Vector2((int)(0.5f * msg.Size.X), (int)(0.5f * msg.Size.Y)), 1.0f, SpriteEffects.None, 0.0f);
currPos.Y += 30.0f;
messages[0].LifeTime -= deltaTime/i;
i++;
}
if (messages[0].LifeTime <= 0.0f) messages.Remove(messages[0]);
}
}
}
+227
View File
@@ -0,0 +1,227 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma
{
public class GUIButton : GUIComponent
{
protected GUITextBlock textBlock;
protected GUIFrame frame;
public delegate bool OnClickedHandler(GUIButton button, object obj);
public OnClickedHandler OnClicked;
public delegate bool OnPressedHandler();
public OnPressedHandler OnPressed;
public bool CanBeSelected = true;
private bool enabled;
public bool Enabled
{
get
{
return enabled;
}
set
{
if (value == enabled) return;
enabled = value;
frame.Color = enabled ? color : Color.Gray * 0.7f;
}
}
public override Color Color
{
get { return base.Color; }
set
{
base.Color = value;
frame.Color = value;
}
}
public override Color HoverColor
{
get { return base.HoverColor; }
set
{
base.HoverColor = value;
frame.HoverColor = value;
}
}
public override Color SelectedColor
{
get
{
return base.SelectedColor;
}
set
{
base.SelectedColor = value;
frame.SelectedColor = value;
}
}
public override Color OutlineColor
{
get { return base.OutlineColor; }
set
{
base.OutlineColor = value;
if (frame != null) frame.OutlineColor = value;
}
}
public Color TextColor
{
get { return textBlock.TextColor; }
set { textBlock.TextColor = value; }
}
public override ScalableFont Font
{
get
{
return (textBlock==null) ? GUI.Font : textBlock.Font;
}
set
{
base.Font = value;
if (textBlock != null) textBlock.Font = value;
}
}
public string Text
{
get { return textBlock.Text; }
set { textBlock.Text = value; }
}
public override string ToolTip
{
get
{
return base.ToolTip;
}
set
{
textBlock.ToolTip = value;
base.ToolTip = value;
}
}
public override Rectangle Rect
{
get
{
return rect;
}
set
{
base.Rect = value;
frame.Rect = new Rectangle(value.X, value.Y, frame.Rect.Width, frame.Rect.Height);
textBlock.Rect = value;
}
}
public bool Selected { get; set; }
public GUIButton(Rectangle rect, string text, string style, GUIComponent parent = null)
: this(rect, text, null, Alignment.Left, style, parent)
{
}
public GUIButton(Rectangle rect, string text, Alignment alignment, string style, GUIComponent parent = null)
: this(rect, text, null, alignment, style, parent)
{
}
public GUIButton(Rectangle rect, string text, Color? color, string style, GUIComponent parent = null)
: this(rect, text, color, (Alignment.Left | Alignment.Top), style, parent)
{
}
public GUIButton(Rectangle rect, string text, Color? color, Alignment alignment, string style = "", GUIComponent parent = null)
: this(rect, text, color, alignment, Alignment.Center, style, parent)
{
}
public GUIButton(Rectangle rect, string text, Color? color, Alignment alignment, Alignment textAlignment, string style = "", GUIComponent parent = null)
: base(style)
{
this.rect = rect;
if (color != null) this.color = (Color)color;
this.alignment = alignment;
if (parent != null) parent.AddChild(this);
frame = new GUIFrame(Rectangle.Empty, style, this);
GUI.Style.Apply(frame, style == "" ? "GUIButton" : style);
textBlock = new GUITextBlock(Rectangle.Empty, text,
Color.Transparent, (this.style == null) ? Color.Black : this.style.textColor,
textAlignment, null, this);
GUI.Style.Apply(textBlock, style, this);
Enabled = true;
}
public override void ApplyStyle(GUIComponentStyle style)
{
base.ApplyStyle(style);
if (frame != null) frame.ApplyStyle(style);
if (textBlock != null) textBlock.ApplyStyle(style);
}
public override void Draw(SpriteBatch spriteBatch)
{
if (!Visible) return;
DrawChildren(spriteBatch);
}
public override void Update(float deltaTime)
{
if (!Visible) return;
base.Update(deltaTime);
if (rect.Contains(PlayerInput.MousePosition) && CanBeSelected && Enabled && (MouseOn == null || MouseOn == this || IsParentOf(MouseOn)))
{
state = ComponentState.Hover;
if (PlayerInput.LeftButtonHeld())
{
if (OnPressed != null)
{
if (OnPressed()) state = ComponentState.Pressed;
}
}
else if (PlayerInput.LeftButtonClicked())
{
GUI.PlayUISound(GUISoundType.Click);
if (OnClicked != null)
{
if (OnClicked(this, UserData) && CanBeSelected) state = ComponentState.Selected;
}
else
{
Selected = !Selected;
// state = state == ComponentState.Selected ? ComponentState.None : ComponentState.Selected;
}
}
}
else
{
state = Selected ? ComponentState.Selected : ComponentState.None;
}
frame.State = state;
}
}
}
+543
View File
@@ -0,0 +1,543 @@
using System;
using System.Collections.Generic;
using System.Linq;
using EventInput;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
{
public abstract class GUIComponent
{
const float FlashDuration = 1.5f;
public static GUIComponent MouseOn
{
get;
private set;
}
public static void ForceMouseOn(GUIComponent c)
{
MouseOn = c;
}
protected static List<GUIComponent> ComponentsToUpdate = new List<GUIComponent>();
public virtual void AddToGUIUpdateList()
{
if (!Visible) return;
if (ComponentsToUpdate.Contains(this)) return;
ComponentsToUpdate.Add(this);
try
{
List<GUIComponent> fixedChildren = new List<GUIComponent>(children);
foreach (GUIComponent c in fixedChildren)
{
c.AddToGUIUpdateList();
}
}
catch (Exception e)
{
DebugConsole.NewMessage("Error in AddToGUIUPdateList! GUIComponent runtime type: "+this.GetType().ToString()+"; children count: "+children.Count.ToString(),Color.Red);
throw;
}
}
public static void ClearUpdateList()
{
if (keyboardDispatcher != null &&
KeyboardDispatcher.Subscriber is GUIComponent &&
!ComponentsToUpdate.Contains((GUIComponent)KeyboardDispatcher.Subscriber))
{
KeyboardDispatcher.Subscriber = null;
}
ComponentsToUpdate.Clear();
}
public static GUIComponent UpdateMouseOn()
{
MouseOn = null;
for (int i=ComponentsToUpdate.Count-1;i>=0;i--)
{
GUIComponent c = ComponentsToUpdate[i];
if (c.MouseRect.Contains(PlayerInput.MousePosition))
{
MouseOn = c;
break;
}
}
return MouseOn;
}
protected static KeyboardDispatcher keyboardDispatcher;
public enum ComponentState { None, Hover, Pressed, Selected };
protected Alignment alignment;
protected GUIComponentStyle style;
protected object userData;
protected Rectangle rect;
public bool CanBeFocused;
protected Vector4 padding;
protected Color color;
protected Color hoverColor;
protected Color selectedColor;
protected GUIComponent parent;
public List<GUIComponent> children;
protected ComponentState state;
protected Color flashColor;
protected float flashTimer;
public virtual ScalableFont Font
{
get;
set;
}
public virtual string ToolTip
{
get;
set;
}
public GUIComponentStyle Style
{
get { return style; }
}
public bool Visible
{
get;
set;
}
public bool TileSprites;
private static GUITextBlock toolTipBlock;
//protected float alpha;
public GUIComponent Parent
{
get { return parent; }
}
public Vector2 Center
{
get { return new Vector2(rect.Center.X, rect.Center.Y); }
}
public virtual Rectangle Rect
{
get { return rect; }
set
{
int prevX = rect.X, prevY = rect.Y;
int prevWidth = rect.Width, prevHeight = rect.Height;
rect = value;
if (prevX == rect.X && prevY == rect.Y && rect.Width == prevWidth && rect.Height == prevHeight) return;
foreach (GUIComponent child in children)
{
child.Rect = new Rectangle(
child.rect.X + (rect.X - prevX),
child.rect.Y + (rect.Y - prevY),
Math.Max(child.rect.Width + (rect.Width - prevWidth),0),
Math.Max(child.rect.Height + (rect.Height - prevHeight),0));
}
}
}
public virtual Rectangle MouseRect
{
get { return CanBeFocused ? rect : Rectangle.Empty; }
}
public Dictionary<GUIComponent.ComponentState, List<UISprite>> sprites;
//public Alignment SpriteAlignment { get; set; }
//public bool RepeatSpriteX, RepeatSpriteY;
public virtual Color OutlineColor { get; set; }
public ComponentState State
{
get { return state; }
set { state = value; }
}
public object UserData
{
get { return userData; }
set { userData = value; }
}
public virtual Vector4 Padding
{
get { return padding; }
set { padding = value; }
}
public int CountChildren
{
get { return children.Count; }
}
public virtual Color Color
{
get { return color; }
set { color = value; }
}
public virtual Color HoverColor
{
get { return hoverColor; }
set { hoverColor = value; }
}
public virtual Color SelectedColor
{
get { return selectedColor; }
set { selectedColor = value; }
}
public static KeyboardDispatcher KeyboardDispatcher
{
get { return keyboardDispatcher; }
}
protected GUIComponent(string style)
{
Visible = true;
TileSprites = true;
OutlineColor = Color.Transparent;
Font = GUI.Font;
children = new List<GUIComponent>();
CanBeFocused = true;
if (style != null)
GUI.Style.Apply(this, style);
}
public static void Init(GameWindow window)
{
keyboardDispatcher = new KeyboardDispatcher(window);
}
public T GetChild<T>() where T : GUIComponent
{
foreach (GUIComponent child in children)
{
if (child is T) return (T)(object)child;
}
return default(T);
}
public GUIComponent GetChild(object obj)
{
foreach (GUIComponent child in children)
{
if (child.UserData == obj) return child;
}
return null;
}
public bool IsParentOf(GUIComponent component)
{
for(int i = children.Count - 1; i >= 0; i--)
{
if (children[i] == component) return true;
if (children[i].IsParentOf(component)) return true;
}
return false;
}
public virtual void Flash(Color? color = null)
{
flashTimer = FlashDuration;
flashColor = (color == null) ? Color.Red * 0.8f : (Color)color;
//foreach (GUIComponent child in children)
//{
// child.Flash();
//}
}
public virtual void Draw(SpriteBatch spriteBatch)
{
if (!Visible) return;
Color currColor = color;
if (state == ComponentState.Selected) currColor = selectedColor;
if (state == ComponentState.Hover) currColor = hoverColor;
if (flashTimer > 0.0f)
{
GUI.DrawRectangle(spriteBatch,
new Rectangle(rect.X - 5, rect.Y - 5, rect.Width + 10, rect.Height + 10),
flashColor * (flashTimer / FlashDuration), true);
}
if (currColor.A > 0.0f && (sprites == null || !sprites.Any())) GUI.DrawRectangle(spriteBatch, rect, currColor * (currColor.A / 255.0f), true);
if (sprites != null && sprites[state] != null && currColor.A > 0.0f)
{
foreach (UISprite uiSprite in sprites[state])
{
if (uiSprite.Slice)
{
Vector2 pos = new Vector2(rect.X, rect.Y);
int centerWidth = Math.Max(rect.Width - uiSprite.Slices[0].Width - uiSprite.Slices[2].Width, 0);
int centerHeight = Math.Max(rect.Height - uiSprite.Slices[0].Height - uiSprite.Slices[8].Height, 0);
Vector2 scale = new Vector2(
MathHelper.Clamp((float)rect.Width / (uiSprite.Slices[0].Width + uiSprite.Slices[2].Width),0, 1),
MathHelper.Clamp((float)rect.Height / (uiSprite.Slices[0].Height + uiSprite.Slices[6].Height), 0, 1));
for (int x = 0; x < 3; x++)
{
float width = (x == 1 ? centerWidth : uiSprite.Slices[x].Width) * scale.X;
for (int y = 0; y < 3; y++)
{
float height = (y == 1 ? centerHeight : uiSprite.Slices[x + y * 3].Height) * scale.Y;
spriteBatch.Draw(uiSprite.Sprite.Texture,
new Rectangle((int)pos.X, (int)pos.Y, (int)width, (int)height),
uiSprite.Slices[x + y * 3],
currColor * (currColor.A / 255.0f));
pos.Y += height;
}
pos.X += width;
pos.Y = rect.Y;
}
}
else if (uiSprite.Tile)
{
Vector2 startPos = new Vector2(rect.X, rect.Y);
Vector2 size = new Vector2(Math.Min(uiSprite.Sprite.SourceRect.Width, rect.Width), Math.Min(uiSprite.Sprite.SourceRect.Height, rect.Height));
if (uiSprite.Sprite.size.X == 0.0f) size.X = rect.Width;
if (uiSprite.Sprite.size.Y == 0.0f) size.Y = rect.Height;
uiSprite.Sprite.DrawTiled(spriteBatch, startPos, size, currColor * (currColor.A / 255.0f));
}
else
{
if (uiSprite.MaintainAspectRatio)
{
float scale = (float)(rect.Width) / uiSprite.Sprite.SourceRect.Width;
spriteBatch.Draw(uiSprite.Sprite.Texture, rect,
new Rectangle(uiSprite.Sprite.SourceRect.X, uiSprite.Sprite.SourceRect.Y, (int)(uiSprite.Sprite.SourceRect.Width), (int)(rect.Height / scale)),
currColor * (currColor.A / 255.0f), 0.0f, Vector2.Zero, SpriteEffects.None, 0.0f);
}
else
{
spriteBatch.Draw(uiSprite.Sprite.Texture, rect, uiSprite.Sprite.SourceRect, currColor * (currColor.A / 255.0f));
}
}
}
}
//Color newColor = color;
//if (state == ComponentState.Selected) newColor = selectedColor;
//if (state == ComponentState.Hover) newColor = hoverColor;
//GUI.DrawRectangle(spriteBatch, rect, newColor*alpha, true);
//DrawChildren(spriteBatch);
}
public void DrawToolTip(SpriteBatch spriteBatch)
{
if (!Visible) return;
int width = 400;
if (toolTipBlock == null || (string)toolTipBlock.userData != ToolTip)
{
toolTipBlock = new GUITextBlock(new Rectangle(0, 0, width, 18), ToolTip, "GUIToolTip", Alignment.TopLeft, Alignment.TopLeft, null, true, GUI.SmallFont);
toolTipBlock.padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f);
toolTipBlock.rect.Width = (int)(GUI.SmallFont.MeasureString(toolTipBlock.WrappedText).X + 20);
toolTipBlock.rect.Height = toolTipBlock.WrappedText.Split('\n').Length * 18 + 7;
toolTipBlock.userData = ToolTip;
}
toolTipBlock.rect = new Rectangle(MouseOn.Rect.Center.X, MouseOn.rect.Bottom, toolTipBlock.rect.Width, toolTipBlock.rect.Height);
if (toolTipBlock.rect.Right > GameMain.GraphicsWidth - 10)
{
toolTipBlock.rect.Location -= new Point(toolTipBlock.rect.Right - (GameMain.GraphicsWidth - 10), 0);
}
toolTipBlock.Draw(spriteBatch);
}
public virtual void Update(float deltaTime)
{
if (!Visible) return;
if (flashTimer>0.0f) flashTimer -= deltaTime;
/*if (CanBeFocused)
{
if (rect.Contains(PlayerInput.MousePosition))
{
MouseOn = this;
}
else
{
if (MouseOn == this) MouseOn = null;
}
}*/
try
{
//use a fixed list since children can change their order in the main children list
//TODO: maybe find a more efficient way of handling changes in list order
List<GUIComponent> fixedChildren = new List<GUIComponent>(children);
foreach (GUIComponent c in fixedChildren)
{
if (!c.Visible) continue;
c.Update(deltaTime);
}
}
catch (Exception e)
{
DebugConsole.NewMessage("Error in Update! GUIComponent runtime type: " + this.GetType().ToString() + "; children count: " + children.Count.ToString(), Color.Red);
throw;
}
}
protected virtual void UpdateDimensions(GUIComponent parent = null)
{
Rectangle parentRect = (parent==null) ? new Rectangle(0,0,GameMain.GraphicsWidth, GameMain.GraphicsHeight) : parent.rect;
Vector4 padding = (parent == null) ? Vector4.Zero : parent.padding;
if (rect.Width == 0) rect.Width = parentRect.Width - rect.X
- (int)padding.X - (int)padding.Z;
if (rect.Height == 0) rect.Height = parentRect.Height - rect.Y
- (int)padding.Y - (int)padding.W;
if (alignment.HasFlag(Alignment.CenterX))
{
rect.X += parentRect.X + (int)parentRect.Width/2 - (int)rect.Width/2;
}
else if (alignment.HasFlag(Alignment.Right))
{
rect.X += parentRect.X + (int)parentRect.Width - (int)padding.Z - (int)rect.Width;
}
else
{
rect.X += parentRect.X + (int)padding.X;
}
if (alignment.HasFlag(Alignment.CenterY))
{
rect.Y += parentRect.Y + (int)parentRect.Height / 2 - (int)rect.Height / 2;
}
else if (alignment.HasFlag(Alignment.Bottom))
{
rect.Y += parentRect.Y + (int)parentRect.Height - (int)padding.W - (int)rect.Height;
}
else
{
rect.Y += parentRect.Y + (int)padding.Y;
}
}
public virtual void ApplyStyle(GUIComponentStyle style)
{
if (style == null) return;
color = style.Color;
hoverColor = style.HoverColor;
selectedColor = style.SelectedColor;
padding = style.Padding;
sprites = style.Sprites;
OutlineColor = style.OutlineColor;
this.style = style;
}
public virtual void DrawChildren(SpriteBatch spriteBatch)
{
for (int i = 0; i < children.Count; i++ )
{
children[i].Draw(spriteBatch);
}
}
public virtual void AddChild(GUIComponent child)
{
if (child == null) return;
if (child.IsParentOf(this))
{
DebugConsole.ThrowError("Tried to add the parent of a GUIComponent as a child.\n" + Environment.StackTrace);
return;
}
if (child == this)
{
DebugConsole.ThrowError("Tried to add a GUIComponent as its own child\n" + Environment.StackTrace);
return;
}
if (children.Contains(child))
{
DebugConsole.ThrowError("Tried to add a the same child twice to a GUIComponent" + Environment.StackTrace);
return;
}
child.parent = this;
child.UpdateDimensions(this);
children.Add(child);
}
public virtual void RemoveChild(GUIComponent child)
{
if (child == null) return;
if (children.Contains(child)) children.Remove(child);
}
public GUIComponent FindChild(object userData)
{
return children.FirstOrDefault(c => c.userData == userData);
}
public List<GUIComponent> FindChildren(object userData)
{
return children.FindAll(c => c.userData == userData);
}
public virtual void ClearChildren()
{
children.Clear();
}
}
}
+205
View File
@@ -0,0 +1,205 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Barotrauma
{
public class GUIDropDown : GUIComponent
{
public delegate bool OnSelectedHandler(GUIComponent selected, object obj = null);
public OnSelectedHandler OnSelected;
private GUIButton button;
private GUIListBox listBox;
public bool Dropped { get; set; }
public object SelectedItemData
{
get
{
if (listBox.Selected == null) return null;
return listBox.Selected.UserData;
}
}
public bool Enabled
{
get { return listBox.Enabled; }
set { listBox.Enabled = value; }
}
public GUIComponent Selected
{
get { return listBox.Selected; }
}
public GUIListBox ListBox
{
get { return listBox; }
}
public object SelectedData
{
get
{
return (listBox.Selected == null) ? null : listBox.Selected.UserData;
}
}
public int SelectedIndex
{
get
{
if (listBox.Selected == null) return -1;
return listBox.children.FindIndex(x => x == listBox.Selected);
}
}
public override string ToolTip
{
get
{
return base.ToolTip;
}
set
{
base.ToolTip = value;
button.ToolTip = value;
listBox.ToolTip = value;
}
}
public GUIDropDown(Rectangle rect, string text, string style, GUIComponent parent = null)
: base(style)
{
this.rect = rect;
if (parent != null) parent.AddChild(this);
button = new GUIButton(this.rect, text, Color.White, Alignment.TopLeft, Alignment.CenterLeft, "GUIDropDown", null);
GUI.Style.Apply(button, style, this);
button.OnClicked = OnClicked;
listBox = new GUIListBox(new Rectangle(this.rect.X, this.rect.Bottom, this.rect.Width, 200), style, null);
listBox.OnSelected = SelectItem;
}
public override void AddChild(GUIComponent child)
{
listBox.AddChild(child);
}
public void AddItem(string text, object userData = null)
{
GUITextBlock textBlock = new GUITextBlock(new Rectangle(0,0,0,20), text, "ListBoxElement", Alignment.TopLeft, Alignment.CenterLeft, listBox);
textBlock.UserData = userData;
}
public override void ClearChildren()
{
listBox.ClearChildren();
}
public List<GUIComponent> GetChildren()
{
return listBox.children;
}
private bool SelectItem(GUIComponent component, object obj)
{
GUITextBlock textBlock = component as GUITextBlock;
if (textBlock==null) return false;
button.Text = textBlock.Text;
Dropped = false;
if (OnSelected != null) OnSelected(component, component.UserData);
return true;
}
public void SelectItem(object userData)
{
//GUIComponent child = listBox.children.FirstOrDefault(c => c.UserData == userData);
//if (child == null) return;
listBox.Select(userData);
//SelectItem(child, userData);
}
public void Select(int index)
{
listBox.Select(index);
}
private bool wasOpened;
private bool OnClicked(GUIComponent component, object obj)
{
if (wasOpened) return false;
wasOpened = true;
Dropped = !Dropped;
if (Dropped && parent.children[parent.children.Count-1]!=this)
{
parent.children.Remove(this);
parent.children.Add(this);
}
return true;
}
public override void AddToGUIUpdateList()
{
base.AddToGUIUpdateList();
button.AddToGUIUpdateList();
if (Dropped) listBox.AddToGUIUpdateList();
}
public override void Update(float deltaTime)
{
if (!Visible) return;
wasOpened = false;
base.Update(deltaTime);
if (Dropped && PlayerInput.LeftButtonClicked())
{
Rectangle listBoxRect = listBox.Rect;
listBoxRect.Width += 20;
if (!listBoxRect.Contains(PlayerInput.MousePosition) && !button.Rect.Contains(PlayerInput.MousePosition))
{
Dropped = false;
}
}
button.Update(deltaTime);
if (Dropped) listBox.Update(deltaTime);
}
public override void Draw(SpriteBatch spriteBatch)
{
if (!Visible) return;
base.Draw(spriteBatch);
button.Draw(spriteBatch);
if (!Dropped) return;
listBox.Draw(spriteBatch);
}
}
}
+61
View File
@@ -0,0 +1,61 @@
using Microsoft.Xna.Framework;
using System.Linq;
namespace Barotrauma
{
public class GUIFrame : GUIComponent
{
public GUIFrame(Rectangle rect, string style = "", GUIComponent parent = null)
: this(rect, null, (Alignment.Left | Alignment.Top), style, parent)
{
}
public GUIFrame(Rectangle rect, Color color, string style = "", GUIComponent parent = null)
: this(rect, color, (Alignment.Left | Alignment.Top), style, parent)
{
}
public GUIFrame(Rectangle rect, Color? color, Alignment alignment, string style = "", GUIComponent parent = null)
: base(style)
{
this.rect = rect;
this.alignment = alignment;
if (color != null) this.color = (Color)color;
if (parent != null)
{
parent.AddChild(this);
}
else
{
UpdateDimensions();
}
//if (style != null) ApplyStyle(style);
}
public override void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
{
if (!Visible) return;
Color currColor = color;
if (state == ComponentState.Selected) currColor = selectedColor;
if (state == ComponentState.Hover) currColor = hoverColor;
if (sprites == null || !sprites.Any()) GUI.DrawRectangle(spriteBatch, rect, currColor * (currColor.A/255.0f), true);
base.Draw(spriteBatch);
if (OutlineColor != Color.Transparent)
{
GUI.DrawRectangle(spriteBatch, rect, OutlineColor * (OutlineColor.A/255.0f), false);
}
DrawChildren(spriteBatch);
}
}
}
+97
View File
@@ -0,0 +1,97 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
{
public class GUIImage : GUIComponent
{
public float Rotation;
private Sprite sprite;
private Rectangle sourceRect;
bool crop;
public bool Crop
{
get
{
return crop;
}
set
{
crop = value;
if (crop)
{
sourceRect.Width = Math.Min(sprite.SourceRect.Width, Rect.Width);
sourceRect.Height = Math.Min(sprite.SourceRect.Height, Rect.Height);
}
}
}
public float Scale
{
get;
set;
}
public Rectangle SourceRect
{
get { return sourceRect; }
set { sourceRect = value; }
}
public GUIImage(Rectangle rect, string spritePath, Alignment alignment, GUIComponent parent = null)
: this(rect, new Sprite(spritePath, Vector2.Zero), alignment, parent)
{
}
public GUIImage(Rectangle rect, Sprite sprite, Alignment alignment, GUIComponent parent = null)
: this(rect, sprite==null ? Rectangle.Empty : sprite.SourceRect, sprite, alignment, parent)
{
}
public GUIImage(Rectangle rect, Rectangle sourceRect, Sprite sprite, Alignment alignment, GUIComponent parent = null)
: base(null)
{
this.rect = rect;
this.alignment = alignment;
color = Color.White;
//alpha = 1.0f;
Scale = 1.0f;
this.sprite = sprite;
if (rect.Width == 0) this.rect.Width = (int)sprite.size.X;
if (rect.Height == 0) this.rect.Height = (int)Math.Min(sprite.size.Y, sprite.size.Y * (this.rect.Width / sprite.size.X));
this.sourceRect = sourceRect;
if (parent != null) parent.AddChild(this);
this.parent = parent;
}
public override void Draw(SpriteBatch spriteBatch)
{
if (!Visible) return;
Color currColor = color;
if (state == ComponentState.Hover) currColor = hoverColor;
if (state == ComponentState.Selected) currColor = selectedColor;
if (sprite != null && sprite.Texture != null)
{
spriteBatch.Draw(sprite.Texture, new Vector2(rect.X, rect.Y), sourceRect, currColor * (currColor.A / 255.0f), Rotation, Vector2.Zero,
Scale, SpriteEffects.None, 0.0f);
}
DrawChildren(spriteBatch);
}
}
}
+426
View File
@@ -0,0 +1,426 @@
using System;
using System.Linq;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
namespace Barotrauma
{
public class GUIListBox : GUIComponent
{
protected List<GUIComponent> selected;
public delegate bool OnSelectedHandler(GUIComponent component, object obj);
public OnSelectedHandler OnSelected;
public delegate object CheckSelectedHandler();
public CheckSelectedHandler CheckSelected;
private GUIScrollBar scrollBar;
private GUIFrame frame;
private int totalSize;
private int spacing;
private bool scrollBarEnabled;
private bool scrollBarHidden;
private bool enabled;
public bool SelectMultiple;
public GUIComponent Selected
{
get
{
return selected.Any() ? selected[0] : null;
}
}
public List<GUIComponent> AllSelected
{
get { return selected; }
}
public object SelectedData
{
get
{
return (Selected == null) ? null : Selected.UserData;
}
}
public int SelectedIndex
{
get
{
if (Selected == null) return -1;
return children.FindIndex(x => x == Selected);
}
}
public float BarScroll
{
get { return scrollBar.BarScroll; }
set { scrollBar.BarScroll = value; }
}
public float BarSize
{
get { return scrollBar.BarSize; }
}
public int Spacing
{
get { return spacing; }
set { spacing = value; }
}
public bool Enabled
{
get { return enabled; }
set { enabled = value; }
}
public override Color Color
{
get
{
return base.Color;
}
set
{
base.Color = value;
frame.Color = value;
}
}
public bool ScrollBarEnabled
{
get { return scrollBarEnabled; }
set
{
scrollBarEnabled = value;
}
}
public GUIListBox(Rectangle rect, string style, GUIComponent parent = null)
: this(rect, style, Alignment.TopLeft, parent)
{
}
public GUIListBox(Rectangle rect, string style, Alignment alignment, GUIComponent parent = null)
: this(rect, null, alignment, style, parent, false)
{
}
public GUIListBox(Rectangle rect, Color? color, string style = null, GUIComponent parent = null)
: this(rect, color, (Alignment.Left | Alignment.Top), style, parent)
{
}
public GUIListBox(Rectangle rect, Color? color, Alignment alignment, string style = null, GUIComponent parent = null, bool isHorizontal = false)
: base(style)
{
this.rect = rect;
this.alignment = alignment;
selected = new List<GUIComponent>();
if (color != null) this.color = (Color)color;
if (parent != null)
parent.AddChild(this);
scrollBarHidden = true;
if (isHorizontal)
{
scrollBar = new GUIScrollBar(
new Rectangle(this.rect.X, this.rect.Bottom - 20, this.rect.Width, 20), null, 1.0f, "");
}
else
{
scrollBar = new GUIScrollBar(
new Rectangle(this.rect.Right - 20, this.rect.Y, 20, this.rect.Height), null, 1.0f, "");
}
scrollBar.IsHorizontal = isHorizontal;
frame = new GUIFrame(new Rectangle(0, 0, this.rect.Width, this.rect.Height), style, this);
if (style != null) GUI.Style.Apply(frame, style, this);
UpdateScrollBarSize();
children.Clear();
enabled = true;
scrollBarEnabled = true;
scrollBar.BarScroll = 0.0f;
}
public void Select(object userData, bool force = false)
{
for (int i = 0; i < children.Count; i++)
{
if (!children[i].UserData.Equals(userData)) continue;
Select(i, force);
//if (OnSelected != null) OnSelected(Selected, Selected.UserData);
if (!SelectMultiple) return;
}
}
private void UpdateChildrenRect(float deltaTime)
{
int x = rect.X, y = rect.Y;
if (!scrollBarHidden)
{
if (scrollBar.IsHorizontal)
{
x -= (int)((totalSize - rect.Width) * scrollBar.BarScroll);
}
else
{
y -= (int)((totalSize - rect.Height) * scrollBar.BarScroll);
}
}
for (int i = 0; i < children.Count; i++)
{
GUIComponent child = children[i];
if (child == frame || !child.Visible) continue;
child.Rect = new Rectangle(x, y, child.Rect.Width, child.Rect.Height);
if (scrollBar.IsHorizontal)
{
x += child.Rect.Width + spacing;
}
else
{
y += child.Rect.Height + spacing;
}
if (deltaTime>0.0f) child.Update(deltaTime);
if (enabled && child.CanBeFocused &&
(MouseOn == this || (MouseOn != null && this.IsParentOf(MouseOn))) && child.Rect.Contains(PlayerInput.MousePosition))
{
child.State = ComponentState.Hover;
if (PlayerInput.LeftButtonClicked())
{
Select(i);
}
}
else if (selected.Contains(child))
{
child.State = ComponentState.Selected;
if (CheckSelected != null)
{
if (CheckSelected() != child.UserData) selected.Remove(child);
}
}
else
{
child.State = ComponentState.None;
}
}
}
public override void AddToGUIUpdateList()
{
if (!Visible) return;
if (ComponentsToUpdate.Contains(this)) return;
ComponentsToUpdate.Add(this);
try
{
List<GUIComponent> fixedChildren = new List<GUIComponent>(children);
int lastVisible = 0;
for (int i = 0; i < fixedChildren.Count; i++)
{
if (fixedChildren[i] == frame) continue;
if (!IsChildVisible(fixedChildren[i]))
{
if (lastVisible > 0) break;
continue;
}
lastVisible = i;
fixedChildren[i].AddToGUIUpdateList();
}
}
catch (Exception e)
{
DebugConsole.NewMessage("Error in AddToGUIUpdateList! GUIComponent runtime type: " + this.GetType().ToString() + "; children count: " + children.Count.ToString(), Color.Red);
throw;
}
if (scrollBarEnabled && !scrollBarHidden) scrollBar.AddToGUIUpdateList();
}
public override Rectangle MouseRect
{
get
{
return rect;
}
}
public override void Update(float deltaTime)
{
if (!Visible) return;
UpdateChildrenRect(deltaTime);
//base.Update(deltaTime);
if (scrollBarEnabled && !scrollBarHidden) scrollBar.Update(deltaTime);
if ((MouseOn == this || MouseOn == scrollBar || IsParentOf(MouseOn)) && PlayerInput.ScrollWheelSpeed != 0)
{
scrollBar.BarScroll -= (PlayerInput.ScrollWheelSpeed / 500.0f) * BarSize;
}
}
public void Select(int childIndex, bool force = false)
{
if (childIndex >= children.Count || childIndex < 0) return;
bool wasSelected = true;
if (OnSelected != null) wasSelected = OnSelected(children[childIndex], children[childIndex].UserData) || force;
if (!wasSelected) return;
if (SelectMultiple)
{
if (selected.Contains(children[childIndex]))
{
selected.Remove(children[childIndex]);
}
else
{
selected.Add(children[childIndex]);
}
}
else
{
selected.Clear();
selected.Add(children[childIndex]);
}
}
public void Deselect()
{
selected.Clear();
}
public void UpdateScrollBarSize()
{
totalSize = 0;
foreach (GUIComponent child in children)
{
if (child == frame) continue;
totalSize += (scrollBar.IsHorizontal) ? child.Rect.Width : child.Rect.Height;
totalSize += spacing;
}
scrollBar.BarSize = scrollBar.IsHorizontal ?
Math.Max(Math.Min((float)rect.Width / (float)totalSize, 1.0f), 5.0f / rect.Width) :
Math.Max(Math.Min((float)rect.Height / (float)totalSize, 1.0f), 5.0f / rect.Height);
scrollBarHidden = scrollBar.BarSize >= 1.0f;
}
public override void AddChild(GUIComponent child)
{
//temporarily reduce the size of the rect to prevent the child from expanding over the scrollbar
if (scrollBar.IsHorizontal)
rect.Height -= scrollBar.Rect.Height;
else
rect.Width -= scrollBar.Rect.Width;
base.AddChild(child);
if (scrollBar.IsHorizontal)
rect.Height += scrollBar.Rect.Height;
else
rect.Width += scrollBar.Rect.Width;
UpdateScrollBarSize();
UpdateChildrenRect(0.0f);
}
public override void ClearChildren()
{
base.ClearChildren();
selected.Clear();
}
public override void RemoveChild(GUIComponent child)
{
base.RemoveChild(child);
if (selected.Contains(child)) selected.Remove(child);
UpdateScrollBarSize();
}
public override void Draw(SpriteBatch spriteBatch)
{
if (!Visible) return;
frame.Draw(spriteBatch);
if (!scrollBarHidden) scrollBar.Draw(spriteBatch);
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
spriteBatch.GraphicsDevice.ScissorRectangle = frame.Rect;
int lastVisible = 0;
for (int i = 0; i < children.Count; i++)
{
GUIComponent child = children[i];
if (child == frame || !child.Visible) continue;
if (!IsChildVisible(child))
{
if (lastVisible > 0) break;
continue;
}
lastVisible = i;
child.Draw(spriteBatch);
}
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
}
private bool IsChildVisible(GUIComponent child)
{
if (child == null) return false;
if (scrollBar.IsHorizontal)
{
if (child.Rect.Right < rect.X) return false;
if (child.Rect.X > rect.Right) return false;
}
else
{
if (child.Rect.Bottom < rect.Y) return false;
if (child.Rect.Y > rect.Bottom) return false;
}
return true;
}
}
}
+51
View File
@@ -0,0 +1,51 @@
using Microsoft.Xna.Framework;
namespace Barotrauma
{
class GUIMessage
{
private ColoredText coloredText;
private Vector2 pos;
private float lifeTime;
private Vector2 size;
public string Text
{
get { return coloredText.Text; }
}
public Color Color
{
get { return coloredText.Color; }
}
public Vector2 Pos
{
get { return pos; }
set { pos = value; }
}
public Vector2 Size
{
get { return size; }
}
public float LifeTime
{
get { return lifeTime; }
set { lifeTime = value; }
}
public GUIMessage(string text, Color color, Vector2 position, float lifeTime)
{
coloredText = new ColoredText(text, color);
pos = position;
this.lifeTime = lifeTime;
size = GUI.Font.MeasureString(text);
}
}
}
@@ -0,0 +1,96 @@
using Microsoft.Xna.Framework;
using System.Collections.Generic;
namespace Barotrauma
{
public class GUIMessageBox : GUIFrame
{
public static List<GUIComponent> MessageBoxes = new List<GUIComponent>();
const int DefaultWidth=400, DefaultHeight=250;
//public delegate bool OnClickedHandler(GUIButton button, object obj);
//public OnClickedHandler OnClicked;
//GUIFrame frame;
public GUIButton[] Buttons;
public static GUIComponent VisibleBox
{
get { return MessageBoxes.Count == 0 ? null : MessageBoxes[0]; }
}
public string Text
{
get { return (children[0].children[1] as GUITextBlock).Text; }
set { (children[0].children[1] as GUITextBlock).Text = value; }
}
public GUIMessageBox(string headerText, string text)
: this(headerText, text, new string[] {"OK"})
{
this.Buttons[0].OnClicked = Close;
}
public GUIMessageBox(string headerText, string text, int width, int height)
: this(headerText, text, new string[] { "OK" }, width, height)
{
this.Buttons[0].OnClicked = Close;
}
public GUIMessageBox(string headerText, string text, string[] buttons, int width = DefaultWidth, int height = DefaultHeight, Alignment textAlignment = Alignment.TopLeft, GUIComponent parent = null)
: base(new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight),
Color.Black * 0.5f, Alignment.TopLeft, null, parent)
{
if (height == 0)
{
string wrappedText = ToolBox.WrapText(text, width, GUI.Font);
string[] lines = wrappedText.Split('\n');
foreach (string line in lines)
{
height += (int)GUI.Font.MeasureString(line).Y;
}
height += 220;
}
var frame = new GUIFrame(new Rectangle(0, 0, width, height), null, Alignment.Center, "", this);
GUI.Style.Apply(frame, "", this);
var header = new GUITextBlock(new Rectangle(0, 0, 0, 30), headerText, null, null, textAlignment, "", frame, true);
GUI.Style.Apply(header, "", this);
if (!string.IsNullOrWhiteSpace(text))
{
var textBlock = new GUITextBlock(new Rectangle(0, 30, 0, height - 70), text,
null, null, textAlignment, "", frame, true);
GUI.Style.Apply(textBlock, "", this);
}
int x = 0;
this.Buttons = new GUIButton[buttons.Length];
for (int i = 0; i < buttons.Length; i++)
{
this.Buttons[i] = new GUIButton(new Rectangle(x, 0, 150, 30), buttons[i], Alignment.Left | Alignment.Bottom, "", frame);
x += this.Buttons[i].Rect.Width + 20;
}
MessageBoxes.Add(this);
}
public bool Close(GUIButton button, object obj)
{
if (parent != null) parent.RemoveChild(this);
if (MessageBoxes.Contains(this)) MessageBoxes.Remove(this);
return true;
}
public static void CloseAll()
{
MessageBoxes.Clear();
}
}
}
@@ -0,0 +1,125 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
{
public class GUIProgressBar : GUIComponent
{
private bool isHorizontal;
private GUIFrame frame, slider;
private float barSize;
public delegate float ProgressGetterHandler();
public ProgressGetterHandler ProgressGetter;
public bool IsHorizontal
{
get { return isHorizontal; }
set { isHorizontal = value; }
}
public float BarSize
{
get { return barSize; }
set
{
float oldBarSize = barSize;
barSize = MathHelper.Clamp(value, 0.0f, 1.0f);
if (barSize != oldBarSize) UpdateRect();
}
}
public GUIProgressBar(Rectangle rect, Color color, float barSize, GUIComponent parent = null)
: this(rect, color, barSize, (Alignment.Left | Alignment.Top), parent)
{
}
public GUIProgressBar(Rectangle rect, Color color, float barSize, Alignment alignment, GUIComponent parent = null)
: this(rect, color, null, barSize, alignment, parent)
{
}
public GUIProgressBar(Rectangle rect, Color color, string style, float barSize, Alignment alignment, GUIComponent parent = null)
: base(style)
{
this.rect = rect;
this.color = color;
isHorizontal = (rect.Width > rect.Height);
this.alignment = alignment;
if (parent != null)
parent.AddChild(this);
frame = new GUIFrame(new Rectangle(0, 0, 0, 0), null, this);
GUI.Style.Apply(frame, "", this);
slider = new GUIFrame(new Rectangle(0, 0, 0, 0), null);
GUI.Style.Apply(slider, "Slider", this);
this.barSize = barSize;
UpdateRect();
}
/*public override void ApplyStyle(GUIComponentStyle style)
{
if (frame == null) return;
frame.Color = style.Color;
frame.HoverColor = style.HoverColor;
frame.SelectedColor = style.SelectedColor;
Padding = style.Padding;
frame.OutlineColor = style.OutlineColor;
this.style = style;
}*/
private void UpdateRect()
{
slider.Rect = new Rectangle(
(int)(frame.Rect.X + padding.X),
(int)(frame.Rect.Y + padding.Y),
isHorizontal ? (int)((frame.Rect.Width - padding.X - padding.Z) * barSize) : frame.Rect.Width,
isHorizontal ? (int)(frame.Rect.Height - padding.Y - padding.W) : (int)(frame.Rect.Height * barSize));
}
public override void Draw(SpriteBatch spriteBatch)
{
if (!Visible) return;
if (ProgressGetter != null) BarSize = ProgressGetter();
DrawChildren(spriteBatch);
Color currColor = color;
if (state == ComponentState.Selected) currColor = selectedColor;
if (state == ComponentState.Hover) currColor = hoverColor;
if (slider.sprites != null && slider.sprites[state].Count > 0)
{
foreach (UISprite uiSprite in slider.sprites[state])
{
if (uiSprite.Tile)
{
uiSprite.Sprite.DrawTiled(spriteBatch, slider.Rect.Location.ToVector2(), slider.Rect.Size.ToVector2(), currColor);
}
else
{
spriteBatch.Draw(uiSprite.Sprite.Texture,
slider.Rect, new Rectangle(
uiSprite.Sprite.SourceRect.X,
uiSprite.Sprite.SourceRect.Y,
(int)(uiSprite.Sprite.SourceRect.Width * (isHorizontal ? barSize : 1.0f)),
(int)(uiSprite.Sprite.SourceRect.Height * (isHorizontal ? 1.0f : barSize))),
currColor);
}
}
}
}
}
}
+218
View File
@@ -0,0 +1,218 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
{
public class GUIScrollBar : GUIComponent
{
public static GUIScrollBar draggingBar;
private bool isHorizontal;
private GUIFrame frame;
private GUIButton bar;
private float barSize;
private float barScroll;
private float step;
private bool enabled;
public delegate bool OnMovedHandler(GUIScrollBar scrollBar, float barScroll);
public OnMovedHandler OnMoved;
public bool IsHorizontal
{
get { return isHorizontal; }
set
{
if (isHorizontal == value) return;
isHorizontal = value;
UpdateRect();
}
}
public bool Enabled
{
get { return enabled; }
set { enabled = value; }
}
public float BarScroll
{
get { return step == 0.0f ? barScroll : MathUtils.RoundTowardsClosest(barScroll, step); }
set
{
barScroll = MathHelper.Clamp(value, 0.0f, 1.0f);
int newX = bar.Rect.X - frame.Rect.X;
int newY = bar.Rect.Y - frame.Rect.Y;
float newScroll = step == 0.0f ? barScroll : MathUtils.RoundTowardsClosest(barScroll, step);
if (isHorizontal)
{
newX = (int)(frame.Padding.X + newScroll * (frame.Rect.Width - bar.Rect.Width - frame.Padding.X - frame.Padding.Z));
newX = MathHelper.Clamp(newX, (int)frame.Padding.X, frame.Rect.Width - bar.Rect.Width - (int)frame.Padding.Z);
}
else
{
newY = (int)(frame.Padding.Y + newScroll * (frame.Rect.Height - bar.Rect.Height - frame.Padding.Y - frame.Padding.W));
newY = MathHelper.Clamp(newY, (int)frame.Padding.Y, frame.Rect.Height - bar.Rect.Height - (int)frame.Padding.W);
}
bar.Rect = new Rectangle(newX + frame.Rect.X, newY + frame.Rect.Y, bar.Rect.Width, bar.Rect.Height);
}
}
public float Step
{
get
{
return step;
}
set
{
step = MathHelper.Clamp(value, 0.0f, 1.0f);
}
}
public float BarSize
{
get { return barSize; }
set
{
float oldBarSize = barSize;
barSize = Math.Min(Math.Max(value, 0.0f), 1.0f);
if (barSize != oldBarSize) UpdateRect();
}
}
public GUIScrollBar(Rectangle rect, string style, float barSize, GUIComponent parent = null)
: this(rect, null, barSize, style, parent)
{
}
public GUIScrollBar(Rectangle rect, Color? color, float barSize, string style = "", GUIComponent parent = null)
: this(rect, color, barSize, Alignment.TopLeft, style, parent)
{
}
public GUIScrollBar(Rectangle rect, Color? color, float barSize, Alignment alignment, string style = "", GUIComponent parent = null)
: base(style)
{
this.rect = rect;
//GetDimensions(parent);
this.alignment = alignment;
if (parent != null)
parent.AddChild(this);
isHorizontal = (rect.Width > rect.Height);
frame = new GUIFrame(new Rectangle(0,0,0,0), style, this);
GUI.Style.Apply(frame, isHorizontal ? "GUIFrameHorizontal" : "GUIFrameVertical", this);
this.barSize = barSize;
bar = new GUIButton(new Rectangle(0, 0, 0, 0), "", color, "", this);
GUI.Style.Apply(bar, isHorizontal ? "GUIButtonHorizontal" : "GUIButtoneVertical", this);
bar.OnPressed = SelectBar;
enabled = true;
UpdateRect();
}
private void UpdateRect()
{
float width = frame.Rect.Width - frame.Padding.X - frame.Padding.Z;
float height = frame.Rect.Height - frame.Padding.Y - frame.Padding.W;
bar.Rect = new Rectangle(
bar.Rect.X,
bar.Rect.Y,
isHorizontal ? (int)(width * barSize) : (int)width,
isHorizontal ? (int)height : (int)(height * barSize));
ClampRect();
foreach (GUIComponent child in bar.children)
{
child.Rect = bar.Rect;
}
}
private void ClampRect()
{
bar.Rect = new Rectangle(
(int)MathHelper.Clamp(bar.Rect.X, frame.Rect.X + frame.Padding.X, frame.Rect.Right - bar.Rect.Width - frame.Padding.X - frame.Padding.Z),
(int)MathHelper.Clamp(bar.Rect.Y, frame.Rect.Y + frame.Padding.Y, frame.Rect.Bottom - bar.Rect.Height - frame.Padding.Y - frame.Padding.W),
bar.Rect.Width,
bar.Rect.Height);
}
public override void Update(float deltaTime)
{
if (!Visible) return;
base.Update(deltaTime);
if (MouseOn == frame)
{
if (PlayerInput.LeftButtonClicked())
{
MoveButton(new Vector2(
Math.Sign(PlayerInput.MousePosition.X - bar.Rect.Center.X) * bar.Rect.Width,
Math.Sign(PlayerInput.MousePosition.Y - bar.Rect.Center.Y) * bar.Rect.Height));
}
}
if (draggingBar == this)
{
if (!PlayerInput.LeftButtonHeld()) draggingBar = null;
MoveButton(PlayerInput.MouseSpeed);
}
}
public override void Draw(SpriteBatch spriteBatch)
{
if (!Visible) return;
DrawChildren(spriteBatch);
}
private bool SelectBar()
{
if (!enabled) return false;
if (barSize == 1.0f) return false;
draggingBar = this;
return true;
}
private void MoveButton(Vector2 moveAmount)
{
if (isHorizontal)
{
moveAmount.Y = 0.0f;
barScroll += moveAmount.X / (frame.Rect.Width - bar.Rect.Width - frame.Padding.X - frame.Padding.Z);
}
else
{
moveAmount.X = 0.0f;
barScroll += moveAmount.Y / (frame.Rect.Height - bar.Rect.Height - frame.Padding.Y - frame.Padding.W);
}
BarScroll = barScroll;
if (moveAmount != Vector2.Zero && OnMoved != null) OnMoved(this, BarScroll);
}
}
}
+73
View File
@@ -0,0 +1,73 @@
using System.Xml.Linq;
using System;
using System.Collections.Generic;
namespace Barotrauma
{
public class GUIStyle
{
private Dictionary<string, GUIComponentStyle> componentStyles;
public GUIStyle(string file)
{
componentStyles = new Dictionary<string, GUIComponentStyle>();
XDocument doc;
try
{
ToolBox.IsProperFilenameCase(file);
doc = XDocument.Load(file);
}
catch (Exception e)
{
DebugConsole.ThrowError("Loading style \"" + file + "\" failed", e);
return;
}
foreach (XElement subElement in doc.Root.Elements())
{
GUIComponentStyle componentStyle = new GUIComponentStyle(subElement);
componentStyles.Add(subElement.Name.ToString().ToLowerInvariant(), componentStyle);
}
}
public void Apply(GUIComponent targetComponent, string styleName = "", GUIComponent parent = null)
{
GUIComponentStyle componentStyle = null;
if (parent != null)
{
GUIComponentStyle parentStyle = parent.Style;
if (parent.Style == null)
{
string parentStyleName = parent.GetType().Name.ToLowerInvariant();
if (!componentStyles.TryGetValue(parentStyleName, out parentStyle))
{
DebugConsole.ThrowError("Couldn't find a GUI style \""+ parentStyleName + "\"");
return;
}
}
string childStyleName = string.IsNullOrEmpty(styleName) ? targetComponent.GetType().Name : styleName;
parentStyle.ChildStyles.TryGetValue(childStyleName.ToLowerInvariant(), out componentStyle);
}
else
{
if (string.IsNullOrEmpty(styleName))
{
styleName = targetComponent.GetType().Name;
}
if (!componentStyles.TryGetValue(styleName.ToLowerInvariant(), out componentStyle))
{
DebugConsole.ThrowError("Couldn't find a GUI style \""+ styleName+"\"");
return;
}
}
targetComponent.ApplyStyle(componentStyle);
}
}
}
+308
View File
@@ -0,0 +1,308 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
namespace Barotrauma
{
public class GUITextBlock : GUIComponent
{
protected string text;
protected Alignment textAlignment;
private float textScale;
protected Vector2 textPos;
protected Vector2 origin;
protected Vector2 caretPos;
protected Color textColor;
private string wrappedText;
public delegate string TextGetterHandler();
public TextGetterHandler TextGetter;
public bool Wrap;
private bool overflowClipActive;
public bool OverflowClip;
private float textDepth;
public override Vector4 Padding
{
get { return padding; }
set
{
padding = value;
SetTextPos();
}
}
public string Text
{
get { return text; }
set
{
if (Text == value) return;
text = value;
wrappedText = value;
SetTextPos();
}
}
public string WrappedText
{
get { return wrappedText; }
}
public override Rectangle Rect
{
get
{
return base.Rect;
}
set
{
if (base.Rect == value) return;
foreach (GUIComponent child in children)
{
child.Rect = new Rectangle(child.Rect.X + value.X - rect.X, child.Rect.Y + value.Y - rect.Y, child.Rect.Width, child.Rect.Height);
}
if (value.Width != rect.Width || value.Height != rect.Height)
{
SetTextPos();
}
rect = value;
}
}
public float TextDepth
{
get { return textDepth; }
set { textDepth = MathHelper.Clamp(value, 0.0f, 1.0f); }
}
public Vector2 TextPos
{
get { return textPos; }
}
public float TextScale
{
get { return textScale; }
set
{
if (value != textScale)
{
textScale = value;
SetTextPos();
}
}
}
public Vector2 Origin
{
get { return origin; }
}
public Color TextColor
{
get { return textColor; }
set { textColor = value; }
}
public Vector2 CaretPos
{
get { return caretPos; }
}
public GUITextBlock(Rectangle rect, string text, string style, GUIComponent parent, ScalableFont font)
: this(rect, text, style, Alignment.TopLeft, Alignment.TopLeft, parent, false, font)
{
}
public GUITextBlock(Rectangle rect, string text, string style, GUIComponent parent = null, bool wrap = false)
: this(rect, text, style, Alignment.TopLeft, Alignment.TopLeft, parent, wrap)
{
}
public GUITextBlock(Rectangle rect, string text, Color? color, Color? textColor, Alignment textAlignment = Alignment.Left, string style = null, GUIComponent parent = null, bool wrap = false)
: this(rect, text,color, textColor, Alignment.TopLeft, textAlignment, style, parent, wrap)
{
}
protected override void UpdateDimensions(GUIComponent parent = null)
{
base.UpdateDimensions(parent);
SetTextPos();
}
public override void ApplyStyle(GUIComponentStyle style)
{
if (style == null) return;
base.ApplyStyle(style);
textColor = style.textColor;
}
public GUITextBlock(Rectangle rect, string text, Color? color, Color? textColor, Alignment alignment, Alignment textAlignment = Alignment.Left, string style = null, GUIComponent parent = null, bool wrap = false, ScalableFont font = null)
: this (rect, text, style, alignment, textAlignment, parent, wrap, font)
{
if (color != null) this.color = (Color)color;
if (textColor != null) this.textColor = (Color)textColor;
}
public GUITextBlock(Rectangle rect, string text, string style, Alignment alignment = Alignment.TopLeft, Alignment textAlignment = Alignment.TopLeft, GUIComponent parent = null, bool wrap = false, ScalableFont font = null)
: base(style)
{
this.Font = font == null ? GUI.Font : font;
this.rect = rect;
this.text = text;
this.alignment = alignment;
this.padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f);
this.textAlignment = textAlignment;
if (parent != null)
parent.AddChild(this);
this.Wrap = wrap;
SetTextPos();
TextScale = 1.0f;
if (rect.Height == 0 && !string.IsNullOrEmpty(Text))
{
this.rect.Height = (int)Font.MeasureString(wrappedText).Y;
}
}
public void SetTextPos()
{
if (text == null) return;
overflowClipActive = false;
wrappedText = text;
Vector2 size = MeasureText(text);
if (Wrap && rect.Width > 0)
{
wrappedText = ToolBox.WrapText(text, rect.Width - padding.X - padding.Z, Font, textScale);
size = MeasureText(wrappedText);
}
else if (OverflowClip)
{
overflowClipActive = size.X > rect.Width;
}
textPos = new Vector2(rect.Width / 2.0f, rect.Height / 2.0f);
origin = size * 0.5f;
if (textAlignment.HasFlag(Alignment.Left) && !overflowClipActive)
origin.X += (rect.Width / 2.0f - padding.X) - size.X / 2;
if (textAlignment.HasFlag(Alignment.Right) || overflowClipActive)
origin.X -= (rect.Width / 2.0f - padding.Z) - size.X / 2;
if (textAlignment.HasFlag(Alignment.Top))
origin.Y += (rect.Height / 2.0f - padding.Y) - size.Y / 2;
if (textAlignment.HasFlag(Alignment.Bottom))
origin.Y -= (rect.Height / 2.0f - padding.W) - size.Y / 2;
origin.X = (int)origin.X;
origin.Y = (int)origin.Y;
textPos.X = (int)textPos.X;
textPos.Y = (int)textPos.Y;
if (wrappedText.Contains("\n"))
{
string[] lines = wrappedText.Split('\n');
Vector2 lastLineSize = MeasureText(lines[lines.Length-1]);
caretPos = new Vector2(rect.X + lastLineSize.X, rect.Y + size.Y - lastLineSize.Y) + textPos - origin;
}
else
{
caretPos = new Vector2(rect.X + size.X, rect.Y) + textPos - origin;
}
}
private Vector2 MeasureText(string text)
{
if (Font == null) return Vector2.Zero;
Vector2 size = Vector2.Zero;
while (size == Vector2.Zero)
{
try { size = Font.MeasureString((text == "") ? " " : text); }
catch { text = text.Substring(0, text.Length - 1); }
}
return size;
}
public override void Draw(SpriteBatch spriteBatch)
{
Draw(spriteBatch, Vector2.Zero);
}
public void Draw(SpriteBatch spriteBatch, Vector2 offset)
{
if (!Visible) return;
Color currColor = color;
if (state == ComponentState.Hover) currColor = hoverColor;
if (state == ComponentState.Selected) currColor = selectedColor;
Rectangle drawRect = rect;
if (offset != Vector2.Zero) drawRect.Location += offset.ToPoint();
base.Draw(spriteBatch);
if (TextGetter != null) Text = TextGetter();
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
if (overflowClipActive)
{
spriteBatch.GraphicsDevice.ScissorRectangle = rect;
}
if (!string.IsNullOrEmpty(text))
{
Font.DrawString(spriteBatch,
Wrap ? wrappedText : text,
new Vector2(rect.X, rect.Y) + textPos + offset,
textColor * (textColor.A / 255.0f),
0.0f, origin, TextScale,
SpriteEffects.None, textDepth);
}
if (overflowClipActive)
{
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
}
DrawChildren(spriteBatch);
if (OutlineColor.A * currColor.A > 0.0f) GUI.DrawRectangle(spriteBatch, rect, OutlineColor * (currColor.A / 255.0f), false);
}
}
}
+335
View File
@@ -0,0 +1,335 @@
using System;
using EventInput;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma
{
delegate void TextBoxEvent(GUITextBox sender, Keys key);
class GUITextBox : GUIComponent, IKeyboardSubscriber
{
public event TextBoxEvent OnSelected;
bool caretVisible;
float caretTimer;
GUITextBlock textBlock;
public delegate bool OnEnterHandler(GUITextBox textBox, string text);
public OnEnterHandler OnEnterPressed;
public event TextBoxEvent OnKeyHit;
public delegate bool OnTextChangedHandler(GUITextBox textBox, string text);
public OnTextChangedHandler OnTextChanged;
public bool CaretEnabled;
private int? maxTextLength;
public GUITextBlock.TextGetterHandler TextGetter
{
get { return textBlock.TextGetter; }
set { textBlock.TextGetter = value; }
}
public bool Wrap
{
get { return textBlock.Wrap; }
set { textBlock.Wrap = value; }
}
public int? MaxTextLength
{
get { return maxTextLength; }
set
{
textBlock.OverflowClip = true;
maxTextLength = value;
}
}
public bool Enabled
{
get;
set;
}
public override string ToolTip
{
get
{
return base.ToolTip;
}
set
{
base.ToolTip = value;
textBlock.ToolTip = value;
}
}
public override ScalableFont Font
{
set
{
base.Font = value;
if (textBlock == null) return;
textBlock.Font = value;
}
}
public override Color Color
{
get { return color; }
set
{
color = value;
textBlock.Color = color;
}
}
public Color TextColor
{
get { return textBlock.TextColor; }
set { textBlock.TextColor = value; }
}
public override Color HoverColor
{
get
{
return base.HoverColor;
}
set
{
base.HoverColor = value;
textBlock.HoverColor = value;
}
}
public override Rectangle Rect
{
get
{
return base.Rect;
}
set
{
base.Rect = value;
textBlock.Rect = value;
}
}
public string Text
{
get
{
return textBlock.Text;
}
set
{
if (textBlock.Text == value) return;
textBlock.Text = value;
if (textBlock.Text == null) textBlock.Text = "";
if (textBlock.Text != "")
{
if (!Wrap)
{
if (maxTextLength != null)
{
if (Text.Length > maxTextLength)
{
Text = textBlock.Text.Substring(0, (int)maxTextLength);
}
}
else if (Font.MeasureString(textBlock.Text).X > (int)(textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z))
{
Text = textBlock.Text.Substring(0, textBlock.Text.Length - 1);
}
}
}
}
}
public GUITextBox(Rectangle rect, string style = null, GUIComponent parent = null)
: this(rect, null, null, Alignment.Left, Alignment.Left, style, parent)
{
}
public GUITextBox(Rectangle rect, Alignment alignment = Alignment.Left, string style = null, GUIComponent parent = null)
: this(rect, null, null, alignment, Alignment.Left, style, parent)
{
}
public GUITextBox(Rectangle rect, Color? color, Color? textColor, Alignment alignment, Alignment textAlignment = Alignment.CenterLeft, string style = null, GUIComponent parent = null)
: base(style)
{
Enabled = true;
this.rect = rect;
if (color != null) this.color = (Color)color;
this.alignment = alignment;
if (parent != null)
parent.AddChild(this);
textBlock = new GUITextBlock(new Rectangle(0,0,0,0), "", color, textColor, textAlignment, style, this);
Font = GUI.Font;
GUI.Style.Apply(textBlock, style == "" ? "GUITextBox" : style);
textBlock.Padding = new Vector4(3.0f, 0.0f, 3.0f, 0.0f);
CaretEnabled = true;
}
public void Select()
{
Selected = true;
keyboardDispatcher.Subscriber = this;
//if (Clicked != null) Clicked(this);
}
public void Deselect()
{
Selected = false;
if (keyboardDispatcher.Subscriber == this) keyboardDispatcher.Subscriber = null;
}
public override void Flash(Color? color = null)
{
textBlock.Flash(color);
}
//MouseState previousMouse;
public override void Update(float deltaTime)
{
if (!Visible) return;
if (flashTimer > 0.0f) flashTimer -= deltaTime;
if (!Enabled) return;
if (rect.Contains(PlayerInput.MousePosition) && Enabled &&
(MouseOn == null || MouseOn == this || IsParentOf(MouseOn) || MouseOn.IsParentOf(this)))
{
state = ComponentState.Hover;
if (PlayerInput.LeftButtonClicked())
{
Select();
if (OnSelected != null) OnSelected(this, Keys.None);
}
}
else
{
state = ComponentState.None;
}
if (CaretEnabled)
{
caretTimer += deltaTime;
caretVisible = ((caretTimer * 1000.0f) % 1000) < 500;
}
if (keyboardDispatcher.Subscriber == this)
{
state = ComponentState.Selected;
Character.DisableControls = true;
if (OnEnterPressed != null && PlayerInput.KeyHit(Keys.Enter))
{
string input = Text;
Text = "";
OnEnterPressed(this, input);
}
#if LINUX
else if (PlayerInput.KeyHit(Keys.Back) && Text.Length>0)
{
Text = Text.Substring(0, Text.Length-1);
}
#endif
}
textBlock.State = state;
textBlock.Update(deltaTime);
}
public override void Draw(SpriteBatch spriteBatch)
{
if (!Visible) return;
DrawChildren(spriteBatch);
if (!CaretEnabled) return;
Vector2 caretPos = textBlock.CaretPos;
if (caretVisible && Selected)
{
GUI.DrawLine(spriteBatch,
new Vector2((int)caretPos.X + 2, caretPos.Y + 3),
new Vector2((int)caretPos.X + 2, caretPos.Y + Font.MeasureString("I").Y - 3),
textBlock.TextColor * (textBlock.TextColor.A / 255.0f));
}
}
public void ReceiveTextInput(char inputChar)
{
Text = Text + inputChar;
if (OnTextChanged!=null) OnTextChanged(this, Text);
}
public void ReceiveTextInput(string text)
{
Text = Text + text;
if (OnTextChanged != null) OnTextChanged(this, Text);
}
public void ReceiveCommandInput(char command)
{
if (Text == null) Text = "";
switch (command)
{
case '\b': //backspace
if (Text.Length > 0) Text = Text.Substring(0, Text.Length - 1);
break;
//case '\r': //return
// if (OnEnterPressed != null)
// OnEnterPressed(this);
// break;
//case '\t': //tab
// if (OnTabPressed != null)
// OnTabPressed(this);
// break;
}
if (OnTextChanged != null) OnTextChanged(this, Text);
}
public void ReceiveSpecialInput(Keys key)
{
if (OnKeyHit != null) OnKeyHit(this, key);
}
//public event TextBoxEvent OnTabPressed;
public bool Selected
{
get;
set;
}
}
}
+146
View File
@@ -0,0 +1,146 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
{
public class GUITickBox : GUIComponent
{
private GUIFrame box;
private GUITextBlock text;
public delegate bool OnSelectedHandler(GUITickBox obj);
public OnSelectedHandler OnSelected;
private bool selected;
public bool Selected
{
get { return selected; }
set
{
if (value == selected) return;
selected = value;
state = (selected) ? ComponentState.Selected : ComponentState.None;
box.State = state;
}
}
private bool enabled;
public bool Enabled
{
get
{
return enabled;
}
set
{
enabled = value;
}
}
public override Rectangle Rect
{
get
{
return rect;
}
set
{
base.Rect = value;
box.Rect = new Rectangle(value.X,value.Y,box.Rect.Width,box.Rect.Height);
text.Rect = new Rectangle(box.Rect.Right + 10, box.Rect.Y + 2, 20, box.Rect.Height);
}
}
public Color TextColor
{
get { return text.TextColor; }
set { text.TextColor = value; }
}
public override Rectangle MouseRect
{
get { return box.Rect; }
}
public override ScalableFont Font
{
get
{
return base.Font;
}
set
{
base.Font = value;
if (text != null) text.Font = value;
}
}
public GUITickBox(Rectangle rect, string label, Alignment alignment, GUIComponent parent)
: this(rect, label, alignment, GUI.Font, parent)
{
}
public GUITickBox(Rectangle rect, string label, Alignment alignment, ScalableFont font, GUIComponent parent)
: base(null)
{
if (parent != null)
parent.AddChild(this);
box = new GUIFrame(rect, Color.DarkGray, "", this);
box.HoverColor = Color.Gray;
box.SelectedColor = Color.DarkGray;
box.CanBeFocused = false;
GUI.Style.Apply(box, "GUITickBox");
text = new GUITextBlock(new Rectangle(rect.Right, rect.Y, 20, rect.Height), label, "", Alignment.TopLeft, Alignment.Left | Alignment.CenterY, this, false, font);
GUI.Style.Apply(text, "GUIButtonHorizontal", this);
this.rect = new Rectangle(box.Rect.X, box.Rect.Y, 240, rect.Height);
Enabled = true;
}
public override void Update(float deltaTime)
{
if (!Visible) return;
if (MouseOn == this && Enabled)
{
box.State = ComponentState.Hover;
if (PlayerInput.LeftButtonHeld())
{
box.State = ComponentState.Selected;
}
if (PlayerInput.LeftButtonClicked())
{
Selected = !Selected;
if (OnSelected != null) OnSelected(this);
}
}
else
{
box.State = ComponentState.None;
}
if (selected)
{
box.State = ComponentState.Selected;
}
}
public override void Draw(SpriteBatch spriteBatch)
{
if (!Visible) return;
DrawChildren(spriteBatch);
}
}
}
@@ -0,0 +1,244 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace Barotrauma
{
class LoadingScreen
{
private Texture2D backgroundTexture,monsterTexture,titleTexture;
readonly RenderTarget2D renderTarget;
float state;
public Vector2 CenterPosition;
public Vector2 TitlePosition;
private float? loadState;
#if !LINUX
Video splashScreenVideo;
VideoPlayer videoPlayer;
#endif
public Vector2 TitleSize
{
get { return new Vector2(titleTexture.Width, titleTexture.Height); }
}
public float Scale
{
get;
private set;
}
public float? LoadState
{
get { return loadState; }
set
{
loadState = value;
DrawLoadingText = true;
}
}
public bool DrawLoadingText
{
get;
set;
}
public LoadingScreen(GraphicsDevice graphics)
{
#if !LINUX
if (GameMain.Config.EnableSplashScreen)
{
try
{
splashScreenVideo = GameMain.Instance.Content.Load<Video>("utg_4");
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to load splashscreen", e);
GameMain.Config.EnableSplashScreen = false;
}
}
#endif
backgroundTexture = TextureLoader.FromFile("Content/UI/titleBackground.png");
monsterTexture = TextureLoader.FromFile("Content/UI/titleMonster.png");
titleTexture = TextureLoader.FromFile("Content/UI/titleText.png");
renderTarget = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
DrawLoadingText = true;
}
public void Draw(SpriteBatch spriteBatch, GraphicsDevice graphics, float deltaTime)
{
#if !LINUX
if (GameMain.Config.EnableSplashScreen && splashScreenVideo != null)
{
try
{
DrawSplashScreen(spriteBatch);
if (videoPlayer != null && videoPlayer.State == MediaState.Playing)
return;
}
catch (Exception e)
{
DebugConsole.ThrowError("Playing splash screen video failed", e);
GameMain.Config.EnableSplashScreen = false;
}
}
#endif
drawn = true;
graphics.SetRenderTarget(renderTarget);
Scale = GameMain.GraphicsHeight/1500.0f;
state += deltaTime;
if (DrawLoadingText)
{
CenterPosition = new Vector2(GameMain.GraphicsWidth*0.3f, GameMain.GraphicsHeight/2.0f);
TitlePosition = CenterPosition + new Vector2(-0.0f + (float)Math.Sqrt(state) * 220.0f, 0.0f) * Scale;
TitlePosition.X = Math.Min(TitlePosition.X, (float)GameMain.GraphicsWidth / 2.0f);
}
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
graphics.Clear(Color.Black);
spriteBatch.Draw(backgroundTexture, CenterPosition, null, Color.White * Math.Min(state / 5.0f, 1.0f), 0.0f,
new Vector2(backgroundTexture.Width / 2.0f, backgroundTexture.Height / 2.0f),
Scale*1.5f, SpriteEffects.None, 0.2f);
spriteBatch.Draw(monsterTexture,
CenterPosition + new Vector2((state % 40) * 100.0f - 1800.0f, (state % 40) * 30.0f - 200.0f) * Scale, null,
Color.White, 0.0f, Vector2.Zero, Scale, SpriteEffects.None, 0.1f);
spriteBatch.Draw(titleTexture,
TitlePosition, null,
Color.White * Math.Min((state - 1.0f) / 5.0f, 1.0f), 0.0f, new Vector2(titleTexture.Width / 2.0f, titleTexture.Height / 2.0f), Scale, SpriteEffects.None, 0.0f);
spriteBatch.End();
graphics.SetRenderTarget(null);
if (Hull.renderer != null)
{
Hull.renderer.ScrollWater(deltaTime);
Hull.renderer.RenderBack(spriteBatch, renderTarget, 0.0f);
}
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
spriteBatch.Draw(titleTexture,
TitlePosition, null,
Color.White * Math.Min((state - 3.0f) / 5.0f, 1.0f), 0.0f, new Vector2(titleTexture.Width / 2.0f, titleTexture.Height / 2.0f), Scale, SpriteEffects.None, 0.0f);
if (DrawLoadingText)
{
string loadText = "";
if (loadState == 100.0f)
{
loadText = "Press any key to continue";
}
else
{
loadText = "Loading... ";
if (loadState!=null)
{
loadText += (int)loadState + " %";
}
}
if (GUI.LargeFont!=null)
{
GUI.LargeFont.DrawString(spriteBatch, loadText,
new Vector2(GameMain.GraphicsWidth/2.0f - GUI.LargeFont.MeasureString(loadText).X/2.0f, GameMain.GraphicsHeight*0.8f),
Color.White);
}
}
spriteBatch.End();
}
#if !LINUX
private void DrawSplashScreen(SpriteBatch spriteBatch)
{
if (videoPlayer == null)
{
videoPlayer = new VideoPlayer();
videoPlayer.Play(splashScreenVideo);
videoPlayer.Volume = GameMain.Config.SoundVolume;
}
else
{
Texture2D videoTexture = null;
if (videoPlayer.State == MediaState.Stopped)
{
videoPlayer.Dispose();
videoPlayer = null;
splashScreenVideo.Dispose();
splashScreenVideo = null;
}
else
{
videoTexture = videoPlayer.GetTexture();
spriteBatch.Begin();
spriteBatch.Draw(videoTexture, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
spriteBatch.End();
if (PlayerInput.KeyHit(Keys.Space) || PlayerInput.KeyHit(Keys.Enter) || PlayerInput.LeftButtonDown())
{
videoPlayer.Stop();
}
}
}
}
#endif
bool drawn;
public IEnumerable<object> DoLoading(IEnumerable<object> loader)
{
drawn = false;
LoadState = null;
while (!drawn)
{
yield return CoroutineStatus.Running;
}
CoroutineManager.StartCoroutine(loader);
yield return CoroutineStatus.Running;
while (CoroutineManager.IsCoroutineRunning(loader.ToString()))
{
yield return CoroutineStatus.Running;
}
loadState = 100.0f;
yield return CoroutineStatus.Success;
}
}
}
@@ -0,0 +1,381 @@
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
namespace Barotrauma.Particles
{
class Particle
{
private ParticlePrefab prefab;
public delegate void OnChangeHullHandler(Vector2 position, Hull currentHull);
public OnChangeHullHandler OnChangeHull;
private Vector2 position;
private Vector2 prevPosition;
private Vector2 velocity;
private float rotation;
private float prevRotation;
private float angularVelocity;
private Vector2 size;
private Vector2 sizeChange;
private Color color;
private float alpha;
private int spriteIndex;
private float totalLifeTime;
private float lifeTime;
private Vector2 velocityChange;
private Vector2 drawPosition;
private float drawRotation;
private Hull currentHull;
private List<Gap> hullGaps;
private float animState;
private int animFrame;
public ParticlePrefab.DrawTargetType DrawTarget
{
get { return prefab.DrawTarget; }
}
public ParticleBlendState BlendState
{
get { return prefab.BlendState; }
}
public Vector2 Size
{
get { return size; }
set { size = value; }
}
public Vector2 VelocityChange
{
get { return velocityChange; }
set { velocityChange = value; }
}
public Vector2 Velocity
{
get { return velocity; }
set { velocity = value; }
}
public void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess = null)
{
this.prefab = prefab;
spriteIndex = Rand.Int(prefab.Sprites.Count);
animState = 0;
animFrame = 0;
currentHull = Hull.FindHull(position, hullGuess);
this.position = position;
prevPosition = position;
drawPosition = position;
velocity = MathUtils.IsValid(speed) ? speed : Vector2.Zero;
if (currentHull != null && currentHull.Submarine != null)
{
velocity += ConvertUnits.ToDisplayUnits(currentHull.Submarine.Velocity);
}
this.rotation = rotation + Rand.Range(prefab.StartRotationMin, prefab.StartRotationMax);
prevRotation = rotation;
angularVelocity = prefab.AngularVelocityMin + (prefab.AngularVelocityMax - prefab.AngularVelocityMin) * Rand.Range(0.0f, 1.0f);
totalLifeTime = prefab.LifeTime;
lifeTime = prefab.LifeTime;
size = prefab.StartSizeMin + (prefab.StartSizeMax - prefab.StartSizeMin) * Rand.Range(0.0f, 1.0f);
sizeChange = prefab.SizeChangeMin + (prefab.SizeChangeMax - prefab.SizeChangeMin) * Rand.Range(0.0f, 1.0f);
color = new Color(prefab.StartColor, 1.0f);
alpha = prefab.StartAlpha;
velocityChange = prefab.VelocityChange;
OnChangeHull = null;
if (prefab.DeleteOnCollision || prefab.CollidesWithWalls)
{
hullGaps = currentHull == null ? new List<Gap>() : currentHull.ConnectedGaps;
}
if (prefab.RotateToDirection)
{
this.rotation = MathUtils.VectorToAngle(new Vector2(velocity.X, -velocity.Y));
prevRotation = rotation;
}
}
public bool Update(float deltaTime)
{
prevPosition = position;
prevRotation = rotation;
//over 3 times faster than position += velocity * deltatime
position.X += velocity.X * deltaTime;
position.Y += velocity.Y * deltaTime;
if (prefab.RotateToDirection)
{
if (velocityChange != Vector2.Zero || angularVelocity != 0.0f)
{
rotation = MathUtils.VectorToAngle(new Vector2(velocity.X, -velocity.Y));
}
}
else
{
rotation += angularVelocity * deltaTime;
}
if (prefab.WaterDrag > 0.0f &&
(currentHull == null || (currentHull.Submarine != null && position.Y - currentHull.Submarine.DrawPosition.Y < currentHull.Surface)))
{
ApplyDrag(prefab.WaterDrag, deltaTime);
}
else if (prefab.Drag > 0.0f)
{
ApplyDrag(prefab.Drag, deltaTime);
}
velocity.X += velocityChange.X * deltaTime;
velocity.Y += velocityChange.Y * deltaTime;
size.X += sizeChange.X * deltaTime;
size.Y += sizeChange.Y * deltaTime;
alpha += prefab.ColorChange.W * deltaTime;
color = new Color(
color.R / 255.0f + prefab.ColorChange.X * deltaTime,
color.G / 255.0f + prefab.ColorChange.Y * deltaTime,
color.B / 255.0f + prefab.ColorChange.Z * deltaTime);
if (prefab.Sprites[spriteIndex] is SpriteSheet)
{
animState += deltaTime;
int frameCount = ((SpriteSheet)prefab.Sprites[spriteIndex]).FrameCount;
animFrame = (int)Math.Min(Math.Floor(animState / prefab.AnimDuration * frameCount), frameCount - 1);
}
lifeTime -= deltaTime;
if (lifeTime <= 0.0f || alpha <= 0.0f || size.X <= 0.0f || size.Y <= 0.0f) return false;
if (!prefab.DeleteOnCollision && !prefab.CollidesWithWalls) return true;
if (currentHull == null)
{
Hull collidedHull = Hull.FindHull(position);
if (collidedHull != null)
{
if (prefab.DeleteOnCollision) return false;
OnWallCollisionOutside(collidedHull);
}
}
else
{
Vector2 collisionNormal = Vector2.Zero;
if (velocity.Y < 0.0f && position.Y - prefab.CollisionRadius * size.Y < currentHull.WorldRect.Y - currentHull.WorldRect.Height)
{
if (prefab.DeleteOnCollision) return false;
collisionNormal = new Vector2(0.0f, 1.0f);
}
else if (velocity.Y > 0.0f && position.Y + prefab.CollisionRadius * size.Y > currentHull.WorldRect.Y)
{
if (prefab.DeleteOnCollision) return false;
collisionNormal = new Vector2(0.0f, -1.0f);
}
else if (velocity.X < 0.0f && position.X - prefab.CollisionRadius * size.X < currentHull.WorldRect.X)
{
if (prefab.DeleteOnCollision) return false;
collisionNormal = new Vector2(1.0f, 0.0f);
}
else if (velocity.X > 0.0f && position.X + prefab.CollisionRadius * size.X > currentHull.WorldRect.Right)
{
if (prefab.DeleteOnCollision) return false;
collisionNormal = new Vector2(-1.0f, 0.0f);
}
if (collisionNormal != Vector2.Zero)
{
bool gapFound = false;
foreach (Gap gap in hullGaps)
{
if (gap.isHorizontal != (collisionNormal.X != 0.0f)) continue;
if (gap.isHorizontal)
{
if (gap.WorldRect.Y < position.Y || gap.WorldRect.Y - gap.WorldRect.Height > position.Y) continue;
int gapDir = Math.Sign(gap.WorldRect.Center.X - currentHull.WorldRect.Center.X);
if (Math.Sign(velocity.X) != gapDir || Math.Sign(position.X - currentHull.WorldRect.Center.X) != gapDir) continue;
}
else
{
if (gap.WorldRect.X > position.X || gap.WorldRect.Right < position.X) continue;
float hullCenterY = currentHull.WorldRect.Y - currentHull.WorldRect.Height / 2;
int gapDir = Math.Sign(gap.WorldRect.Y - hullCenterY);
if (Math.Sign(velocity.Y) != gapDir || Math.Sign(position.Y - hullCenterY) != gapDir) continue;
}
gapFound = true;
break;
}
if (!gapFound)
{
OnWallCollisionInside(currentHull, collisionNormal);
}
else
{
Hull newHull = Hull.FindHull(position);
if (newHull != currentHull)
{
currentHull = newHull;
hullGaps = currentHull == null ? new List<Gap>() : currentHull.ConnectedGaps;
OnChangeHull?.Invoke(position, currentHull);
}
}
}
}
return true;
}
private void ApplyDrag(float dragCoefficient, float deltaTime)
{
if (Math.Abs(velocity.X) < 0.0001f && Math.Abs(velocity.Y) < 0.0001f) return;
float speed = velocity.Length();
velocity -= (velocity / speed) * Math.Min(speed * speed * dragCoefficient * deltaTime, 1.0f);
}
private void OnWallCollisionInside(Hull prevHull, Vector2 collisionNormal)
{
Rectangle prevHullRect = prevHull.WorldRect;
Vector2 subVel = ConvertUnits.ToDisplayUnits(prevHull.Submarine.Velocity);
velocity -= subVel;
if (Math.Abs(collisionNormal.X) > Math.Abs(collisionNormal.Y))
{
if (collisionNormal.X > 0.0f)
{
position.X = Math.Max(position.X, prevHullRect.X + prefab.CollisionRadius * size.X);
}
else
{
position.X = Math.Min(position.X, prevHullRect.Right - prefab.CollisionRadius * size.X);
}
velocity.X = Math.Sign(collisionNormal.X) * Math.Abs(velocity.X) * prefab.Restitution;
velocity.Y *= (1.0f - prefab.Friction);
}
else
{
if (collisionNormal.Y > 0.0f)
{
position.Y = Math.Max(position.Y, prevHullRect.Y - prevHullRect.Height + prefab.CollisionRadius * size.Y);
}
else
{
position.Y = Math.Min(position.Y, prevHullRect.Y - prefab.CollisionRadius * size.Y);
}
velocity.X *= (1.0f - prefab.Friction);
velocity.Y = Math.Sign(collisionNormal.Y) * Math.Abs(velocity.Y) * prefab.Restitution;
}
velocity += subVel;
}
private void OnWallCollisionOutside(Hull collisionHull)
{
Rectangle hullRect = collisionHull.WorldRect;
if (position.Y < hullRect.Y - hullRect.Height)
{
position.Y = hullRect.Y - hullRect.Height - prefab.CollisionRadius;
velocity.Y = -velocity.Y;
}
else if (position.Y > hullRect.Y)
{
position.Y = hullRect.Y + prefab.CollisionRadius;
velocity.X = Math.Abs(velocity.Y) * Math.Sign(velocity.X);
velocity.Y = -velocity.Y;
}
if (position.X < hullRect.X)
{
position.X = hullRect.X - prefab.CollisionRadius;
velocity.X = -velocity.X;
}
else if (position.X > hullRect.X + hullRect.Width)
{
position.X = hullRect.X + hullRect.Width + prefab.CollisionRadius;
velocity.X = -velocity.X;
}
velocity *= prefab.Restitution;
}
public void UpdateDrawPos()
{
drawPosition = Timing.Interpolate(prevPosition, position);
drawRotation = Timing.Interpolate(prevRotation, rotation);
prevPosition = position;
prevRotation = rotation;
}
public void Draw(SpriteBatch spriteBatch)
{
Vector2 drawSize = size;
if (prefab.GrowTime > 0.0f && totalLifeTime - lifeTime < prefab.GrowTime)
{
drawSize *= ((totalLifeTime - lifeTime) / prefab.GrowTime);
}
if (prefab.Sprites[spriteIndex] is SpriteSheet)
{
((SpriteSheet)prefab.Sprites[spriteIndex]).Draw(
spriteBatch, animFrame,
new Vector2(drawPosition.X, -drawPosition.Y),
color * alpha,
prefab.Sprites[spriteIndex].Origin, drawRotation,
drawSize, SpriteEffects.None, prefab.Sprites[spriteIndex].Depth);
}
else
{
prefab.Sprites[spriteIndex].Draw(spriteBatch,
new Vector2(drawPosition.X, -drawPosition.Y),
color * alpha,
prefab.Sprites[spriteIndex].Origin, drawRotation,
drawSize, SpriteEffects.None, prefab.Sprites[spriteIndex].Depth);
}
}
}
}
@@ -0,0 +1,83 @@
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using FarseerPhysics;
using System;
namespace Barotrauma.Particles
{
class ParticleEmitterPrefab
{
public readonly string Name;
public readonly ParticlePrefab particlePrefab;
public readonly float AngleMin, AngleMax;
public readonly float VelocityMin, VelocityMax;
public readonly float ScaleMin, ScaleMax;
public readonly float ParticleAmount;
public ParticleEmitterPrefab(XElement element)
{
Name = element.Name.ToString();
particlePrefab = GameMain.ParticleManager.FindPrefab(ToolBox.GetAttributeString(element, "particle", ""));
if (element.Attribute("startrotation") == null)
{
AngleMin = ToolBox.GetAttributeFloat(element, "anglemin", 0.0f);
AngleMax = ToolBox.GetAttributeFloat(element, "anglemax", 0.0f);
}
else
{
AngleMin = ToolBox.GetAttributeFloat(element, "angle", 0.0f);
AngleMax = AngleMin;
}
AngleMin = MathHelper.ToRadians(AngleMin);
AngleMax = MathHelper.ToRadians(AngleMax);
if (element.Attribute("scalemin")==null)
{
ScaleMin = 1.0f;
ScaleMax = 1.0f;
}
else
{
ScaleMin = ToolBox.GetAttributeFloat(element,"scalemin",1.0f);
ScaleMax = Math.Max(ScaleMin, ToolBox.GetAttributeFloat(element, "scalemax", 1.0f));
}
if (element.Attribute("velocity") == null)
{
VelocityMin = ToolBox.GetAttributeFloat(element, "velocitymin", 0.0f);
VelocityMax = ToolBox.GetAttributeFloat(element, "velocitymax", 0.0f);
}
else
{
VelocityMin = ToolBox.GetAttributeFloat(element, "velocity", 0.0f);
VelocityMax = VelocityMin;
}
ParticleAmount = ToolBox.GetAttributeInt(element, "particleamount", 1);
}
public void Emit(Vector2 position, Hull hullGuess = null)
{
for (int i = 0; i<ParticleAmount; i++)
{
float angle = Rand.Range(AngleMin, AngleMax);
Vector2 velocity = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * Rand.Range(VelocityMin, VelocityMax);
var particle = GameMain.ParticleManager.CreateParticle(particlePrefab, position, velocity, 0.0f, hullGuess);
if (particle!=null)
{
particle.Size *= Rand.Range(ScaleMin, ScaleMax);
}
}
}
}
}
@@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.Particles
{
enum ParticleBlendState
{
AlphaBlend, Additive, Distortion
}
class ParticleManager
{
public static int particleCount;
private const int MaxOutOfViewDist = 500;
private const int MaxParticles = 1500;
private Particle[] particles;
private Dictionary<string, ParticlePrefab> prefabs;
Camera cam;
public ParticleManager(string configFile, Camera cam)
{
this.cam = cam;
particles = new Particle[MaxParticles];
XDocument doc = ToolBox.TryLoadXml(configFile);
if (doc == null || doc.Root == null) return;
prefabs = new Dictionary<string, ParticlePrefab>();
foreach (XElement element in doc.Root.Elements())
{
if (prefabs.ContainsKey(element.Name.ToString()))
{
DebugConsole.ThrowError("Error in " + configFile + "! Each particle prefab must have a unique name.");
continue;
}
prefabs.Add(element.Name.ToString(), new ParticlePrefab(element));
}
}
public Particle CreateParticle(string prefabName, Vector2 position, float angle, float speed, Hull hullGuess = null)
{
return CreateParticle(prefabName, position, new Vector2((float)Math.Cos(angle), (float)-Math.Sin(angle)) * speed, angle, hullGuess);
}
public Particle CreateParticle(string prefabName, Vector2 position, Vector2 speed, float rotation=0.0f, Hull hullGuess = null)
{
ParticlePrefab prefab = FindPrefab(prefabName);
if (prefab == null)
{
DebugConsole.ThrowError("Particle prefab \"" + prefabName+"\" not found!");
return null;
}
return CreateParticle(prefab, position, speed, rotation, hullGuess);
}
public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation = 0.0f, Hull hullGuess = null)
{
if (!Submarine.RectContains(MathUtils.ExpandRect(cam.WorldView, MaxOutOfViewDist), position)) return null;
//if (!cam.WorldView.Contains(position)) return null;
if (particleCount >= MaxParticles) return null;
if (particles[particleCount] == null) particles[particleCount] = new Particle();
particles[particleCount].Init(prefab, position, speed, rotation, hullGuess);
particleCount++;
return particles[particleCount-1];
}
public ParticlePrefab FindPrefab(string prefabName)
{
ParticlePrefab prefab;
prefabs.TryGetValue(prefabName, out prefab);
if (prefab == null)
{
DebugConsole.ThrowError("Particle prefab " + prefabName + " not found!");
return null;
}
return prefab;
}
private void RemoveParticle(int index)
{
particleCount--;
Particle swap = particles[index];
particles[index] = particles[particleCount];
particles[particleCount] = swap;
}
public void Update(float deltaTime)
{
for (int i = 0; i < particleCount; i++)
{
bool remove = false;
try
{
remove = !particles[i].Update(deltaTime);
}
catch (Exception e)
{
DebugConsole.ThrowError("Particle update failed", e);
remove = true;
}
if (remove) RemoveParticle(i);
}
}
public void UpdateTransforms()
{
for (int i = 0; i < particleCount; i++)
{
particles[i].UpdateDrawPos();
}
}
public void Draw(SpriteBatch spriteBatch, bool inWater, ParticleBlendState blendState)
{
ParticlePrefab.DrawTargetType drawTarget = inWater ? ParticlePrefab.DrawTargetType.Water : ParticlePrefab.DrawTargetType.Air;
for (int i = 0; i < particleCount; i++)
{
if (particles[i].BlendState != blendState) continue;
if (!particles[i].DrawTarget.HasFlag(drawTarget)) continue;
particles[i].Draw(spriteBatch);
}
}
}
}
@@ -0,0 +1,181 @@
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using FarseerPhysics;
using System.Collections.Generic;
namespace Barotrauma.Particles
{
class ParticlePrefab
{
public enum DrawTargetType { Air = 1, Water = 2, Both = 3 }
public readonly string Name;
public readonly List<Sprite> Sprites;
public readonly float AnimDuration;
public readonly bool LoopAnim;
public readonly float AngularVelocityMin, AngularVelocityMax;
public readonly float StartRotationMin, StartRotationMax;
public readonly Vector2 StartSizeMin, StartSizeMax;
public readonly Vector2 SizeChangeMin, SizeChangeMax;
public readonly float Drag, WaterDrag;
public readonly Color StartColor;
public readonly float StartAlpha;
public readonly Vector4 ColorChange;
public readonly float LifeTime;
public readonly float GrowTime;
public readonly float CollisionRadius;
public readonly bool DeleteOnCollision;
public readonly bool CollidesWithWalls;
public readonly float Friction;
public readonly float Restitution;
public readonly Vector2 VelocityChange;
public readonly DrawTargetType DrawTarget;
public readonly ParticleBlendState BlendState;
public readonly bool RotateToDirection;
public ParticlePrefab(XElement element)
{
Name = element.Name.ToString();
Sprites = new List<Sprite>();
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "sprite":
Sprites.Add(new Sprite(subElement));
break;
case "spritesheet":
case "animatedsprite":
Sprites.Add(new SpriteSheet(subElement));
break;
}
}
AnimDuration = ToolBox.GetAttributeFloat(element, "animduration", 1.0f);
LoopAnim = ToolBox.GetAttributeBool(element, "loopanim", true);
if (element.Attribute("angularvelocity") == null)
{
AngularVelocityMin = ToolBox.GetAttributeFloat(element, "angularvelocitymin", 0.0f);
AngularVelocityMax = ToolBox.GetAttributeFloat(element, "angularvelocitymax", 0.0f);
}
else
{
AngularVelocityMin = ToolBox.GetAttributeFloat(element, "angularvelocity", 0.0f);
AngularVelocityMax = AngularVelocityMin;
}
if (element.Attribute("startsize") == null)
{
StartSizeMin = ToolBox.GetAttributeVector2(element, "startsizemin", Vector2.One);
StartSizeMax = ToolBox.GetAttributeVector2(element, "startsizemax", Vector2.One);
}
else
{
StartSizeMin = ToolBox.GetAttributeVector2(element, "startsize", Vector2.One);
StartSizeMax = StartSizeMin;
}
if (element.Attribute("sizechange") == null)
{
SizeChangeMin = ToolBox.GetAttributeVector2(element, "sizechangemin", Vector2.Zero);
SizeChangeMax = ToolBox.GetAttributeVector2(element, "sizechangemax", Vector2.Zero);
}
else
{
SizeChangeMin = ToolBox.GetAttributeVector2(element, "sizechange", Vector2.Zero);
SizeChangeMax = SizeChangeMin;
}
Drag = ToolBox.GetAttributeFloat(element, "drag", 0.0f);
WaterDrag = ToolBox.GetAttributeFloat(element, "waterdrag", 0.0f);
Friction = ToolBox.GetAttributeFloat(element, "friction", 0.5f);
Restitution = ToolBox.GetAttributeFloat(element, "restitution", 0.5f);
switch (ToolBox.GetAttributeString(element, "blendstate", "alphablend"))
{
case "alpha":
case "alphablend":
BlendState = ParticleBlendState.AlphaBlend;
break;
case "add":
case "additive":
BlendState = ParticleBlendState.Additive;
break;
case "distort":
case "distortion":
BlendState = ParticleBlendState.Distortion;
break;
}
GrowTime = ToolBox.GetAttributeFloat(element, "growtime", 0.0f);
if (element.Attribute("startrotation") == null)
{
StartRotationMin = ToolBox.GetAttributeFloat(element, "startrotationmin", 0.0f);
StartRotationMax = ToolBox.GetAttributeFloat(element, "startrotationmax", 0.0f);
}
else
{
StartRotationMin = ToolBox.GetAttributeFloat(element, "startrotation", 0.0f);
StartRotationMax = StartRotationMin;
}
StartRotationMin = MathHelper.ToRadians(StartRotationMin);
StartRotationMax = MathHelper.ToRadians(StartRotationMax);
StartColor = new Color(ToolBox.GetAttributeVector4(element, "startcolor", Vector4.One));
StartAlpha = ToolBox.GetAttributeFloat(element, "startalpha", 1.0f);
DeleteOnCollision = ToolBox.GetAttributeBool(element, "deleteoncollision", false);
CollidesWithWalls = ToolBox.GetAttributeBool(element, "collideswithwalls", false);
CollisionRadius = ToolBox.GetAttributeFloat(element,
"collisionradius",
Sprites.Count > 0 ? 1 : Sprites[0].SourceRect.Width / 2.0f);
ColorChange = ToolBox.GetAttributeVector4(element, "colorchange", Vector4.Zero);
LifeTime = ToolBox.GetAttributeFloat(element, "lifetime", 5.0f);
VelocityChange = ToolBox.GetAttributeVector2(element, "velocitychange", Vector2.Zero);
VelocityChange = ConvertUnits.ToDisplayUnits(VelocityChange);
RotateToDirection = ToolBox.GetAttributeBool(element, "rotatetodirection", false);
switch (ToolBox.GetAttributeString(element, "drawtarget", "air").ToLowerInvariant())
{
case "air":
default:
DrawTarget = DrawTargetType.Air;
break;
case "water":
DrawTarget = DrawTargetType.Water;
break;
case "both":
DrawTarget = DrawTargetType.Both;
break;
}
}
}
}
@@ -0,0 +1,97 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Barotrauma
{
class BlurEffect
{
public readonly Effect Effect;
public BlurEffect(Effect effect, float dx, float dy)
{
Effect = effect;
SetParameters(dx, dy);
}
/// <summary>
/// Computes sample weightings and texture coordinate offsets
/// for one pass of a separable gaussian blur filter.
/// </summary>
public void SetParameters(float dx, float dy)
{
EffectParameter weightsParameter = Effect.Parameters["SampleWeights"];
EffectParameter offsetsParameter = Effect.Parameters["SampleOffsets"];
// Look up how many samples our gaussian blur effect supports.
int sampleCount = weightsParameter.Elements.Count;
// Create temporary arrays for computing our filter settings.
float[] sampleWeights = new float[sampleCount];
Vector2[] sampleOffsets = new Vector2[sampleCount];
sampleWeights[0] = ComputeGaussian(0);
sampleOffsets[0] = new Vector2(0);
float totalWeights = sampleWeights[0];
// Add pairs of additional sample taps, positioned
// along a line in both directions from the center.
for (int i = 0; i < sampleCount / 2; i++)
{
// Store weights for the positive and negative taps.
float weight = ComputeGaussian(i + 1);
sampleWeights[i * 2 + 1] = weight;
sampleWeights[i * 2 + 2] = weight;
totalWeights += weight * 2;
// To get the maximum amount of blurring from a limited number of
// pixel shader samples, we take advantage of the bilinear filtering
// hardware inside the texture fetch unit. If we position our texture
// coordinates exactly halfway between two texels, the filtering unit
// will average them for us, giving two samples for the price of one.
// This allows us to step in units of two texels per sample, rather
// than just one at a time. The 1.5 offset kicks things off by
// positioning us nicely in between two texels.
float sampleOffset = i * 2 + 1.5f;
Vector2 delta = new Vector2(dx, dy) * sampleOffset;
// Store texture coordinate offsets for the positive and negative taps.
sampleOffsets[i * 2 + 1] = delta;
sampleOffsets[i * 2 + 2] = -delta;
}
// Normalize the list of sample weightings, so they will always sum to one.
for (int i = 0; i < sampleWeights.Length; i++)
{
sampleWeights[i] /= totalWeights;
}
weightsParameter.SetValue(sampleWeights);
offsetsParameter.SetValue(sampleOffsets);
}
/// <summary>
/// Evaluates a single point on the gaussian falloff curve.
/// Used for setting up the blur filter weightings.
/// </summary>
float ComputeGaussian(float n)
{
float theta = 2.0f;
return (float)((1.0 / Math.Sqrt(2 * Math.PI * theta)) *
Math.Exp(-(n * n) / (2 * theta * theta)));
}
}
}
@@ -0,0 +1,326 @@
using System;
using System.Linq;
using FarseerPhysics;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
namespace Barotrauma
{
class EditCharacterScreen : Screen
{
Camera cam;
GUIComponent GUIpanel;
GUIButton physicsButton;
GUIListBox limbList, jointList;
GUIFrame limbPanel;
Character editingCharacter;
Limb editingLimb;
//RevoluteJoint editingJoint;
List<Texture2D> textures;
List<string> texturePaths;
private bool physicsEnabled;
public Camera Cam
{
get { return cam; }
}
public override void Select()
{
base.Select();
GameMain.DebugDraw = true;
cam = new Camera();
GUIpanel = new GUIFrame(new Rectangle(0, 0, 300, GameMain.GraphicsHeight), "");
GUIpanel.Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f);
physicsButton = new GUIButton(new Rectangle(0, 50, 200, 25), "Physics", Alignment.Left, "", GUIpanel);
physicsButton.OnClicked += TogglePhysics;
new GUITextBlock(new Rectangle(0, 80, 0, 25), "Limbs:", "", GUIpanel);
limbList = new GUIListBox(new Rectangle(0, 110, 0, 250), Color.White * 0.7f, "", GUIpanel);
limbList.OnSelected = SelectLimb;
new GUITextBlock(new Rectangle(0, 360, 0, 25), "Joints:", "", GUIpanel);
jointList = new GUIListBox(new Rectangle(0, 390, 0, 250), Color.White * 0.7f, "", GUIpanel);
while (Character.CharacterList.Count > 1)
{
Character.CharacterList.First().Remove();
}
if (Character.CharacterList.Count == 1)
{
if (editingCharacter != Character.CharacterList[0]) UpdateLimbLists(Character.CharacterList[0]);
editingCharacter = Character.CharacterList[0];
Vector2 camPos = editingCharacter.AnimController.Limbs[0].body.SimPosition;
camPos = ConvertUnits.ToDisplayUnits(camPos);
camPos.Y = -camPos.Y;
cam.TargetPos = camPos;
if (physicsEnabled)
{
editingCharacter.Control(1.0f, cam);
}
else
{
cam.TargetPos = Vector2.Zero;
}
}
textures = new List<Texture2D>();
texturePaths = new List<string>();
foreach (Limb limb in editingCharacter.AnimController.Limbs)
{
if (limb.sprite==null || texturePaths.Contains(limb.sprite.FilePath)) continue;
textures.Add(limb.sprite.Texture);
texturePaths.Add(limb.sprite.FilePath);
}
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
public override void Update(double deltaTime)
{
cam.MoveCamera((float)deltaTime);
if (physicsEnabled)
{
Character.UpdateAnimAll((float)deltaTime);
Ragdoll.UpdateAll(cam, (float)deltaTime);
GameMain.World.Step((float)deltaTime);
}
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
//cam.UpdateTransform();
graphics.Clear(Color.CornflowerBlue);
spriteBatch.Begin(SpriteSortMode.BackToFront,
BlendState.AlphaBlend,
null, null, null, null,
cam.Transform);
Submarine.Draw(spriteBatch, true);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.BackToFront,
BlendState.AlphaBlend,
null, null, null, null,
cam.Transform);
//if (EntityPrefab.Selected != null) EntityPrefab.Selected.UpdatePlacing(spriteBatch, cam);
//Entity.DrawSelecting(spriteBatch, cam);
if (editingCharacter!=null)
editingCharacter.Draw(spriteBatch);
spriteBatch.End();
//-------------------- HUD -----------------------------
spriteBatch.Begin(SpriteSortMode.Immediate, null, null, null, GameMain.ScissorTestEnable);
GUIpanel.Draw(spriteBatch);
EditLimb(spriteBatch);
int y = 0;
for (int i = 0; i < textures.Count; i++ )
{
int x = GameMain.GraphicsWidth - textures[i].Width;
spriteBatch.Draw(textures[i], new Vector2(x, y), Color.White);
foreach (Limb limb in editingCharacter.AnimController.Limbs)
{
if (limb.sprite == null || limb.sprite.FilePath != texturePaths[i]) continue;
Rectangle rect = limb.sprite.SourceRect;
rect.X += x;
rect.Y += y;
GUI.DrawRectangle(spriteBatch, rect, Color.Red);
Vector2 limbBodyPos = new Vector2(
rect.X + limb.sprite.Origin.X,
rect.Y + limb.sprite.Origin.Y);
DrawJoints(spriteBatch, limb, limbBodyPos);
//if (limb.BodyShapeTexture == null) continue;
//spriteBatch.Draw(limb.BodyShapeTexture, limbBodyPos,
// null, Color.White, 0.0f,
// new Vector2(limb.BodyShapeTexture.Width, limb.BodyShapeTexture.Height) / 2,
// 1.0f, SpriteEffects.None, 0.0f);
GUI.DrawLine(spriteBatch, limbBodyPos + Vector2.UnitY * 5.0f, limbBodyPos - Vector2.UnitY * 5.0f, Color.White);
GUI.DrawLine(spriteBatch, limbBodyPos + Vector2.UnitX * 5.0f, limbBodyPos - Vector2.UnitX * 5.0f, Color.White);
if (Vector2.Distance(PlayerInput.MousePosition, limbBodyPos)<5.0f && PlayerInput.LeftButtonHeld())
{
limb.sprite.Origin += PlayerInput.MouseSpeed;
}
}
y += textures[i].Height;
}
GUI.Draw((float)deltaTime, spriteBatch, cam);
//EntityPrefab.DrawList(spriteBatch, new Vector2(20,50));
//Entity.Edit(spriteBatch, cam);
spriteBatch.End();
}
private void UpdateLimbLists(Character character)
{
limbList.ClearChildren();
foreach (Limb limb in character.AnimController.Limbs)
{
GUITextBlock textBlock = new GUITextBlock(
new Rectangle(0,0,0,25),
limb.type.ToString(),
Color.Transparent,
Color.White,
Alignment.Left, null,
limbList);
textBlock.Padding = new Vector4(10.0f, 0.0f, 0.0f, 0.0f);
textBlock.UserData = limb;
}
jointList.ClearChildren();
foreach (RevoluteJoint joint in character.AnimController.limbJoints)
{
Limb limb1 = (Limb)(joint.BodyA.UserData);
Limb limb2 = (Limb)(joint.BodyB.UserData);
GUITextBlock textBlock = new GUITextBlock(
new Rectangle(0, 0, 0, 25),
limb1.type.ToString() + " - " + limb2.type.ToString(),
Color.Transparent,
Color.White,
Alignment.Left, null,
jointList);
textBlock.Padding = new Vector4(10.0f, 0.0f, 0.0f, 0.0f);
textBlock.UserData = joint;
}
}
private void DrawJoints(SpriteBatch spriteBatch, Limb limb, Vector2 limbBodyPos)
{
foreach (var joint in editingCharacter.AnimController.limbJoints)
{
Vector2 jointPos = Vector2.Zero;
if (joint.BodyA == limb.body.FarseerBody)
{
jointPos = ConvertUnits.ToDisplayUnits(joint.LocalAnchorA);
}
else if (joint.BodyB == limb.body.FarseerBody)
{
jointPos = ConvertUnits.ToDisplayUnits(joint.LocalAnchorB);
}
else
{
continue;
}
jointPos.Y = -jointPos.Y;
jointPos += limbBodyPos;
if (joint.BodyA == limb.body.FarseerBody)
{
float a1 = joint.UpperLimit - MathHelper.PiOver2;
float a2 = joint.LowerLimit - MathHelper.PiOver2;
float a3 =( a1+a2)/2.0f;
GUI.DrawLine(spriteBatch, jointPos, jointPos + new Vector2((float)Math.Cos(a1), -(float)Math.Sin(a1)) * 30.0f, Color.Green);
GUI.DrawLine(spriteBatch, jointPos, jointPos + new Vector2((float)Math.Cos(a2), -(float)Math.Sin(a2)) * 30.0f, Color.DarkGreen);
GUI.DrawLine(spriteBatch, jointPos, jointPos + new Vector2((float)Math.Cos(a3), -(float)Math.Sin(a3)) * 30.0f, Color.LightGray);
}
GUI.DrawRectangle(spriteBatch, jointPos, new Vector2(5.0f, 5.0f), Color.Red, true);
if (Vector2.Distance(PlayerInput.MousePosition, jointPos) < 6.0f)
{
GUI.DrawRectangle(spriteBatch, jointPos - new Vector2(3.0f, 3.0f), new Vector2(11.0f, 11.0f), Color.Red, false);
if (PlayerInput.LeftButtonHeld())
{
Vector2 speed = ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed);
speed.Y = -speed.Y;
if (joint.BodyA == limb.body.FarseerBody)
{
joint.LocalAnchorA += speed;
}
else
{
joint.LocalAnchorB += speed;
}
}
}
}
}
private bool SelectLimb(GUIComponent component, object selection)
{
try
{
editingLimb = (Limb)selection;
limbPanel = new GUIFrame(new Rectangle(300, 0, 500, 100), Color.Gray*0.8f);
limbPanel.Padding = new Vector4(10.0f,10.0f,10.0f,10.0f);
new GUITextBlock(new Rectangle(0, 0, 200, 25), editingLimb.type.ToString(), Color.Transparent, Color.Black, Alignment.Left, null, limbPanel);
//spriteOrigin = new GUITextBlock(new Rectangle(0, 25, 200, 25), "Sprite origin: ", Color.White, Color.Black, Alignment.Left, limbPanel);
}
catch
{
return false;
}
return true;
}
private void EditLimb(SpriteBatch spriteBatch)
{
if (editingLimb == null) return;
limbPanel.Draw(spriteBatch);
}
private bool TogglePhysics(GUIButton button, object selection)
{
physicsEnabled = !physicsEnabled;
physicsButton.Text = (physicsEnabled) ? "Disable physics" : "Enable physics";
return false;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,443 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma
{
class GameScreen : Screen
{
private Camera cam;
private Color waterColor = new Color(0.75f, 0.8f, 0.9f, 1.0f);
readonly RenderTarget2D renderTargetBackground;
readonly RenderTarget2D renderTarget;
readonly RenderTarget2D renderTargetWater;
readonly RenderTarget2D renderTargetAir;
private BlurEffect lightBlur;
private Effect damageEffect;
private Texture2D damageStencil;
public BackgroundCreatureManager BackgroundCreatureManager;
public override Camera Cam
{
get { return cam; }
}
public GameScreen(GraphicsDevice graphics, ContentManager content)
{
cam = new Camera();
cam.Translate(new Vector2(-10.0f, 50.0f));
renderTargetBackground = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
renderTarget = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
renderTargetWater = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
renderTargetAir = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
var files = GameMain.SelectedPackage.GetFilesOfType(ContentType.BackgroundCreaturePrefabs);
if(files.Count > 0)
BackgroundCreatureManager = new BackgroundCreatureManager(files);
else
BackgroundCreatureManager = new BackgroundCreatureManager("Content/BackgroundSprites/BackgroundCreaturePrefabs.xml");
#if LINUX
var blurEffect = content.Load<Effect>("blurshader_opengl");
damageEffect = content.Load<Effect>("damageshader_opengl");
#else
var blurEffect = content.Load<Effect>("blurshader");
damageEffect = content.Load<Effect>("damageshader");
#endif
damageStencil = TextureLoader.FromFile("Content/Map/walldamage.png");
damageEffect.Parameters["xStencil"].SetValue(damageStencil);
damageEffect.Parameters["aMultiplier"].SetValue(50.0f);
damageEffect.Parameters["cMultiplier"].SetValue(200.0f);
lightBlur = new BlurEffect(blurEffect, 0.001f, 0.001f);
}
public override void Select()
{
base.Select();
if (Character.Controlled!=null)
{
cam.Position = Character.Controlled.WorldPosition;
cam.UpdateTransform();
}
else if (Submarine.MainSub != null)
{
cam.Position = Submarine.MainSub.WorldPosition;
cam.UpdateTransform();
}
foreach (MapEntity entity in MapEntity.mapEntityList)
entity.IsHighlighted = false;
}
public override void Deselect()
{
base.Deselect();
Sounds.SoundManager.LowPassHFGain = 1.0f;
}
public override void AddToGUIUpdateList()
{
if (Character.Controlled != null && Character.Controlled.SelectedConstruction != null)
{
if (Character.Controlled.SelectedConstruction == Character.Controlled.ClosestItem)
{
Character.Controlled.SelectedConstruction.AddToGUIUpdateList();
}
}
if (GameMain.GameSession != null) GameMain.GameSession.AddToGUIUpdateList();
Character.AddAllToGUIUpdateList();
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
public override void Update(double deltaTime)
{
#if DEBUG
if (GameMain.GameSession != null && GameMain.GameSession.Level != null && GameMain.GameSession.Submarine != null &&
!DebugConsole.IsOpen)
{
var closestSub = Submarine.FindClosest(cam.WorldViewCenter);
if (closestSub == null) closestSub = GameMain.GameSession.Submarine;
Vector2 targetMovement = Vector2.Zero;
if (PlayerInput.KeyDown(Keys.I)) targetMovement.Y += 1.0f;
if (PlayerInput.KeyDown(Keys.K)) targetMovement.Y -= 1.0f;
if (PlayerInput.KeyDown(Keys.J)) targetMovement.X -= 1.0f;
if (PlayerInput.KeyDown(Keys.L)) targetMovement.X += 1.0f;
if (targetMovement != Vector2.Zero)
closestSub.ApplyForce(targetMovement * closestSub.SubBody.Body.Mass * 100.0f);
}
#endif
foreach (MapEntity e in MapEntity.mapEntityList)
{
e.IsHighlighted = false;
}
if (GameMain.GameSession != null) GameMain.GameSession.Update((float)deltaTime);
if (Level.Loaded != null) Level.Loaded.Update((float)deltaTime);
if (Character.Controlled != null && Character.Controlled.SelectedConstruction != null)
{
if (Character.Controlled.SelectedConstruction == Character.Controlled.ClosestItem)
{
Character.Controlled.SelectedConstruction.UpdateHUD(cam, Character.Controlled);
}
}
Character.UpdateAll(cam, (float)deltaTime);
BackgroundCreatureManager.Update(cam, (float)deltaTime);
GameMain.ParticleManager.Update((float)deltaTime);
StatusEffect.UpdateAll((float)deltaTime);
if (Character.Controlled != null && Lights.LightManager.ViewTarget != null)
{
cam.TargetPos = Lights.LightManager.ViewTarget.WorldPosition;
}
GameMain.LightManager.Update((float)deltaTime);
cam.MoveCamera((float)deltaTime);
foreach (Submarine sub in Submarine.Loaded)
{
sub.SetPrevTransform(sub.Position);
}
foreach (PhysicsBody pb in PhysicsBody.list)
{
pb.SetPrevTransform(pb.SimPosition, pb.Rotation);
}
MapEntity.UpdateAll(cam, (float)deltaTime);
Character.UpdateAnimAll((float)deltaTime);
Ragdoll.UpdateAll(cam, (float)deltaTime);
foreach (Submarine sub in Submarine.Loaded)
{
sub.Update((float)deltaTime);
}
GameMain.World.Step((float)deltaTime);
if (!PlayerInput.LeftButtonHeld())
{
Inventory.draggingSlot = null;
Inventory.draggingItem = null;
}
}
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
cam.UpdateTransform(true);
Submarine.CullEntities(cam);
DrawMap(graphics, spriteBatch);
spriteBatch.Begin(SpriteSortMode.Immediate, null, null, null, GameMain.ScissorTestEnable);
if (Character.Controlled != null && Character.Controlled.SelectedConstruction != null)
{
if (Character.Controlled.SelectedConstruction == Character.Controlled.ClosestItem)
{
Character.Controlled.SelectedConstruction.DrawHUD(spriteBatch, cam, Character.Controlled);
}
}
if (Character.Controlled != null && cam != null) Character.Controlled.DrawHUD(spriteBatch, cam);
if (GameMain.GameSession != null) GameMain.GameSession.Draw(spriteBatch);
if (Character.Controlled == null && !GUI.DisableHUD)
{
for (int i = 0; i < Submarine.MainSubs.Length; i++)
{
if (Submarine.MainSubs[i] != null)
{
Color indicatorColor = i == 0 ? Color.LightBlue * 0.5f : Color.Red * 0.5f;
DrawSubmarineIndicator(spriteBatch, Submarine.MainSubs[i], indicatorColor);
}
}
}
GUI.Draw((float)deltaTime, spriteBatch, cam);
spriteBatch.End();
}
public void DrawMap(GraphicsDevice graphics, SpriteBatch spriteBatch)
{
foreach (Submarine sub in Submarine.Loaded)
{
sub.UpdateTransform();
}
GameMain.ParticleManager.UpdateTransforms();
GameMain.LightManager.ObstructVision = Character.Controlled != null && Character.Controlled.ObstructVision;
GameMain.LightManager.UpdateLightMap(graphics, spriteBatch, cam, lightBlur.Effect);
if (Character.Controlled != null)
{
GameMain.LightManager.UpdateObstructVision(graphics, spriteBatch, cam, Character.Controlled.CursorWorldPosition);
}
//----------------------------------------------------------------------------------------
//1. draw the background, characters and the parts of the submarine that are behind them
//----------------------------------------------------------------------------------------
graphics.SetRenderTarget(renderTargetBackground);
if (Level.Loaded == null)
{
graphics.Clear(new Color(11, 18, 26, 255));
}
else
{
Level.Loaded.DrawBack(graphics, spriteBatch, cam, BackgroundCreatureManager);
}
spriteBatch.Begin(SpriteSortMode.BackToFront,
BlendState.AlphaBlend,
null, null, null, null,
cam.Transform);
Submarine.DrawBack(spriteBatch, false, s => s is Structure);
spriteBatch.End();
graphics.SetRenderTarget(renderTarget);
spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.Opaque);
spriteBatch.Draw(renderTargetBackground, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.BackToFront,
BlendState.AlphaBlend,
null, null, null, null,
cam.Transform);
Submarine.DrawBack(spriteBatch, false, s => !(s is Structure));
foreach (Character c in Character.CharacterList) c.Draw(spriteBatch);
spriteBatch.End();
//----------------------------------------------------------------------------------------
//draw the rendertarget and particles that are only supposed to be drawn in water into renderTargetWater
graphics.SetRenderTarget(renderTargetWater);
spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.Opaque);
spriteBatch.Draw(renderTarget, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), waterColor);
spriteBatch.End();
#if LINUX
spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.NonPremultiplied,
null, DepthStencilState.DepthRead, null, null,
cam.Transform);
#else
spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.AlphaBlend,
null, DepthStencilState.DepthRead, null, null,
cam.Transform);
#endif
GameMain.ParticleManager.Draw(spriteBatch, true, Particles.ParticleBlendState.AlphaBlend);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.Additive,
null, DepthStencilState.Default, null, null,
cam.Transform);
GameMain.ParticleManager.Draw(spriteBatch, true, Particles.ParticleBlendState.Additive);
spriteBatch.End();
//----------------------------------------------------------------------------------------
//draw the rendertarget and particles that are only supposed to be drawn in air into renderTargetAir
graphics.SetRenderTarget(renderTargetAir);
spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.Opaque);
spriteBatch.Draw(renderTarget, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
spriteBatch.End();
#if LINUX
spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.NonPremultiplied,
null, DepthStencilState.DepthRead, null, null,
cam.Transform);
#else
spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.AlphaBlend,
null, DepthStencilState.DepthRead, null, null,
cam.Transform);
#endif
GameMain.ParticleManager.Draw(spriteBatch, false, Particles.ParticleBlendState.AlphaBlend);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.Additive,
null, DepthStencilState.DepthRead, null, null,
cam.Transform);
GameMain.ParticleManager.Draw(spriteBatch, false, Particles.ParticleBlendState.Additive);
spriteBatch.End();
if (Character.Controlled != null && GameMain.LightManager.LosEnabled)
{
graphics.SetRenderTarget(renderTarget);
spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.Opaque, null, null, null, lightBlur.Effect);
spriteBatch.Draw(renderTargetBackground, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.BackToFront,
BlendState.AlphaBlend, SamplerState.LinearWrap,
null, null, null,
cam.Transform);
Submarine.DrawDamageable(spriteBatch, null, false);
Submarine.DrawFront(spriteBatch, false, s => s is Structure);
spriteBatch.End();
GameMain.LightManager.DrawLOS(spriteBatch, lightBlur.Effect, true);
}
graphics.SetRenderTarget(null);
//----------------------------------------------------------------------------------------
//2. pass the renderTarget to the water shader to do the water effect
//----------------------------------------------------------------------------------------
Hull.renderer.RenderBack(spriteBatch, renderTargetWater);
Array.Clear(Hull.renderer.vertices, 0, Hull.renderer.vertices.Length);
Hull.renderer.PositionInBuffer = 0;
foreach (Hull hull in Hull.hullList)
{
hull.Render(graphics, cam);
}
Hull.renderer.Render(graphics, cam, renderTargetAir, Cam.ShaderTransform);
//----------------------------------------------------------------------------------------
//3. draw the sections of the map that are on top of the water
//----------------------------------------------------------------------------------------
spriteBatch.Begin(SpriteSortMode.BackToFront,
BlendState.AlphaBlend, SamplerState.LinearWrap,
null, null, null,
cam.Transform);
Submarine.DrawFront(spriteBatch, false, null);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Immediate,
BlendState.NonPremultiplied, SamplerState.LinearWrap,
null, null,
damageEffect,
cam.Transform);
Submarine.DrawDamageable(spriteBatch, damageEffect, false);
spriteBatch.End();
GameMain.LightManager.DrawLightMap(spriteBatch, lightBlur.Effect);
spriteBatch.Begin(SpriteSortMode.BackToFront,
BlendState.AlphaBlend, SamplerState.LinearWrap,
null, null, null,
cam.Transform);
if (Level.Loaded != null) Level.Loaded.DrawFront(spriteBatch);
foreach (Character c in Character.CharacterList) c.DrawFront(spriteBatch,cam);
spriteBatch.End();
if (Character.Controlled != null && GameMain.LightManager.LosEnabled)
{
GameMain.LightManager.DrawLOS(spriteBatch, lightBlur.Effect,false);
spriteBatch.Begin(SpriteSortMode.Immediate,
BlendState.AlphaBlend, SamplerState.LinearWrap, DepthStencilState.None, RasterizerState.CullNone, null);
float r = Math.Min(CharacterHUD.damageOverlayTimer * 0.5f, 0.5f);
spriteBatch.Draw(renderTarget, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight),
Color.Lerp(GameMain.LightManager.AmbientLight*0.5f, Color.Red, r));
spriteBatch.End();
}
}
}
}
@@ -0,0 +1,546 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
namespace Barotrauma
{
class LobbyScreen : Screen
{
enum PanelTab { Crew = 0, Map = 1, Store = 3 }
private GUIFrame topPanel;
private GUIFrame[] bottomPanel;
private GUIButton startButton;
private int selectedRightPanel;
private GUIListBox characterList, hireList;
private GUIListBox selectedItemList;
private GUIListBox storeItemList;
private SinglePlayerMode gameMode;
private GUIFrame previewFrame;
private GUIButton buyButton;
private Level selectedLevel;
float mapZoom = 3.0f;
private string CostTextGetter()
{
return "Cost: "+selectedItemCost.ToString()+" credits";
}
private int selectedItemCost
{
get
{
int cost = 0;
foreach (GUIComponent child in selectedItemList.children)
{
MapEntityPrefab ep = child.UserData as MapEntityPrefab;
if (ep == null) continue;
cost += ep.Price;
}
return cost;
}
}
private CrewManager CrewManager
{
get { return GameMain.GameSession.CrewManager; }
}
public LobbyScreen()
{
Rectangle panelRect = new Rectangle(
40, 40,
GameMain.GraphicsWidth - 80,
100);
topPanel = new GUIFrame(panelRect, "");
topPanel.Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f);
GUITextBlock moneyText = new GUITextBlock(new Rectangle(0, 0, 0, 25), "", "",
Alignment.BottomLeft, Alignment.BottomLeft, topPanel);
moneyText.TextGetter = GetMoney;
GUIButton button = new GUIButton(new Rectangle(-240, 0, 100, 30), "Map", null, Alignment.BottomRight, "", topPanel);
button.UserData = PanelTab.Map;
button.OnClicked = SelectRightPanel;
SelectRightPanel(button, button.UserData);
button = new GUIButton(new Rectangle(-120, 0, 100, 30), "Crew", null, Alignment.BottomRight, "", topPanel);
button.UserData = PanelTab.Crew;
button.OnClicked = SelectRightPanel;
button = new GUIButton(new Rectangle(0, 0, 100, 30), "Store", null, Alignment.BottomRight, "", topPanel);
button.UserData = PanelTab.Store;
button.OnClicked = SelectRightPanel;
//---------------------------------------------------------------
//---------------------------------------------------------------
panelRect = new Rectangle(
40,
panelRect.Bottom + 40,
panelRect.Width,
GameMain.GraphicsHeight - 120 - panelRect.Height);
bottomPanel = new GUIFrame[4];
bottomPanel[(int)PanelTab.Crew] = new GUIFrame(panelRect, "");
bottomPanel[(int)PanelTab.Crew].Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f);
//new GUITextBlock(new Rectangle(0, 0, 200, 25), "Crew:", Color.Transparent, Color.White, Alignment.Left, "", bottomPanel[(int)PanelTab.Crew]);
int crewColumnWidth = Math.Min(300, (panelRect.Width - 40) / 2);
new GUITextBlock(new Rectangle(0, 0, 100, 20), "Crew:", "", bottomPanel[(int)PanelTab.Crew], GUI.LargeFont);
characterList = new GUIListBox(new Rectangle(0, 40, crewColumnWidth, 0), "", bottomPanel[(int)PanelTab.Crew]);
characterList.OnSelected = SelectCharacter;
//---------------------------------------
bottomPanel[(int)PanelTab.Map] = new GUIFrame(panelRect, "");
bottomPanel[(int)PanelTab.Map].Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f);
startButton = new GUIButton(new Rectangle(0, 0, 100, 30), "Start",
Alignment.BottomRight, "", bottomPanel[(int)PanelTab.Map]);
startButton.OnClicked = StartShift;
startButton.Enabled = false;
//---------------------------------------
bottomPanel[(int)PanelTab.Store] = new GUIFrame(panelRect, "");
bottomPanel[(int)PanelTab.Store].Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f);
int sellColumnWidth = (panelRect.Width - 40) / 2 - 20;
selectedItemList = new GUIListBox(new Rectangle(0, 30, sellColumnWidth, 400), Color.White * 0.7f, "", bottomPanel[(int)PanelTab.Store]);
selectedItemList.OnSelected = DeselectItem;
var costText = new GUITextBlock(new Rectangle(0, 0, 100, 25), "Cost: ", "", Alignment.BottomLeft, Alignment.TopLeft, bottomPanel[(int)PanelTab.Store]);
costText.TextGetter = CostTextGetter;
buyButton = new GUIButton(new Rectangle(selectedItemList.Rect.Width - 100, 0, 100, 25), "Buy", Alignment.Bottom, "", bottomPanel[(int)PanelTab.Store]);
buyButton.OnClicked = BuyItems;
storeItemList = new GUIListBox(new Rectangle(0, 30, sellColumnWidth, 400), Color.White * 0.7f, Alignment.TopRight, "", bottomPanel[(int)PanelTab.Store]);
storeItemList.OnSelected = SelectItem;
int x = selectedItemList.Rect.Width + 40;
foreach (MapEntityCategory category in Enum.GetValues(typeof(MapEntityCategory)))
{
var items = MapEntityPrefab.list.FindAll(ep => ep.Price>0.0f && ep.Category.HasFlag(category));
if (!items.Any()) continue;
var categoryButton = new GUIButton(new Rectangle(x, 0, 100, 20), category.ToString(), "", bottomPanel[(int)PanelTab.Store]);
categoryButton.UserData = category;
categoryButton.OnClicked = SelectItemCategory;
if (category==MapEntityCategory.Equipment)
{
SelectItemCategory(categoryButton, category);
}
x += 110;
}
}
public override void Select()
{
base.Select();
gameMode = GameMain.GameSession.gameMode as SinglePlayerMode;
UpdateCharacterLists();
}
private void UpdateLocationTab(Location location)
{
topPanel.RemoveChild(topPanel.FindChild("locationtitle"));
topPanel.UserData = location;
var locationTitle = new GUITextBlock(new Rectangle(0, 0, 200, 25),
"Location: "+location.Name, Color.Transparent, Color.White, Alignment.TopLeft, "", topPanel);
locationTitle.UserData = "locationtitle";
locationTitle.Font = GUI.LargeFont;
if (hireList == null)
{
hireList = new GUIListBox(new Rectangle(0, 40, 300, 0), "", Alignment.Right, bottomPanel[(int)PanelTab.Crew]);
new GUITextBlock(new Rectangle(0, 0, 300, 20), "Hire:", "", Alignment.Right, Alignment.Left, bottomPanel[(int)PanelTab.Crew], false, GUI.LargeFont);
hireList.OnSelected = SelectCharacter;
}
if (location.HireManager == null)
{
hireList.ClearChildren();
hireList.Enabled = false;
new GUITextBlock(new Rectangle(0, 0, 0, 0), "No-one available for hire", Color.Transparent, Color.LightGray, Alignment.Center, Alignment.Center, "", hireList);
return;
}
hireList.Enabled = true;
hireList.ClearChildren();
foreach (CharacterInfo c in location.HireManager.availableCharacters)
{
var frame = c.CreateCharacterFrame(hireList, c.Name + " (" + c.Job.Name + ")", c);
new GUITextBlock(
new Rectangle(0, 0, 0, 25),
c.Salary.ToString(),
null, null,
Alignment.TopRight, "", frame);
}
}
public override void Deselect()
{
SelectLocation(null,null);
base.Deselect();
}
public void SelectLocation(Location location, LocationConnection connection)
{
GUIComponent locationPanel = bottomPanel[(int)PanelTab.Map].GetChild("selectedlocation");
if (locationPanel != null) bottomPanel[(int)PanelTab.Map].RemoveChild(locationPanel);
locationPanel = new GUIFrame(new Rectangle(0, 0, 250, 190), Color.Transparent, Alignment.TopRight, null, bottomPanel[(int)PanelTab.Map]);
locationPanel.UserData = "selectedlocation";
if (location == null) return;
new GUITextBlock(new Rectangle(0, 0, 250, 0), location.Name, "", Alignment.TopLeft, Alignment.TopCenter, locationPanel, true, GUI.LargeFont);
if (GameMain.GameSession.Map.SelectedConnection != null && GameMain.GameSession.Map.SelectedConnection.Mission != null)
{
var mission = GameMain.GameSession.Map.SelectedConnection.Mission;
new GUITextBlock(new Rectangle(0, 80, 0, 20), "Mission: "+mission.Name, "", locationPanel);
new GUITextBlock(new Rectangle(0, 100, 0, 20), "Reward: " + mission.Reward+" credits", "", locationPanel);
new GUITextBlock(new Rectangle(0, 130, 0, 0), mission.Description, "", locationPanel, true);
}
startButton.Enabled = true;
selectedLevel = connection.Level;
}
private void UpdateCharacterLists()
{
characterList.ClearChildren();
foreach (CharacterInfo c in CrewManager.characterInfos)
{
c.CreateCharacterFrame(characterList, c.Name + " ("+c.Job.Name+") ", c);
}
}
private void CreateItemFrame(MapEntityPrefab ep, GUIListBox listBox, int width)
{
GUIFrame frame = new GUIFrame(new Rectangle(0, 0, 0, 50), "ListBoxElement", listBox);
frame.UserData = ep;
frame.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f);
frame.ToolTip = ep.Description;
ScalableFont font = listBox.Rect.Width < 280 ? GUI.SmallFont : GUI.Font;
GUITextBlock textBlock = new GUITextBlock(
new Rectangle(50, 0, 0, 25),
ep.Name,
null,null,
Alignment.Left, Alignment.CenterX | Alignment.Left,
"", frame);
textBlock.Font = font;
textBlock.Padding = new Vector4(5.0f, 0.0f, 5.0f, 0.0f);
textBlock.ToolTip = ep.Description;
if (ep.sprite != null)
{
GUIImage img = new GUIImage(new Rectangle(0, 0, 40, 40), ep.sprite, Alignment.CenterLeft, frame);
img.Color = ep.SpriteColor;
img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f);
}
textBlock = new GUITextBlock(
new Rectangle(width - 80, 0, 80, 25),
ep.Price.ToString(),
null, null, Alignment.TopLeft,
Alignment.TopLeft, "", frame);
textBlock.Font = font;
textBlock.ToolTip = ep.Description;
}
private bool SelectItem(GUIComponent component, object obj)
{
MapEntityPrefab prefab = obj as MapEntityPrefab;
if (prefab == null) return false;
CreateItemFrame(prefab, selectedItemList, selectedItemList.Rect.Width);
buyButton.Enabled = CrewManager.Money >= selectedItemCost;
return false;
}
private bool DeselectItem(GUIComponent component, object obj)
{
MapEntityPrefab prefab = obj as MapEntityPrefab;
if (prefab == null) return false;
selectedItemList.RemoveChild(selectedItemList.children.Find(c => c.UserData == obj));
return false;
}
private bool BuyItems(GUIButton button, object obj)
{
int cost = selectedItemCost;
if (CrewManager.Money < cost) return false;
CrewManager.Money -= cost;
for (int i = selectedItemList.children.Count-1; i>=0; i--)
{
GUIComponent child = selectedItemList.children[i];
ItemPrefab ip = child.UserData as ItemPrefab;
if (ip == null) continue;
gameMode.CargoManager.AddItem(ip);
selectedItemList.RemoveChild(child);
}
return false;
}
public override void AddToGUIUpdateList()
{
base.AddToGUIUpdateList();
topPanel.AddToGUIUpdateList();
bottomPanel[selectedRightPanel].AddToGUIUpdateList();
}
public override void Update(double deltaTime)
{
base.Update(deltaTime);
topPanel.Update((float)deltaTime);
bottomPanel[selectedRightPanel].Update((float)deltaTime);
mapZoom += PlayerInput.ScrollWheelSpeed / 1000.0f;
mapZoom = MathHelper.Clamp(mapZoom, 1.0f, 4.0f);
GameMain.GameSession.Map.Update((float)deltaTime, new Rectangle(
bottomPanel[selectedRightPanel].Rect.X + 20,
bottomPanel[selectedRightPanel].Rect.Y + 20,
bottomPanel[selectedRightPanel].Rect.Width - 310,
bottomPanel[selectedRightPanel].Rect.Height - 40), mapZoom);
}
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
if (characterList.CountChildren != CrewManager.characterInfos.Count)
{
UpdateCharacterLists();
}
graphics.Clear(Color.Black);
spriteBatch.Begin(SpriteSortMode.Immediate, null, null, null, GameMain.ScissorTestEnable);
Sprite backGround = GameMain.GameSession.Map.CurrentLocation.Type.Background;
spriteBatch.Draw(backGround.Texture, Vector2.Zero, null, Color.White, 0.0f, Vector2.Zero,
Math.Max((float)GameMain.GraphicsWidth / backGround.SourceRect.Width, (float)GameMain.GraphicsHeight / backGround.SourceRect.Height), SpriteEffects.None, 0.0f);
topPanel.Draw(spriteBatch);
bottomPanel[selectedRightPanel].Draw(spriteBatch);
if (selectedRightPanel == (int)PanelTab.Map)
{
GameMain.GameSession.Map.Draw(spriteBatch, new Rectangle(
bottomPanel[selectedRightPanel].Rect.X + 20,
bottomPanel[selectedRightPanel].Rect.Y + 20,
bottomPanel[selectedRightPanel].Rect.Width - 310,
bottomPanel[selectedRightPanel].Rect.Height - 40), mapZoom);
}
if (topPanel.UserData as Location != GameMain.GameSession.Map.CurrentLocation)
{
UpdateLocationTab(GameMain.GameSession.Map.CurrentLocation);
}
GUI.Draw((float)deltaTime, spriteBatch, null);
spriteBatch.End();
}
public bool SelectRightPanel(GUIButton button, object selection)
{
try
{
selectedRightPanel = (int)selection;
}
catch { return false; }
if (button != null)
{
button.Selected = true;
foreach (GUIComponent child in topPanel.children)
{
GUIButton otherButton = child as GUIButton;
if (otherButton == null || otherButton == button) continue;
otherButton.Selected = false;
}
}
return true;
}
private bool SelectItemCategory(GUIButton button, object selection)
{
if (!(selection is MapEntityCategory)) return false;
storeItemList.ClearChildren();
MapEntityCategory category = (MapEntityCategory)selection;
var items = MapEntityPrefab.list.FindAll(ep => ep.Price > 0.0f && ep.Category.HasFlag(category));
int width = storeItemList.Rect.Width;
foreach (MapEntityPrefab ep in items)
{
CreateItemFrame(ep, storeItemList, width);
}
storeItemList.children.Sort((x, y) => (x.UserData as MapEntityPrefab).Name.CompareTo((y.UserData as MapEntityPrefab).Name));
foreach (GUIComponent child in button.Parent.children)
{
var otherButton = child as GUIButton;
if (child.UserData is MapEntityCategory && otherButton != button)
{
otherButton.Selected = false;
}
}
button.Selected = true;
return true;
}
private string GetMoney()
{
return "Money: " + ((GameMain.GameSession == null) ? "0" : string.Format(CultureInfo.InvariantCulture, "{0:N0}", CrewManager.Money)) + " credits";
}
private bool SelectCharacter(GUIComponent component, object selection)
{
GUIComponent prevInfoFrame = null;
foreach (GUIComponent child in bottomPanel[selectedRightPanel].children)
{
if (!(child.UserData is CharacterInfo)) continue;
prevInfoFrame = child;
}
if (prevInfoFrame != null) bottomPanel[selectedRightPanel].RemoveChild(prevInfoFrame);
CharacterInfo characterInfo = selection as CharacterInfo;
if (characterInfo == null) return false;
characterList.Deselect();
hireList.Deselect();
if (Character.Controlled != null && characterInfo == Character.Controlled.Info) return false;
if (previewFrame == null || previewFrame.UserData != characterInfo)
{
int width = Math.Min(300, bottomPanel[(int)PanelTab.Crew].Rect.Width - hireList.Rect.Width - characterList.Rect.Width - 50);
previewFrame = new GUIFrame(new Rectangle(0, 60, width, 300),
new Color(0.0f, 0.0f, 0.0f, 0.8f),
Alignment.TopCenter, "", bottomPanel[selectedRightPanel]);
previewFrame.Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f);
previewFrame.UserData = characterInfo;
characterInfo.CreateInfoFrame(previewFrame);
}
if (component.Parent == hireList)
{
GUIButton hireButton = new GUIButton(new Rectangle(0,0, 100, 20), "Hire", Alignment.BottomCenter, "", previewFrame);
hireButton.UserData = characterInfo;
hireButton.OnClicked = HireCharacter;
}
return true;
}
private bool HireCharacter(GUIButton button, object selection)
{
CharacterInfo characterInfo = selection as CharacterInfo;
if (characterInfo == null) return false;
if (gameMode.TryHireCharacter(GameMain.GameSession.Map.CurrentLocation.HireManager, characterInfo))
{
UpdateLocationTab(GameMain.GameSession.Map.CurrentLocation);
SelectCharacter(null,null);
}
return false;
}
private bool StartShift(GUIButton button, object selection)
{
if (GameMain.GameSession.Map.SelectedConnection == null) return false;
GameMain.Instance.ShowLoading(ShiftLoading());
return true;
}
private IEnumerable<object> ShiftLoading()
{
GameMain.GameSession.StartShift(selectedLevel, true);
GameMain.GameScreen.Select();
yield return CoroutineStatus.Success;
}
public bool QuitToMainMenu(GUIButton button, object selection)
{
GameMain.MainMenuScreen.Select();
return true;
}
}
}
@@ -0,0 +1,586 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Barotrauma.Networking;
using System.Linq;
using System.Xml.Linq;
using System.IO;
namespace Barotrauma
{
class MainMenuScreen : Screen
{
public enum Tab { NewGame = 1, LoadGame = 2, HostServer = 3, Settings = 4 }
GUIFrame buttonsTab;
private GUIFrame[] menuTabs;
private GUIListBox subList;
private GUIListBox saveList;
private GUITextBox saveNameBox, seedBox;
private GUITextBox serverNameBox, portBox, passwordBox, maxPlayersBox;
private GUITickBox isPublicBox, useUpnpBox;
private GameMain game;
private Tab selectedTab;
public MainMenuScreen(GameMain game)
{
menuTabs = new GUIFrame[Enum.GetValues(typeof(Tab)).Length+1];
buttonsTab = new GUIFrame(new Rectangle(0,0,0,0), Color.Transparent, Alignment.Left | Alignment.CenterY);
buttonsTab.Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f);
//menuTabs[(int)Tabs.Main].Padding = GUI.style.smallPadding;
int y = (int)(GameMain.GraphicsHeight * 0.3f);
Rectangle panelRect = new Rectangle(
290, y,
500, 360);
GUIButton button = new GUIButton(new Rectangle(50, y, 200, 30), "Tutorial", null, Alignment.TopLeft, Alignment.Left, "", buttonsTab);
button.Color = button.Color * 0.8f;
button.OnClicked = TutorialButtonClicked;
button = new GUIButton(new Rectangle(50, y + 60, 200, 30), "New Game", null, Alignment.TopLeft, Alignment.Left, "", buttonsTab);
button.Color = button.Color * 0.8f;
button.UserData = Tab.NewGame;
button.OnClicked = SelectTab;
button = new GUIButton(new Rectangle(50, y + 100, 200, 30), "Load Game", null, Alignment.TopLeft, Alignment.Left, "", buttonsTab);
button.Color = button.Color * 0.8f;
button.UserData = Tab.LoadGame;
button.OnClicked = SelectTab;
button = new GUIButton(new Rectangle(50, y + 160, 200, 30), "Join Server", null, Alignment.TopLeft, Alignment.Left, "", buttonsTab);
button.Color = button.Color * 0.8f;
//button.UserData = (int)Tabs.JoinServer;
button.OnClicked = JoinServerClicked;
button = new GUIButton(new Rectangle(50, y + 200, 200, 30), "Host Server", null, Alignment.TopLeft, Alignment.Left, "", buttonsTab);
button.Color = button.Color * 0.8f;
button.UserData = Tab.HostServer;
button.OnClicked = SelectTab;
button = new GUIButton(new Rectangle(50, y + 260, 200, 30), "Submarine Editor", null, Alignment.TopLeft, Alignment.Left, "", buttonsTab);
button.Color = button.Color * 0.8f;
button.OnClicked = (GUIButton btn, object userdata) => { GameMain.EditMapScreen.Select(); return true; };
button = new GUIButton(new Rectangle(50, y + 320, 200, 30), "Settings", null, Alignment.TopLeft, Alignment.Left, "", buttonsTab);
button.Color = button.Color * 0.8f;
button.UserData = Tab.Settings;
button.OnClicked = SelectTab;
button = new GUIButton(new Rectangle(0, 0, 150, 30), "Quit", Alignment.BottomRight, "", buttonsTab);
button.Color = button.Color * 0.8f;
button.OnClicked = QuitClicked;
panelRect.Y += 10;
//----------------------------------------------------------------------
menuTabs[(int)Tab.NewGame] = new GUIFrame(panelRect, "");
menuTabs[(int)Tab.NewGame].Padding = new Vector4(20.0f,20.0f,20.0f,20.0f);
//new GUITextBlock(new Rectangle(0, -20, 0, 30), "New Game", null, null, Alignment.CenterX, "", menuTabs[(int)Tabs.NewGame]);
new GUITextBlock(new Rectangle(0, 0, 0, 30), "Selected submarine:", null, null, Alignment.Left, "", menuTabs[(int)Tab.NewGame]);
subList = new GUIListBox(new Rectangle(0, 30, 230, panelRect.Height-100), "", menuTabs[(int)Tab.NewGame]);
UpdateSubList();
new GUITextBlock(new Rectangle((int)(subList.Rect.Width + 20), 0, 100, 20),
"Save name: ", "", Alignment.Left, Alignment.Left, menuTabs[(int)Tab.NewGame]);
saveNameBox = new GUITextBox(new Rectangle((int)(subList.Rect.Width + 30), 30, 180, 20),
Alignment.TopLeft, "", menuTabs[(int)Tab.NewGame]);
new GUITextBlock(new Rectangle((int)(subList.Rect.Width + 20), 60, 100, 20),
"Map Seed: ", "", Alignment.Left, Alignment.Left, menuTabs[(int)Tab.NewGame]);
seedBox = new GUITextBox(new Rectangle((int)(subList.Rect.Width + 30), 90, 180, 20),
Alignment.TopLeft, "", menuTabs[(int)Tab.NewGame]);
seedBox.Text = ToolBox.RandomSeed(8);
button = new GUIButton(new Rectangle(0, 0, 100, 30), "Start", Alignment.BottomRight, "", menuTabs[(int)Tab.NewGame]);
button.OnClicked = (GUIButton btn, object userData) =>
{
Submarine selectedSub = subList.SelectedData as Submarine;
if (selectedSub != null && selectedSub.HasTag(SubmarineTag.Shuttle))
{
var msgBox = new GUIMessageBox("Shuttle selected",
"Most shuttles are not adequately equipped to deal with the dangers of the Europan depths. "+
"Are you sure you want to choose a shuttle as your vessel?",
new string[] {"Yes", "No"});
msgBox.Buttons[0].OnClicked = StartGame;
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = msgBox.Close;
return false;
}
StartGame(btn, userData);
return true;
};
//----------------------------------------------------------------------
menuTabs[(int)Tab.LoadGame] = new GUIFrame(panelRect, "");
//menuTabs[(int)Tabs.LoadGame].Padding = GUI.style.smallPadding;
menuTabs[(int)Tab.HostServer] = new GUIFrame(panelRect, "");
//menuTabs[(int)Tabs.JoinServer].Padding = GUI.style.smallPadding;
//new GUITextBlock(new Rectangle(0, -25, 0, 30), "Host Server", "", Alignment.CenterX, Alignment.CenterX, menuTabs[(int)Tabs.HostServer], false, GUI.LargeFont);
new GUITextBlock(new Rectangle(0, 0, 100, 30), "Server Name:", "", Alignment.TopLeft, Alignment.Left, menuTabs[(int)Tab.HostServer]);
serverNameBox = new GUITextBox(new Rectangle(160, 0, 200, 30), null, null, Alignment.TopLeft, Alignment.Left, "", menuTabs[(int)Tab.HostServer]);
new GUITextBlock(new Rectangle(0, 50, 100, 30), "Server port:", "", Alignment.TopLeft, Alignment.Left, menuTabs[(int)Tab.HostServer]);
portBox = new GUITextBox(new Rectangle(160, 50, 200, 30), null, null, Alignment.TopLeft, Alignment.Left, "", menuTabs[(int)Tab.HostServer]);
portBox.Text = NetConfig.DefaultPort.ToString();
portBox.ToolTip = "Server port";
new GUITextBlock(new Rectangle(0, 100, 100, 30), "Max players:", "", Alignment.TopLeft, Alignment.Left, menuTabs[(int)Tab.HostServer]);
maxPlayersBox = new GUITextBox(new Rectangle(195, 100, 30, 30), null, null, Alignment.TopLeft, Alignment.Center, "", menuTabs[(int)Tab.HostServer]);
maxPlayersBox.Text = "8";
maxPlayersBox.Enabled = false;
var minusPlayersBox = new GUIButton(new Rectangle(160, 100, 30, 30), "-", "", menuTabs[(int)Tab.HostServer]);
minusPlayersBox.UserData = -1;
minusPlayersBox.OnClicked = ChangeMaxPlayers;
var plusPlayersBox = new GUIButton(new Rectangle(230, 100, 30, 30), "+", "", menuTabs[(int)Tab.HostServer]);
plusPlayersBox.UserData = 1;
plusPlayersBox.OnClicked = ChangeMaxPlayers;
new GUITextBlock(new Rectangle(0, 150, 100, 30), "Password (optional):", "", Alignment.TopLeft, Alignment.Left, menuTabs[(int)Tab.HostServer]);
passwordBox = new GUITextBox(new Rectangle(160, 150, 200, 30), null, null, Alignment.TopLeft, Alignment.Left, "", menuTabs[(int)Tab.HostServer]);
isPublicBox = new GUITickBox(new Rectangle(10, 200, 20, 20), "Public server", Alignment.TopLeft, menuTabs[(int)Tab.HostServer]);
isPublicBox.ToolTip = "Public servers are shown in the list of available servers in the \"Join Server\" -tab";
useUpnpBox = new GUITickBox(new Rectangle(10, 250, 20, 20), "Attempt UPnP port forwarding", Alignment.TopLeft, menuTabs[(int)Tab.HostServer]);
useUpnpBox.ToolTip = "UPnP can be used for forwarding ports on your router to allow players join the server."
+ " However, UPnP isn't supported by all routers, so you may need to setup port forwards manually"
+" if players are unable to join the server (see the readme for instructions).";
GUIButton hostButton = new GUIButton(new Rectangle(0, 0, 100, 30), "Start", Alignment.BottomRight, "", menuTabs[(int)Tab.HostServer]);
hostButton.OnClicked = HostServerClicked;
this.game = game;
}
public override void Select()
{
base.Select();
if (GameMain.NetworkMember != null)
{
GameMain.NetworkMember.Disconnect();
GameMain.NetworkMember = null;
}
Submarine.Unload();
UpdateSubList();
SelectTab(null, 0);
}
private void UpdateSubList()
{
var subsToShow = Submarine.SavedSubmarines.Where(s => !s.HasTag(SubmarineTag.HideInMenus));
subList.ClearChildren();
foreach (Submarine sub in subsToShow)
{
var textBlock = new GUITextBlock(
new Rectangle(0, 0, 0, 25),
ToolBox.LimitString(sub.Name, GUI.Font, subList.Rect.Width - 65), "ListBoxElement",
Alignment.Left, Alignment.Left, subList)
{
Padding = new Vector4(10.0f, 0.0f, 0.0f, 0.0f),
ToolTip = sub.Description,
UserData = sub
};
if (sub.HasTag(SubmarineTag.Shuttle))
{
textBlock.TextColor = textBlock.TextColor * 0.85f;
var shuttleText = new GUITextBlock(new Rectangle(0, 0, 0, 25), "Shuttle", "", Alignment.Left, Alignment.CenterY | Alignment.Right, textBlock, false, GUI.SmallFont);
shuttleText.TextColor = textBlock.TextColor * 0.8f;
shuttleText.ToolTip = textBlock.ToolTip;
}
}
if (Submarine.SavedSubmarines.Count > 0) subList.Select(Submarine.SavedSubmarines[0]);
}
public bool SelectTab(GUIButton button, object obj)
{
try
{
SelectTab((Tab)obj);
}
catch
{
selectedTab = 0;
}
if (button != null) button.Selected = true;
foreach (GUIComponent child in buttonsTab.children)
{
GUIButton otherButton = child as GUIButton;
if (otherButton == null || otherButton == button) continue;
otherButton.Selected = false;
}
if (Selected != this) Select();
return true;
}
public void SelectTab(Tab tab)
{
if (GameMain.Config.UnsavedSettings)
{
var applyBox = new GUIMessageBox("Apply changes?", "Do you want to apply the settings or discard the changes?",
new string[] {"Apply", "Discard"});
applyBox.Buttons[0].OnClicked += applyBox.Close;
applyBox.Buttons[0].OnClicked += ApplySettings;
applyBox.Buttons[0].UserData = tab;
applyBox.Buttons[1].OnClicked += applyBox.Close;
applyBox.Buttons[1].OnClicked += DiscardSettings;
applyBox.Buttons[1].UserData = tab;
return;
}
selectedTab = tab;
switch (selectedTab)
{
case Tab.NewGame:
saveNameBox.Text = SaveUtil.CreateSavePath();
break;
case Tab.LoadGame:
UpdateLoadScreen();
break;
case Tab.Settings:
GameMain.Config.ResetSettingsFrame();
menuTabs[(int)Tab.Settings] = GameMain.Config.SettingsFrame;
break;
}
}
private bool ApplySettings(GUIButton button, object userData)
{
GameMain.Config.Save("config.xml");
if (userData is Tab) SelectTab((Tab)userData);
if (GameMain.GraphicsWidth != GameMain.Config.GraphicsWidth || GameMain.GraphicsHeight != GameMain.Config.GraphicsHeight)
{
new GUIMessageBox("Restart required", "You need to restart the game for the resolution changes to take effect.");
}
return true;
}
private bool DiscardSettings(GUIButton button, object userData)
{
GameMain.Config.Load("config.xml");
if (userData is Tab) SelectTab((Tab)userData);
return true;
}
private bool TutorialButtonClicked(GUIButton button, object obj)
{
//!!!!!!!!!!!!!!!!!! placeholder
TutorialMode.StartTutorial(Tutorials.TutorialType.TutorialTypes[0]);
return true;
}
private bool JoinServerClicked(GUIButton button, object obj)
{
GameMain.ServerListScreen.Select();
return true;
}
private bool ChangeMaxPlayers(GUIButton button, object obj)
{
int currMaxPlayers = 8;
int.TryParse(maxPlayersBox.Text, out currMaxPlayers);
currMaxPlayers = (int)MathHelper.Clamp(currMaxPlayers + (int)button.UserData, 1, NetConfig.MaxPlayers);
maxPlayersBox.Text = currMaxPlayers.ToString();
return true;
}
private bool HostServerClicked(GUIButton button, object obj)
{
string name = serverNameBox.Text;
if (string.IsNullOrEmpty(name))
{
serverNameBox.Flash();
return false;
}
int port;
if (!int.TryParse(portBox.Text, out port) || port < 0 || port > 65535)
{
portBox.Text = NetConfig.DefaultPort.ToString();
portBox.Flash();
return false;
}
GameMain.NetLobbyScreen = new NetLobbyScreen();
try
{
GameMain.NetworkMember = new GameServer(name, port, isPublicBox.Selected, passwordBox.Text, useUpnpBox.Selected, int.Parse(maxPlayersBox.Text));
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to start server", e);
}
GameMain.NetLobbyScreen.IsServer = true;
//Game1.NetLobbyScreen.Select();
return true;
}
private bool QuitClicked(GUIButton button, object obj)
{
game.Exit();
return true;
}
private void UpdateLoadScreen()
{
menuTabs[(int)Tab.LoadGame].ClearChildren();
string[] saveFiles = SaveUtil.GetSaveFiles();
saveList = new GUIListBox(new Rectangle(0, 0, 200, menuTabs[(int)Tab.LoadGame].Rect.Height - 80), Color.White, "", menuTabs[(int)Tab.LoadGame]);
saveList.OnSelected = SelectSaveFile;
foreach (string saveFile in saveFiles)
{
GUITextBlock textBlock = new GUITextBlock(
new Rectangle(0, 0, 0, 25),
saveFile,
"ListBoxElement",
Alignment.Left,
Alignment.Left,
saveList);
textBlock.Padding = new Vector4(10.0f, 0.0f, 0.0f, 0.0f);
textBlock.UserData = saveFile;
}
var button = new GUIButton(new Rectangle(0, 0, 100, 30), "Start", Alignment.Right | Alignment.Bottom, "", menuTabs[(int)Tab.LoadGame]);
button.OnClicked = LoadGame;
}
private bool SelectSaveFile(GUIComponent component, object obj)
{
string fileName = (string)obj;
XDocument doc = SaveUtil.LoadGameSessionDoc(fileName);
if (doc==null)
{
DebugConsole.ThrowError("Error loading save file \""+fileName+"\". The file may be corrupted.");
return false;
}
RemoveSaveFrame();
string subName = ToolBox.GetAttributeString(doc.Root, "submarine", "");
string saveTime = ToolBox.GetAttributeString(doc.Root, "savetime", "unknown");
string mapseed = ToolBox.GetAttributeString(doc.Root, "mapseed", "unknown");
GUIFrame saveFileFrame = new GUIFrame(new Rectangle((int)(saveList.Rect.Width + 20), 0, 200, 230), Color.Black*0.4f, "", menuTabs[(int)Tab.LoadGame]);
saveFileFrame.UserData = "savefileframe";
saveFileFrame.Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f);
new GUITextBlock(new Rectangle(0,0,0,20), fileName, "", Alignment.TopLeft, Alignment.TopLeft, saveFileFrame, false, GUI.LargeFont);
new GUITextBlock(new Rectangle(0, 35, 0, 20), "Submarine: ", "", saveFileFrame).Font = GUI.SmallFont;
new GUITextBlock(new Rectangle(15, 52, 0, 20), subName, "", saveFileFrame).Font = GUI.SmallFont;
new GUITextBlock(new Rectangle(0, 70, 0, 20), "Last saved: ", "", saveFileFrame).Font = GUI.SmallFont;
new GUITextBlock(new Rectangle(15, 85, 0, 20), saveTime, "", saveFileFrame).Font = GUI.SmallFont;
new GUITextBlock(new Rectangle(0, 105, 0, 20), "Map seed: ", "", saveFileFrame).Font = GUI.SmallFont;
new GUITextBlock(new Rectangle(15, 120, 0, 20), mapseed, "", saveFileFrame).Font = GUI.SmallFont;
var deleteSaveButton = new GUIButton(new Rectangle(0, 0, 100, 20), "Delete", Alignment.BottomCenter, "", saveFileFrame);
deleteSaveButton.UserData = fileName;
deleteSaveButton.OnClicked = DeleteSave;
return true;
}
private bool DeleteSave(GUIButton button, object obj)
{
string saveFile = obj as string;
if (obj == null) return false;
SaveUtil.DeleteSave(saveFile);
UpdateLoadScreen();
return true;
}
private void RemoveSaveFrame()
{
GUIComponent prevFrame = null;
foreach (GUIComponent child in menuTabs[(int)Tab.LoadGame].children)
{
if (child.UserData as string != "savefileframe") continue;
prevFrame = child;
break;
}
menuTabs[(int)Tab.LoadGame].RemoveChild(prevFrame);
}
public override void AddToGUIUpdateList()
{
buttonsTab.AddToGUIUpdateList();
if (selectedTab > 0) menuTabs[(int)selectedTab].AddToGUIUpdateList();
}
public override void Update(double deltaTime)
{
buttonsTab.Update((float)deltaTime);
if (selectedTab>0) menuTabs[(int)selectedTab].Update((float)deltaTime);
GameMain.TitleScreen.TitlePosition =
Vector2.Lerp(GameMain.TitleScreen.TitlePosition, new Vector2(
GameMain.TitleScreen.TitleSize.X / 2.0f * GameMain.TitleScreen.Scale + 30.0f,
GameMain.TitleScreen.TitleSize.Y / 2.0f * GameMain.TitleScreen.Scale + 30.0f),
0.1f);
}
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
graphics.Clear(Color.CornflowerBlue);
GameMain.TitleScreen.DrawLoadingText = false;
GameMain.TitleScreen.Draw(spriteBatch, graphics, (float)deltaTime);
//Game1.GameScreen.DrawMap(graphics, spriteBatch);
spriteBatch.Begin(SpriteSortMode.Immediate, null, null, null, GameMain.ScissorTestEnable);
buttonsTab.Draw(spriteBatch);
if (selectedTab>0) menuTabs[(int)selectedTab].Draw(spriteBatch);
GUI.Draw((float)deltaTime, spriteBatch, null);
GUI.Font.DrawString(spriteBatch, "Barotrauma v"+GameMain.Version, new Vector2(10, GameMain.GraphicsHeight-20), Color.White);
spriteBatch.End();
}
private bool StartGame(GUIButton button, object userData)
{
if (string.IsNullOrEmpty(saveNameBox.Text)) return false;
string[] existingSaveFiles = SaveUtil.GetSaveFiles();
if (Array.Find(existingSaveFiles, s => s == saveNameBox.Text)!=null)
{
new GUIMessageBox("Save name already in use", "Please choose another name for the save file");
return false;
}
Submarine selectedSub = subList.SelectedData as Submarine;
if (selectedSub == null)
{
new GUIMessageBox("Submarine not selected", "Please select a submarine");
return false;
}
if (!Directory.Exists(SaveUtil.TempPath))
{
Directory.CreateDirectory(SaveUtil.TempPath);
}
File.Copy(selectedSub.FilePath, Path.Combine(SaveUtil.TempPath, selectedSub.Name+".sub"), true);
selectedSub = new Submarine(Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"), "");
GameMain.GameSession = new GameSession(selectedSub, saveNameBox.Text, GameModePreset.list.Find(gm => gm.Name == "Single Player"));
(GameMain.GameSession.gameMode as SinglePlayerMode).GenerateMap(seedBox.Text);
GameMain.LobbyScreen.Select();
//new GUIMessageBox("Welcome to Barotrauma!", "Please note that the single player mode is very unfinished at the moment; "+
//"for example, the NPCs don't have an AI yet and there are only a couple of different quests to complete. The multiplayer "+
//"mode should be much more enjoyable to play at the moment, so my recommendation is to try out and get a hang of the game mechanics "+
//"in the single player mode and then move on to multiplayer. Have fun!\n - Regalis, the main dev of Subsurface", 400, 350);
return true;
}
private bool PreviousTab(GUIButton button, object obj)
{
//selectedTab = (int)Tabs.Main;
return true;
}
private bool LoadGame(GUIButton button, object obj)
{
string saveFile = saveList.SelectedData as string;
if (string.IsNullOrWhiteSpace(saveFile)) return false;
try
{
SaveUtil.LoadGame(saveFile);
}
catch (Exception e)
{
DebugConsole.ThrowError("Loading save \""+saveFile+"\" failed", e);
return false;
}
GameMain.LobbyScreen.Select();
return true;
}
}
}
File diff suppressed because it is too large Load Diff
+99
View File
@@ -0,0 +1,99 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
namespace Barotrauma
{
class Screen
{
private static Screen selected;
public static Screen Selected
{
get { return selected; }
}
public virtual void Deselect()
{
}
public virtual void Select()
{
if (selected != null && selected != this)
{
selected.Deselect();
GUIComponent.KeyboardDispatcher.Subscriber = null;
}
selected = this;
}
public virtual Camera Cam
{
get { return null; }
}
public virtual void AddToGUIUpdateList()
{
}
public virtual void Update(double deltaTime)
{
}
public virtual void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
}
public void ColorFade(Color from, Color to, float duration)
{
if (duration <= 0.0f) return;
CoroutineManager.StartCoroutine(UpdateColorFade(from, to, duration));
}
private IEnumerable<object> UpdateColorFade(Color from, Color to, float duration)
{
while (Selected != this)
{
yield return CoroutineStatus.Running;
}
float timer = 0.0f;
while (timer < duration)
{
GUI.ScreenOverlayColor = Color.Lerp(from, to, Math.Min(timer / duration, 1.0f));
timer += CoroutineManager.UnscaledDeltaTime;
yield return CoroutineStatus.Running;
}
GUI.ScreenOverlayColor = to;
yield return CoroutineStatus.Success;
}
protected void DrawSubmarineIndicator(SpriteBatch spriteBatch, Submarine submarine, Color color)
{
Vector2 subDiff = submarine.WorldPosition - Cam.WorldViewCenter;
if (Math.Abs(subDiff.X) > Cam.WorldView.Width || Math.Abs(subDiff.Y) > Cam.WorldView.Height)
{
Vector2 normalizedSubDiff = Vector2.Normalize(subDiff);
Vector2 iconPos =
Cam.WorldToScreen(Cam.WorldViewCenter) +
new Vector2(normalizedSubDiff.X * GameMain.GraphicsWidth * 0.4f, -normalizedSubDiff.Y * GameMain.GraphicsHeight * 0.4f);
GUI.SubmarineIcon.Draw(spriteBatch, iconPos, color);
Vector2 arrowOffset = normalizedSubDiff * GUI.SubmarineIcon.size.X * 0.7f;
arrowOffset.Y = -arrowOffset.Y;
GUI.Arrow.Draw(spriteBatch, iconPos + arrowOffset, color, MathUtils.VectorToAngle(arrowOffset) + MathHelper.PiOver2);
}
}
}
}
@@ -0,0 +1,343 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Barotrauma.Networking;
using System.Collections.Generic;
using RestSharp;
namespace Barotrauma
{
class ServerListScreen : Screen
{
//how often the client is allowed to refresh servers
private TimeSpan AllowedRefreshInterval = new TimeSpan(0,0,3);
private GUIFrame menu;
private GUIListBox serverList;
private GUIButton joinButton;
private GUITextBox clientNameBox, ipBox;
//private RestRequestAsyncHandle restRequestHandle;
private bool masterServerResponded;
private IRestResponse masterServerResponse;
private int[] columnX;
//a timer for
private DateTime refreshDisableTimer;
private bool waitingForRefresh;
public ServerListScreen()
{
int width = Math.Min(GameMain.GraphicsWidth - 160, 1000);
int height = Math.Min(GameMain.GraphicsHeight - 160, 700);
Rectangle panelRect = new Rectangle(0, 0, width, height);
menu = new GUIFrame(panelRect, null, Alignment.Center, "");
menu.Padding = new Vector4(40.0f, 40.0f, 40.0f, 20.0f);
new GUITextBlock(new Rectangle(0, -25, 0, 30), "Join Server", "", Alignment.CenterX, Alignment.CenterX, menu, false, GUI.LargeFont);
new GUITextBlock(new Rectangle(0, 30, 0, 30), "Your Name:", "", menu);
clientNameBox = new GUITextBox(new Rectangle(0, 60, 200, 30), "", menu);
new GUITextBlock(new Rectangle(0, 100, 0, 30), "Server IP:", "", menu);
ipBox = new GUITextBox(new Rectangle(0, 130, 200, 30), "", menu);
int middleX = (int)(width * 0.4f);
serverList = new GUIListBox(new Rectangle(middleX,60,0,height-160), "", menu);
serverList.OnSelected = SelectServer;
float[] columnRelativeX = new float[] { 0.15f, 0.5f, 0.15f, 0.2f };
columnX = new int[columnRelativeX.Length];
for (int n = 0; n < columnX.Length; n++)
{
columnX[n] = (int)(columnRelativeX[n] * serverList.Rect.Width);
if (n > 0) columnX[n] += columnX[n - 1];
}
ScalableFont font = GUI.SmallFont; // serverList.Rect.Width < 400 ? GUI.SmallFont : GUI.Font;
new GUITextBlock(new Rectangle(middleX, 30, 0, 30), "Password", "", menu).Font = font;
new GUITextBlock(new Rectangle(middleX + columnX[0], 30, 0, 30), "Name", "", menu).Font = font;
new GUITextBlock(new Rectangle(middleX + columnX[1], 30, 0, 30), "Players", "", menu).Font = font;
new GUITextBlock(new Rectangle(middleX + columnX[2], 30, 0, 30), "Round started", "", menu).Font = font;
joinButton = new GUIButton(new Rectangle(-170, 0, 150, 30), "Refresh", Alignment.BottomRight, "", menu);
joinButton.OnClicked = RefreshServers;
joinButton = new GUIButton(new Rectangle(0,0,150,30), "Join", Alignment.BottomRight, "", menu);
joinButton.OnClicked = JoinServer;
GUIButton button = new GUIButton(new Rectangle(-20, -20, 100, 30), "Back", Alignment.TopLeft, "", menu);
button.OnClicked = GameMain.MainMenuScreen.SelectTab;
button.SelectedColor = button.Color;
refreshDisableTimer = DateTime.Now;
}
public override void Select()
{
base.Select();
RefreshServers(null, null);
}
private bool SelectServer(GUIComponent component, object obj)
{
string ip = obj as string;
if (string.IsNullOrWhiteSpace(ip)) return false;
ipBox.Text = ip;
return true;
}
private bool RefreshServers(GUIButton button, object obj)
{
if (waitingForRefresh) return false;
serverList.ClearChildren();
new GUITextBlock(new Rectangle(0, 0, 0, 20), "Refreshing server list...", "", serverList);
CoroutineManager.StartCoroutine(WaitForRefresh());
return true;
}
private IEnumerable<object> WaitForRefresh()
{
waitingForRefresh = true;
if (refreshDisableTimer > DateTime.Now)
{
yield return new WaitForSeconds((float)(refreshDisableTimer - DateTime.Now).TotalSeconds);
}
//CoroutineManager.StartCoroutine(UpdateServerList());
CoroutineManager.StartCoroutine(SendMasterServerRequest());
waitingForRefresh = false;
refreshDisableTimer = DateTime.Now + AllowedRefreshInterval;
yield return CoroutineStatus.Success;
}
private void UpdateServerList(string masterServerData)
{
serverList.ClearChildren();
//string masterServerData = GetMasterServerData();
if (string.IsNullOrWhiteSpace(masterServerData))
{
new GUITextBlock(new Rectangle(0, 0, 0, 20), "Couldn't find any servers", "", serverList);
return;
}
if (masterServerData.Substring(0, 5).ToLowerInvariant() == "error")
{
DebugConsole.ThrowError("Error while connecting to master server ("+masterServerData+")!");
return;
}
string[] lines = masterServerData.Split('\n');
for (int i = 0; i<lines.Length; i++)
{
string[] arguments = lines[i].Split('|');
if (arguments.Length < 3) continue;
string IP = arguments[0];
string port = arguments[1];
string serverName = arguments[2];
string gameStarted = (arguments.Length > 3) ? arguments[3] : "";
string currPlayersStr = (arguments.Length > 4) ? arguments[4] : "";
string maxPlayersStr = (arguments.Length > 5) ? arguments[5] : "";
string hasPassWordStr = (arguments.Length > 6) ? arguments[6] : "";
var serverFrame = new GUIFrame(new Rectangle(0, 0, 0, 30), (i % 2 == 0) ? Color.Transparent : Color.White * 0.2f, "ListBoxElement", serverList);
serverFrame.UserData = IP + ":" + port;
var passwordBox = new GUITickBox(new Rectangle(columnX[0] / 2, 0, 20, 20), "", Alignment.CenterLeft, serverFrame);
passwordBox.Selected = hasPassWordStr == "1";
passwordBox.Enabled = false;
passwordBox.UserData = "password";
new GUITextBlock(new Rectangle(columnX[0], 0, 0, 0), serverName, "", Alignment.TopLeft, Alignment.CenterLeft, serverFrame);
int playerCount = 0, maxPlayers = 1;
int.TryParse(currPlayersStr, out playerCount);
int.TryParse(maxPlayersStr, out maxPlayers);
new GUITextBlock(new Rectangle(columnX[1], 0, 0, 0), playerCount + "/" + maxPlayers, "", Alignment.TopLeft, Alignment.CenterLeft, serverFrame);
var gameStartedBox = new GUITickBox(new Rectangle(columnX[2] + (columnX[3] - columnX[2])/ 2, 0, 20, 20), "", Alignment.CenterRight, serverFrame);
gameStartedBox.Selected = gameStarted == "1";
gameStartedBox.Enabled = false;
}
}
private IEnumerable<object> SendMasterServerRequest()
{
RestClient client = null;
try
{
client = new RestClient(NetConfig.MasterServerUrl);
}
catch (Exception e)
{
DebugConsole.ThrowError("Error while connecting to master server", e);
}
if (client == null) yield return CoroutineStatus.Success;
var request = new RestRequest("masterserver2.php", Method.GET);
request.AddParameter("gamename", "barotrauma");
request.AddParameter("action", "listservers");
// execute the request
masterServerResponded = false;
var restRequestHandle = client.ExecuteAsync(request, response => MasterServerCallBack(response));
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 8);
while (!masterServerResponded)
{
if (DateTime.Now > timeOut)
{
serverList.ClearChildren();
restRequestHandle.Abort();
new GUIMessageBox("Connection error", "Couldn't connect to master server (request timed out).");
yield return CoroutineStatus.Success;
}
yield return CoroutineStatus.Running;
}
if (masterServerResponse.ErrorException != null)
{
serverList.ClearChildren();
new GUIMessageBox("Connection error", "Error while connecting to master server {" + masterServerResponse.ErrorException + "}");
}
else if (masterServerResponse.StatusCode != System.Net.HttpStatusCode.OK)
{
serverList.ClearChildren();
switch (masterServerResponse.StatusCode)
{
case System.Net.HttpStatusCode.NotFound:
new GUIMessageBox("Connection error",
"Error while connecting to master server (404 - \"" + NetConfig.MasterServerUrl + "\" not found)");
break;
case System.Net.HttpStatusCode.ServiceUnavailable:
new GUIMessageBox("Connection error",
"Error while connecting to master server (505 - Service Unavailable) " +
"The master server may be down for maintenance or temporarily overloaded. Please try again after in a few moments.");
break;
default:
new GUIMessageBox("Connection error",
"Error while connecting to master server (" + masterServerResponse.StatusCode + ": " + masterServerResponse.StatusDescription + ")");
break;
}
}
else
{
UpdateServerList(masterServerResponse.Content);
}
yield return CoroutineStatus.Success;
}
private void MasterServerCallBack(IRestResponse response)
{
masterServerResponse = response;
masterServerResponded = true;
}
private bool JoinServer(GUIButton button, object obj)
{
if (string.IsNullOrWhiteSpace(clientNameBox.Text))
{
clientNameBox.Flash();
return false;
}
string ip = ipBox.Text;
if (string.IsNullOrWhiteSpace(ip))
{
ipBox.Flash();
return false;
}
CoroutineManager.StartCoroutine(ConnectToServer(ip));
return true;
}
public void JoinServer(string ip, bool hasPassword, string msg = "Password required")
{
CoroutineManager.StartCoroutine(ConnectToServer(ip));
}
private IEnumerable<object> ConnectToServer(string ip)
{
try
{
GameMain.NetworkMember = new GameClient(clientNameBox.Text);
GameMain.Client.ConnectToServer(ip);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to start the client", e);
}
yield return CoroutineStatus.Success;
}
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
graphics.Clear(Color.CornflowerBlue);
GameMain.TitleScreen.DrawLoadingText = false;
GameMain.TitleScreen.Draw(spriteBatch, graphics, (float)deltaTime);
spriteBatch.Begin(SpriteSortMode.Immediate, null, null, null, GameMain.ScissorTestEnable);
menu.Draw(spriteBatch);
GUI.Draw((float)deltaTime, spriteBatch, null);
spriteBatch.End();
}
public override void AddToGUIUpdateList()
{
menu.AddToGUIUpdateList();
}
public override void Update(double deltaTime)
{
//GameMain.TitleScreen.Update();
menu.Update((float)deltaTime);
//GUI.Update((float)deltaTime);
}
}
}
@@ -0,0 +1,337 @@
using System;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Barotrauma.Networking;
using FarseerPhysics;
using FarseerPhysics.Factories;
using FarseerPhysics.Dynamics;
using System.IO;
using System.Collections.Generic;
using RestSharp;
namespace Barotrauma
{
class ServerListScreen : Screen
{
//how often the client is allowed to refresh servers
private TimeSpan AllowedRefreshInterval = new TimeSpan(0,0,3);
private GUIFrame menu;
private GUIListBox serverList;
private GUIButton joinButton;
private GUITextBox clientNameBox, ipBox;
//private RestRequestAsyncHandle restRequestHandle;
private bool masterServerResponded;
private int[] columnX;
//a timer for
private DateTime refreshDisableTimer;
private bool waitingForRefresh;
public ServerListScreen()
{
int width = Math.Min(GameMain.GraphicsWidth - 160, 1000);
int height = Math.Min(GameMain.GraphicsHeight - 160, 700);
Rectangle panelRect = new Rectangle(0, 0, width, height);
menu = new GUIFrame(panelRect, null, Alignment.Center, GUI.Style);
menu.Padding = new Vector4(40.0f, 40.0f, 40.0f, 20.0f);
new GUITextBlock(new Rectangle(0, -25, 0, 30), "Join Server", GUI.Style, Alignment.CenterX, Alignment.CenterX, menu, false, GUI.LargeFont);
new GUITextBlock(new Rectangle(0, 30, 0, 30), "Your Name:", GUI.Style, menu);
clientNameBox = new GUITextBox(new Rectangle(0, 60, 200, 30), GUI.Style, menu);
new GUITextBlock(new Rectangle(0, 100, 0, 30), "Server IP:", GUI.Style, menu);
ipBox = new GUITextBox(new Rectangle(0, 130, 200, 30), GUI.Style, menu);
int middleX = (int)(width * 0.4f);
serverList = new GUIListBox(new Rectangle(middleX,60,0,height-160), GUI.Style, menu);
serverList.OnSelected = SelectServer;
float[] columnRelativeX = new float[] { 0.15f, 0.55f, 0.15f, 0.15f };
columnX = new int[columnRelativeX.Length];
for (int n = 0; n < columnX.Length; n++)
{
columnX[n] = (int)(columnRelativeX[n] * serverList.Rect.Width);
if (n > 0) columnX[n] += columnX[n - 1];
}
SpriteFont font = serverList.Rect.Width < 400 ? GUI.SmallFont : GUI.Font;
new GUITextBlock(new Rectangle(middleX, 30, 0, 30), "Password", GUI.Style, menu).Font = font;
new GUITextBlock(new Rectangle(middleX + columnX[0], 30, 0, 30), "Name", GUI.Style, menu).Font = font;
new GUITextBlock(new Rectangle(middleX + columnX[1], 30, 0, 30), "Players", GUI.Style, menu).Font = font;
new GUITextBlock(new Rectangle(middleX + columnX[2], 30, 0, 30), "Round started", GUI.Style, menu).Font = font;
joinButton = new GUIButton(new Rectangle(-170, 0, 150, 30), "Refresh", Alignment.BottomRight, GUI.Style, menu);
joinButton.OnClicked = RefreshServers;
joinButton = new GUIButton(new Rectangle(0,0,150,30), "Join", Alignment.BottomRight, GUI.Style, menu);
joinButton.OnClicked = JoinServer;
GUIButton button = new GUIButton(new Rectangle(-20, -20, 100, 30), "Back", Alignment.TopLeft, GUI.Style, menu);
button.OnClicked = GameMain.MainMenuScreen.SelectTab;
button.CanBeSelected = false;
button.SelectedColor = button.Color;
refreshDisableTimer = DateTime.Now;
}
public override void Select()
{
base.Select();
RefreshServers(null, null);
}
private bool SelectServer(GUIComponent component, object obj)
{
string ip = obj as string;
if (string.IsNullOrWhiteSpace(ip)) return false;
ipBox.Text = ip;
return true;
}
private bool RefreshServers(GUIButton button, object obj)
{
if (waitingForRefresh) return false;
serverList.ClearChildren();
new GUITextBlock(new Rectangle(0, 0, 0, 20), "Refreshing server list...", GUI.Style, serverList);
CoroutineManager.StartCoroutine(WaitForRefresh());
return true;
}
private IEnumerable<object> WaitForRefresh()
{
waitingForRefresh = true;
if (refreshDisableTimer > DateTime.Now)
{
yield return new WaitForSeconds((float)(refreshDisableTimer - DateTime.Now).TotalSeconds);
}
//CoroutineManager.StartCoroutine(UpdateServerList());
CoroutineManager.StartCoroutine(SendMasterServerRequest());
waitingForRefresh = false;
refreshDisableTimer = DateTime.Now + AllowedRefreshInterval;
yield return CoroutineStatus.Success;
}
private void UpdateServerList(string masterServerData)
{
serverList.ClearChildren();
//string masterServerData = GetMasterServerData();
if (string.IsNullOrWhiteSpace(masterServerData))
{
var nameText = new GUITextBlock(new Rectangle(0, 0, 0, 20), "Couldn't find any servers", GUI.Style, serverList);
return;
}
if (masterServerData.Substring(0,5).ToLower()=="error")
{
DebugConsole.ThrowError("Error while connecting to master server ("+masterServerData+")!");
return;
}
string[] lines = masterServerData.Split('\n');
for (int i = 0; i<lines.Length; i++)
{
string[] arguments = lines[i].Split('|');
if (arguments.Length < 3) continue;
string IP = arguments[0];
string port = arguments[1];
string serverName = arguments[2];
string gameStarted = (arguments.Length > 3) ? arguments[3] : "";
string playerCountStr = (arguments.Length > 4) ? arguments[4] : "";
string hasPassWordStr = (arguments.Length > 5) ? arguments[5] : "";
var serverFrame = new GUIFrame(new Rectangle(0,0,0,20), (i%2 == 0) ? Color.Transparent : Color.White*0.2f, null, serverList);
serverFrame.UserData = IP+":"+port;
serverFrame.HoverColor = Color.Gold * 0.2f;
serverFrame.SelectedColor = Color.Gold * 0.5f;
var passwordBox = new GUITickBox(new Rectangle(columnX[0]/2, 0, 20, 20), "", Alignment.TopLeft, serverFrame);
passwordBox.Selected = hasPassWordStr == "1";
passwordBox.Enabled = false;
passwordBox.UserData = "password";
var nameText = new GUITextBlock(new Rectangle(columnX[0], 0, 0, 0), serverName, GUI.Style, serverFrame);
int playerCount, maxPlayers;
playerCount = GameClient.ByteToPlayerCount((byte)int.Parse(playerCountStr), out maxPlayers);
var playerCountText = new GUITextBlock(new Rectangle(columnX[1], 0, 0, 0), playerCount + "/" + maxPlayers, GUI.Style, serverFrame);
var gameStartedBox = new GUITickBox(new Rectangle(columnX[2] + (columnX[3] - columnX[2])/ 2, 0, 20, 20), "", Alignment.TopLeft, serverFrame);
gameStartedBox.Selected = gameStarted == "1";
gameStartedBox.Enabled = false;
}
}
private IEnumerable<object> SendMasterServerRequest()
{
RestClient client = null;
try
{
client = new RestClient(NetConfig.MasterServerUrl);
}
catch (Exception e)
{
DebugConsole.ThrowError("Error while connecting to master server", e);
}
if (client == null) yield return CoroutineStatus.Success;
var request = new RestRequest("masterserver.php", Method.GET);
request.AddParameter("gamename", "barotrauma"); // adds to POST or URL querystring based on Method
request.AddParameter("action", "listservers"); // adds to POST or URL querystring based on Method
// easily add HTTP Headers
//request.AddHeader("header", "value");
//// add files to upload (works with compatible verbs)
//request.AddFile(path);
// execute the request
masterServerResponded = false;
var restRequestHandle = client.ExecuteAsync(request, response => MasterServerCallBack(response));
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 8);
while (!masterServerResponded)
{
if (DateTime.Now > timeOut)
{
serverList.ClearChildren();
restRequestHandle.Abort();
DebugConsole.ThrowError("Couldn't connect to master server (request timed out)");
}
yield return CoroutineStatus.Running;
}
yield return CoroutineStatus.Success;
}
private void MasterServerCallBack(IRestResponse response)
{
masterServerResponded = true;
if (response.ErrorException!=null)
{
serverList.ClearChildren();
DebugConsole.ThrowError("Error while connecting to master server", response.ErrorException);
return;
}
if (response.StatusCode!= System.Net.HttpStatusCode.OK)
{
serverList.ClearChildren();
DebugConsole.ThrowError("Error while connecting to master server (" +response.StatusCode+": "+response.StatusDescription+")");
return;
}
UpdateServerList(response.Content);
}
private bool JoinServer(GUIButton button, object obj)
{
if (string.IsNullOrWhiteSpace(clientNameBox.Text))
{
clientNameBox.Flash();
return false;
}
string ip = ipBox.Text;
if (string.IsNullOrWhiteSpace(ip))
{
ipBox.Flash();
return false;
}
CoroutineManager.StartCoroutine(JoinServer(ip));
return true;
}
private IEnumerable<object> JoinServer(string ip)
{
string selectedPassword = "";
if (serverList.Selected!=null && (serverList.Selected.GetChild("password") as GUITickBox).Selected)
{
var msgBox = new GUIMessageBox("Password required:", "");
var passwordBox = new GUITextBox(new Rectangle(0,40,150,25), Alignment.TopLeft, GUI.Style, msgBox);
passwordBox.UserData = "password";
var okButton = msgBox.GetChild<GUIButton>();
while (GUIMessageBox.MessageBoxes.Contains(msgBox))
{
okButton.Enabled = !string.IsNullOrWhiteSpace(passwordBox.Text);
yield return CoroutineStatus.Running;
}
selectedPassword = passwordBox.Text;
}
GameMain.NetworkMember = new GameClient(clientNameBox.Text);
GameMain.Client.ConnectToServer(ip, selectedPassword);
yield return CoroutineStatus.Success;
}
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
graphics.Clear(Color.CornflowerBlue);
GameMain.GameScreen.DrawMap(graphics, spriteBatch);
spriteBatch.Begin();
menu.Draw(spriteBatch);
//if (previewPlayer!=null) previewPlayer.Draw(spriteBatch);
GUI.Draw((float)deltaTime, spriteBatch, null);
spriteBatch.End();
}
public override void Update(double deltaTime)
{
menu.Update((float)deltaTime);
GUI.Update((float)deltaTime);
}
}
}
+117
View File
@@ -0,0 +1,117 @@
using System;
using NVorbis;
using OpenTK.Audio.OpenAL;
namespace Barotrauma.Sounds
{
class OggSound : IDisposable
{
//internal VorbisReader Reader { get; private set; }
//const int DefaultBufferSize = 44100;
//private VorbisReader reader;
//private SoundEffect effect;
//SoundEffectInstance instance;
public const int DefaultBufferCount = 3;
private short[] castBuffer;
private int sampleRate;
private ALFormat format;
private string file;
int alBufferId;
public int AlBufferId
{
get { return alBufferId; }
}
//public bool IsLooped { get; set; }
public static OggSound Load(string oggFile, int bufferCount = DefaultBufferCount)
{
OggSound sound = new OggSound();
sound.file = oggFile;
using (VorbisReader reader = new VorbisReader(oggFile))
{
int bufferSize = (int)reader.TotalSamples;
float[] buffer = new float[bufferSize];
sound.castBuffer = new short[bufferSize];
int readSamples = reader.ReadSamples(buffer, 0, bufferSize);
CastBuffer(buffer, sound.castBuffer, readSamples);
sound.alBufferId = AL.GenBuffer();
sound.format = reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16;
sound.sampleRate = reader.SampleRate;
//alSourceId = AL.GenSource();
AL.BufferData(sound.alBufferId, reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, sound.castBuffer,
readSamples * sizeof(short), reader.SampleRate);
ALHelper.Check();
}
//AL.Source(alSourceId, ALSourcei.Buffer, alBufferId);
//if (ALHelper.XRam.IsInitialized)
//{
// ALHelper.XRam.SetBufferMode(bufferCount, ref alBufferId, XRamExtension.XRamStorage.Hardware);
// ALHelper.Check();
//}
//Volume = 1;
//if (ALHelper.Efx.IsInitialized)
//{
// alFilterId = ALHelper.Efx.GenFilter();
// ALHelper.Efx.Filter(alFilterId, EfxFilteri.FilterType, (int)EfxFilterType.Lowpass);
// ALHelper.Efx.Filter(alFilterId, EfxFilterf.LowpassGain, 1);
// LowPassHFGain = 1;
//}
return sound;
}
public void SetBufferData(int alBufferId)
{
AL.BufferData(alBufferId, format, castBuffer,
castBuffer.Length * sizeof(short), sampleRate);
}
static void CastBuffer(float[] inBuffer, short[] outBuffer, int length)
{
for (int i = 0; i < length; i++)
{
int temp = (int)(32767f * inBuffer[i]);
if (temp > short.MaxValue) temp = short.MaxValue;
else if (temp < short.MinValue) temp = short.MinValue;
outBuffer[i] = (short)temp;
}
}
public void Dispose()
{
//var state = AL.GetSourceState(alSourceId);
//if (state == ALSourceState.Playing || state == ALSourceState.Paused)
// Stop();
System.Diagnostics.Debug.WriteLine(alBufferId);
//AL.DeleteSource(alSourceId);
AL.DeleteBuffer(alBufferId);
//if (ALHelper.Efx.IsInitialized)
// ALHelper.Efx.DeleteFilter(alFilterId);
ALHelper.Check();
}
}
}
+525
View File
@@ -0,0 +1,525 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using NVorbis;
using OpenTK.Audio.OpenAL;
namespace Barotrauma.Sounds
{
internal static class ALHelper
{
public static readonly XRamExtension XRam = new XRamExtension();
public static readonly EffectsExtension Efx = new EffectsExtension();
static ALHelper()
{
try
{
Debug.WriteLine("OpenAL Soft [" + (AL.Get(ALGetString.Version).Contains("SOFT") ? "X" : " ") + "], ");
Debug.WriteLine("X-RAM [" + (XRam.IsInitialized ? "X" : " ") + "], ");
Debug.WriteLine("Effect Extensions [" + (Efx.IsInitialized ? "X" : " ") + "]");
}
catch (Exception e)
{
DebugConsole.ThrowError("OpenAL error!", e);
}
}
[Conditional("TRACE")]
public static void TraceMemoryUsage(Action<string, int, int> logHandler)
{
var usedHeap = (double)GC.GetTotalMemory(true);
string[] sizes = { "B", "KB", "MB", "GB" };
int order = 0;
while (usedHeap >= 1024 && order + 1 < sizes.Length)
{
order++;
usedHeap = usedHeap / 1024;
}
//logHandler(String.Format("Total memory : {0:0.###} {1} ", usedHeap, sizes[order]), 0, 6);
}
public static void Check()
{
ALError error;
if ((error = AL.GetError()) != ALError.NoError)
{
#if DEBUG
DebugConsole.ThrowError("OpenAL error: " + AL.GetErrorString(error));
#else
DebugConsole.NewMessage("OpenAL error: " + AL.GetErrorString(error), Microsoft.Xna.Framework.Color.Red);
#endif
}
}
}
public class OggStream : IDisposable
{
public const int DefaultBufferCount = 3;
internal readonly object stopMutex = new object();
internal readonly object prepareMutex = new object();
internal readonly int alSourceId;
internal readonly int[] alBufferIds;
//readonly int alFilterId;
readonly Stream underlyingStream;
internal VorbisReader Reader { get; private set; }
internal bool Ready { get; private set; }
internal bool Preparing { get; private set; }
public int BufferCount { get; private set; }
#if TRACE
public int logX, logY;
public Action<string, int, int> LogHandler;
#endif
public OggStream(string filename, int bufferCount = DefaultBufferCount) : this(File.OpenRead(filename), bufferCount) { }
public OggStream(Stream stream, int bufferCount = DefaultBufferCount)
{
BufferCount = bufferCount;
alBufferIds = AL.GenBuffers(bufferCount);
alSourceId = AL.GenSource();
if (ALHelper.XRam.IsInitialized)
{
ALHelper.XRam.SetBufferMode(BufferCount, ref alBufferIds[0], XRamExtension.XRamStorage.Hardware);
ALHelper.Check();
}
if (ALHelper.Efx.IsInitialized)
{
//alFilterId = ALHelper.Efx.GenFilter();
//ALHelper.Efx.Filter(alFilterId, EfxFilteri.FilterType, (int)EfxFilterType.Lowpass);
//ALHelper.Efx.Filter(alFilterId, EfxFilterf.LowpassGain, 1);
//ALHelper.Efx.BindFilterToSource(alSourceId, alFilterId);
//LowPassHFGain = 1;
}
underlyingStream = stream;
IsLooped = true;
}
public void Prepare()
{
if (Preparing) return;
var state = AL.GetSourceState(alSourceId);
lock (stopMutex)
{
switch (state)
{
case ALSourceState.Playing:
case ALSourceState.Paused:
return;
case ALSourceState.Stopped:
lock (prepareMutex)
{
Reader.DecodedTime = TimeSpan.Zero;
Ready = false;
Empty();
}
break;
}
if (!Ready)
{
lock (prepareMutex)
{
Preparing = true;
Open(precache: true);
}
}
}
}
public void Play(float volume)
{
var state = AL.GetSourceState(alSourceId);
switch (state)
{
case ALSourceState.Playing: return;
case ALSourceState.Paused:
Resume();
return;
}
Prepare();
AL.SourcePlay(alSourceId);
this.Volume = volume;
ALHelper.Check();
Preparing = false;
OggStreamer.Instance.AddStream(this);
}
public void Pause()
{
if (AL.GetSourceState(alSourceId) != ALSourceState.Playing)
return;
OggStreamer.Instance.RemoveStream(this);
AL.SourcePause(alSourceId);
ALHelper.Check();
}
public void Resume()
{
if (AL.GetSourceState(alSourceId) != ALSourceState.Paused)
return;
OggStreamer.Instance.AddStream(this);
AL.SourcePlay(alSourceId);
ALHelper.Check();
}
public void Stop()
{
var state = AL.GetSourceState(alSourceId);
if (state == ALSourceState.Playing || state == ALSourceState.Paused)
{
StopPlayback();
}
lock (stopMutex)
{
OggStreamer.Instance.RemoveStream(this);
}
}
/*float lowPassHfGain;
public float LowPassHFGain
{
get { return lowPassHfGain; }
set
{
if (ALHelper.Efx.IsInitialized)
{
ALHelper.Efx.Filter(alFilterId, EfxFilterf.LowpassGainHF, lowPassHfGain = value);
ALHelper.Efx.BindFilterToSource(alSourceId, alFilterId);
ALHelper.Check();
}
}
}*/
float volume;
public float Volume
{
get { return volume; }
set
{
AL.Source(alSourceId, ALSourcef.Gain, volume = value);
ALHelper.Check();
}
}
public bool IsLooped { get; set; }
public void Dispose()
{
var state = AL.GetSourceState(alSourceId);
if (state == ALSourceState.Playing || state == ALSourceState.Paused)
StopPlayback();
lock (prepareMutex)
{
OggStreamer.Instance.RemoveStream(this);
if (state != ALSourceState.Initial)
Empty();
Close();
underlyingStream.Dispose();
}
AL.DeleteSource(alSourceId);
AL.DeleteBuffers(alBufferIds);
/*if (ALHelper.Efx.IsInitialized)
ALHelper.Efx.DeleteFilter(alFilterId);*/
ALHelper.Check();
}
void StopPlayback()
{
AL.SourceStop(alSourceId);
ALHelper.Check();
}
void Empty()
{
int queued;
AL.GetSource(alSourceId, ALGetSourcei.BuffersQueued, out queued);
ALHelper.Check();
if (queued > 0)
{
try
{
AL.SourceUnqueueBuffers(alSourceId, queued);
if (AL.GetError() != ALError.NoError)
{
throw new InvalidOperationException();
}
}
catch (InvalidOperationException)
{
// This is a bug in the OpenAL implementation
// Salvage what we can
int processed;
AL.GetSource(alSourceId, ALGetSourcei.BuffersProcessed, out processed);
var salvaged = new int[processed];
if (processed > 0)
{
AL.SourceUnqueueBuffers(alSourceId, processed, salvaged);
ALHelper.Check();
}
// Try turning it off again?
AL.SourceStop(alSourceId);
ALHelper.Check();
Empty();
}
}
}
internal void Open(bool precache = false)
{
underlyingStream.Seek(0, SeekOrigin.Begin);
Reader = new VorbisReader(underlyingStream, false);
if (precache)
{
// Fill first buffer synchronously
OggStreamer.Instance.FillBuffer(this, alBufferIds[0]);
AL.SourceQueueBuffer(alSourceId, alBufferIds[0]);
ALHelper.Check();
// Schedule the others asynchronously
OggStreamer.Instance.AddStream(this);
}
Ready = true;
}
internal void Close()
{
if (Reader != null)
{
Reader.Dispose();
Reader = null;
}
Ready = false;
}
}
public class OggStreamer : IDisposable
{
const float DefaultUpdateRate = 10;
const int DefaultBufferSize = 44100;
static readonly object singletonMutex = new object();
readonly object iterationMutex = new object();
readonly object readMutex = new object();
readonly float[] readSampleBuffer;
readonly short[] castBuffer;
readonly HashSet<OggStream> streams = new HashSet<OggStream>();
readonly List<OggStream> threadLocalStreams = new List<OggStream>();
readonly Thread underlyingThread;
volatile bool cancelled;
public float UpdateRate { get; private set; }
public int BufferSize { get; private set; }
static OggStreamer instance;
public static OggStreamer Instance
{
get
{
lock (singletonMutex)
{
if (instance == null)
throw new InvalidOperationException("No instance running");
return instance;
}
}
private set { lock (singletonMutex) instance = value; }
}
public OggStreamer(int bufferSize = DefaultBufferSize, float updateRate = DefaultUpdateRate)
{
lock (singletonMutex)
{
if (instance != null)
throw new InvalidOperationException("Already running");
Instance = this;
underlyingThread = new Thread(EnsureBuffersFilled) { Priority = ThreadPriority.Lowest };
//background threads are automatically stopped when all foreground threads have been stopped
// -> the streaming thread won't stay running in the background if the main thread crashes
underlyingThread.IsBackground = true;
underlyingThread.Start();
}
UpdateRate = updateRate;
BufferSize = bufferSize;
readSampleBuffer = new float[bufferSize];
castBuffer = new short[bufferSize];
}
public void Dispose()
{
lock (singletonMutex)
{
Debug.Assert(Instance == this, "Two instances running, somehow...?");
cancelled = true;
lock (iterationMutex)
streams.Clear();
Instance = null;
}
}
internal bool AddStream(OggStream stream)
{
lock (iterationMutex)
return streams.Add(stream);
}
internal bool RemoveStream(OggStream stream)
{
lock (iterationMutex)
return streams.Remove(stream);
}
public bool FillBuffer(OggStream stream, int bufferId)
{
int readSamples;
lock (readMutex)
{
readSamples = stream.Reader.ReadSamples(readSampleBuffer, 0, BufferSize);
CastBuffer(readSampleBuffer, castBuffer, readSamples);
}
AL.BufferData(bufferId, stream.Reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, castBuffer,
readSamples * sizeof(short), stream.Reader.SampleRate);
ALHelper.Check();
return readSamples != BufferSize;
}
static void CastBuffer(float[] inBuffer, short[] outBuffer, int length)
{
for (int i = 0; i < length; i++)
{
var temp = (int)(32767f * inBuffer[i]);
if (temp > short.MaxValue) temp = short.MaxValue;
else if (temp < short.MinValue) temp = short.MinValue;
outBuffer[i] = (short)temp;
}
}
void EnsureBuffersFilled()
{
while (!cancelled)
{
Thread.Sleep((int)(1000 / UpdateRate));
if (cancelled) break;
threadLocalStreams.Clear();
lock (iterationMutex) threadLocalStreams.AddRange(streams);
foreach (var stream in threadLocalStreams)
{
lock (stream.prepareMutex)
{
lock (iterationMutex)
if (!streams.Contains(stream))
continue;
bool finished = false;
int queued;
AL.GetSource(stream.alSourceId, ALGetSourcei.BuffersQueued, out queued);
ALHelper.Check();
int processed;
AL.GetSource(stream.alSourceId, ALGetSourcei.BuffersProcessed, out processed);
ALHelper.Check();
if (processed == 0 && queued == stream.BufferCount) continue;
int[] tempBuffers;
if (processed > 0)
tempBuffers = AL.SourceUnqueueBuffers(stream.alSourceId, processed);
else
tempBuffers = stream.alBufferIds.Skip(queued).ToArray();
for (int i = 0; i < tempBuffers.Length; i++)
{
finished |= FillBuffer(stream, tempBuffers[i]);
if (finished)
{
if (stream.IsLooped)
stream.Reader.DecodedTime = TimeSpan.Zero;
else
{
streams.Remove(stream);
i = tempBuffers.Length;
}
}
}
AL.SourceQueueBuffers(stream.alSourceId, tempBuffers.Length, tempBuffers);
ALHelper.Check();
if (finished && !stream.IsLooped)
continue;
}
lock (stream.stopMutex)
{
if (stream.Preparing) continue;
lock (iterationMutex)
if (!streams.Contains(stream))
continue;
var state = AL.GetSourceState(stream.alSourceId);
if (state == ALSourceState.Stopped)
{
AL.SourcePlay(stream.alSourceId);
ALHelper.Check();
}
}
}
}
}
}
}
+277
View File
@@ -0,0 +1,277 @@
using System.Collections.Generic;
using System.IO;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using Barotrauma.Sounds;
using System;
namespace Barotrauma
{
public class Sound
{
public static Vector3 CameraPos;
private static List<Sound> loadedSounds = new List<Sound>();
private static OggStream stream;
private OggSound oggSound;
string filePath;
private int alSourceId;
private bool destroyOnGameEnd;
//public float Volume
//{
// set { SoundManager.Volume(sourceIndex, value); }
//}
private Sound(string file, bool destroyOnGameEnd)
{
filePath = file;
foreach (Sound loadedSound in loadedSounds)
{
if (loadedSound.filePath == file) oggSound = loadedSound.oggSound;
}
if (oggSound == null && !SoundManager.Disabled)
{
try
{
DebugConsole.Log("Loading sound " + file);
oggSound = OggSound.Load(file);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to load sound "+file+"!", e);
}
ALHelper.Check();
}
this.destroyOnGameEnd = destroyOnGameEnd;
loadedSounds.Add(this);
}
public string FilePath
{
get { return filePath; }
}
public int AlBufferId
{
get { return oggSound==null ? -1 : oggSound.AlBufferId; }
}
public static void Init()
{
SoundManager.Init();
}
public static Sound Load(string file, bool destroyOnGameEnd = true)
{
if (!File.Exists(file))
{
DebugConsole.ThrowError("File \"" + file + "\" not found!");
return null;
}
return new Sound(file, destroyOnGameEnd);
}
public int Play(float volume = 1.0f)
{
if (volume <= 0.0f) return -1;
alSourceId = SoundManager.Play(this, volume);
return alSourceId;
}
public int Play(float baseVolume, float range, Vector2 position)
{
//position = new Vector2(position.X - CameraPos.X, position.Y - CameraPos.Y);
//volume = (range == 0.0f) ? 0.0f : MathHelper.Clamp(volume * (range - position.Length())/range, 0.0f, 1.0f);
Vector2 relativePos = GetRelativePosition(position);
float volume = GetVolume(relativePos, range, baseVolume);
if (volume <= 0.0f) return -1;
alSourceId = SoundManager.Play(this, relativePos/100.0f, volume);
return alSourceId;
//if (newIndex == -1) return -1;
//return UpdatePosition(newIndex, position, range, volume);
}
//public int Play(float volume, float range, Body body)
//{
// //Vector2 bodyPosition = ConvertUnits.ToDisplayUnits(body.Position);
// //bodyPosition.Y = -bodyPosition.Y;
// alSourceId = Play(volume, range, ConvertUnits.ToDisplayUnits(body.Position));
// return alSourceId;
//}
private float GetVolume(Vector2 relativePosition, float range, float baseVolume)
{
float volume = (range == 0.0f) ? 0.0f : MathHelper.Clamp(baseVolume * (range - relativePosition.Length()) / range, 0.0f, 1.0f);
return volume;
}
private Vector2 GetRelativePosition(Vector2 position)
{
return new Vector2(position.X - CameraPos.X, position.Y - CameraPos.Y);
}
//public static int UpdatePosition(int sourceIndex, Vector2 position, float range, float baseVolume = 1.0f)
//{
// position = new Vector2(position.X - CameraPos.X, position.Y - CameraPos.Y);
// float volume = (range == 0.0f) ? 0.0f : MathHelper.Clamp(baseVolume * (range - position.Length())/range, 0.0f, 1.0f);
// if (volume <= 0.0f)
// {
// if (sourceIndex > 0)
// {
// SoundManager.Stop(sourceIndex);
// sourceIndex = -1;
// }
// return sourceIndex;
// }
// SoundManager.UpdateSoundPosition(sourceIndex, position, volume, volume);
// return sourceIndex;
//}
//public int UpdatePosition(int sourceIndex, Body body, float range, float baseVolume = 1.0f)
//{
// Vector2 bodyPosition = ConvertUnits.ToDisplayUnits(body.Position);
// bodyPosition.Y = -bodyPosition.Y;
// return UpdatePosition(sourceIndex, bodyPosition, range, baseVolume);
//}
public int Loop(int sourceIndex, float volume)
{
if (volume <= 0.0f)
{
if (sourceIndex > 0)
{
SoundManager.Stop(sourceIndex);
sourceIndex = -1;
}
return sourceIndex;
}
int newIndex = SoundManager.Loop(this, sourceIndex, volume);
return newIndex;
}
public int Loop(int sourceIndex, float baseVolume, Vector2 position, float range)
{
Vector2 relativePos = GetRelativePosition(position);
float volume = GetVolume(relativePos, range, baseVolume);
if (volume <= 0.0f)
{
if (sourceIndex > 0)
{
SoundManager.Stop(sourceIndex);
sourceIndex = -1;
}
return sourceIndex;
}
alSourceId = SoundManager.Loop(this, sourceIndex, relativePos / 100.0f, volume);
return alSourceId;
}
public bool IsPlaying
{
get
{
return SoundManager.IsPlaying(alSourceId);
}
}
public static void OnGameEnd()
{
List<Sound> removableSounds = loadedSounds.FindAll(s => s.destroyOnGameEnd);
foreach (Sound sound in removableSounds)
{
sound.Remove();
}
}
public void Remove()
{
//sound already removed?
if (!loadedSounds.Contains(this)) return;
loadedSounds.Remove(this);
if (alSourceId > 0 &&
(SoundManager.IsPlaying(alSourceId) || SoundManager.IsPaused(alSourceId)))
{
SoundManager.Stop(alSourceId);
ALHelper.Check();
}
foreach (Sound s in loadedSounds)
{
if (s.oggSound == oggSound) return;
}
SoundManager.ClearAlSource(AlBufferId);
ALHelper.Check();
if (oggSound != null)
{
oggSound.Dispose();
oggSound = null;
}
}
public static void StartStream(string file, float volume = 1.0f)
{
if (SoundManager.Disabled) return;
stream = SoundManager.StartStream(file, volume);
}
public static void StreamVolume(float volume = 1.0f)
{
if (SoundManager.Disabled) return;
stream.Volume = volume;
}
public static void StopStream()
{
if (stream != null) SoundManager.StopStream();
}
public static void Dispose()
{
SoundManager.Dispose();
}
}
}
@@ -0,0 +1,355 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using OpenTK.Audio.OpenAL;
using OpenTK.Audio;
using System;
namespace Barotrauma.Sounds
{
static class SoundManager
{
public static bool Disabled
{
get;
private set;
}
public const int DefaultSourceCount = 16;
private static readonly List<int> alSources = new List<int>();
private static readonly int[] alBuffers = new int[DefaultSourceCount];
private static int lowpassFilterId;
private static readonly Sound[] soundsPlaying = new Sound[DefaultSourceCount];
private static AudioContext AC;
private static OggStreamer oggStreamer;
private static OggStream oggStream;
public static float MasterVolume = 1.0f;
public static void Init()
{
var availableDevices = AudioContext.AvailableDevices;
if (availableDevices.Count == 0)
{
DebugConsole.ThrowError("No audio devices found. Disabling audio playback.");
Disabled = true;
return;
}
try
{
AC = new AudioContext();
ALHelper.Check();
}
catch (DllNotFoundException)
{
Program.CrashMessageBox("OpenAL32.dll not found");
throw;
}
for (int i = 0 ; i < DefaultSourceCount; i++)
{
alSources.Add(OpenTK.Audio.OpenAL.AL.GenSource());
}
ALHelper.Check();
if (ALHelper.Efx.IsInitialized)
{
lowpassFilterId = ALHelper.Efx.GenFilter();
//alFilters.Add(alFilterId);
ALHelper.Efx.Filter(lowpassFilterId, OpenTK.Audio.OpenAL.EfxFilteri.FilterType, (int)OpenTK.Audio.OpenAL.EfxFilterType.Lowpass);
LowPassHFGain = 1.0f;
}
}
public static int Play(Sound sound, float volume = 1.0f)
{
if (Disabled) return -1;
return Play(sound, Vector2.Zero, volume, 0.0f);
}
public static int Play(Sound sound, Vector2 position, float volume = 1.0f, float lowPassGain = 0.0f, bool loop=false)
{
if (Disabled || sound.AlBufferId == -1) return -1;
int sourceIndex = FindAudioSource(volume);
if (sourceIndex > -1)
{
soundsPlaying[sourceIndex] = sound;
alBuffers[sourceIndex] = sound.AlBufferId;
OpenTK.Audio.OpenAL.AL.Source(alSources[sourceIndex], OpenTK.Audio.OpenAL.ALSourceb.Looping, loop);
OpenTK.Audio.OpenAL.AL.Source(alSources[sourceIndex], OpenTK.Audio.OpenAL.ALSourcei.Buffer, sound.AlBufferId);
UpdateSoundPosition(sourceIndex, position, volume);
OpenTK.Audio.OpenAL.AL.SourcePlay(alSources[sourceIndex]);
}
return sourceIndex;
}
private static int FindAudioSource(float volume)
{
//find a source that's free to use (not playing or paused)
for (int i = 1; i < DefaultSourceCount; i++)
{
if (OpenTK.Audio.OpenAL.AL.GetSourceState(alSources[i]) == OpenTK.Audio.OpenAL.ALSourceState.Initial
|| OpenTK.Audio.OpenAL.AL.GetSourceState(alSources[i]) == OpenTK.Audio.OpenAL.ALSourceState.Stopped)
{
return i;
}
}
//not found -> take up the channel that is playing at the lowest volume
float lowestVolume = volume;
int quietestSourceIndex = -1;
for (int i = 1; i < DefaultSourceCount; i++)
{
float vol;
OpenTK.Audio.OpenAL.AL.GetSource(alSources[i], ALSourcef.Gain, out vol);
if (vol < lowestVolume)
{
quietestSourceIndex = i;
lowestVolume = vol;
}
}
if (quietestSourceIndex > -1)
{
Stop(quietestSourceIndex);
}
return quietestSourceIndex;
}
public static int Loop(Sound sound, int sourceIndex, float volume = 1.0f)
{
if (Disabled) return -1;
return Loop(sound,sourceIndex, Vector2.Zero, volume);
}
public static int Loop(Sound sound, int sourceIndex, Vector2 position, float volume = 1.0f)
{
if (Disabled) return -1;
if (!MathUtils.IsValid(volume))
{
volume = 0.0f;
}
if (sourceIndex<1 || soundsPlaying[sourceIndex] != sound)
{
sourceIndex = Play(sound, position, volume, 0.0f, true);
}
else
{
UpdateSoundPosition(sourceIndex, position, volume);
AL.Source(alSources[sourceIndex], ALSourceb.Looping, true);
}
ALHelper.Check();
return sourceIndex;
}
public static void Pause(int sourceIndex)
{
if (Disabled) return;
if (AL.GetSourceState(alSources[sourceIndex]) != ALSourceState.Playing)
return;
AL.SourcePause(alSources[sourceIndex]);
ALHelper.Check();
}
public static void Resume(int sourceIndex)
{
if (Disabled) return;
if (AL.GetSourceState(alSources[sourceIndex]) != ALSourceState.Paused)
return;
AL.SourcePlay(alSources[sourceIndex]);
ALHelper.Check();
}
public static void Stop(int sourceIndex)
{
if (Disabled) return;
if (sourceIndex < 1) return;
var state = AL.GetSourceState(alSources[sourceIndex]);
if (state == ALSourceState.Playing || state == ALSourceState.Paused)
{
AL.SourceStop(alSources[sourceIndex]);
AL.Source(alSources[sourceIndex], ALSourceb.Looping, false);
soundsPlaying[sourceIndex] = null;
}
}
public static Sound GetPlayingSound(int sourceIndex)
{
if (Disabled) return null;
if (sourceIndex < 1 || sourceIndex>alSources.Count-1) return null;
if (AL.GetSourceState(alSources[sourceIndex]) != ALSourceState.Playing) return null;
return soundsPlaying[sourceIndex];
}
public static bool IsPlaying(int sourceIndex)
{
if (Disabled) return false;
if (sourceIndex < 1 || sourceIndex>alSources.Count-1) return false;
return AL.GetSourceState(alSources[sourceIndex]) == ALSourceState.Playing;
}
public static bool IsPaused(int sourceIndex)
{
if (Disabled) return false;
if (sourceIndex < 1 || sourceIndex > alSources.Count - 1) return false;
return AL.GetSourceState(alSources[sourceIndex]) == ALSourceState.Paused;
}
public static bool IsLooping(int sourceIndex)
{
if (Disabled) return false;
if (sourceIndex < 1 || sourceIndex > alSources.Count - 1) return false;
bool isLooping;
OpenTK.Audio.OpenAL.AL.GetSource(alSources[sourceIndex], OpenTK.Audio.OpenAL.ALSourceb.Looping, out isLooping);
return isLooping;
}
static float lowPassHfGain;
public static float LowPassHFGain
{
get { return lowPassHfGain; }
set
{
if (Disabled) return;
if (ALHelper.Efx.IsInitialized)
{
lowPassHfGain = value;
for (int i = 0; i < DefaultSourceCount; i++)
{
//find a source that's free to use (not playing or paused)
if (OpenTK.Audio.OpenAL.AL.GetSourceState(alSources[i]) != OpenTK.Audio.OpenAL.ALSourceState.Playing
&& OpenTK.Audio.OpenAL.AL.GetSourceState(alSources[i])!= OpenTK.Audio.OpenAL.ALSourceState.Paused) continue;
ALHelper.Efx.Filter(lowpassFilterId, OpenTK.Audio.OpenAL.EfxFilterf.LowpassGainHF, lowPassHfGain = value);
ALHelper.Efx.BindFilterToSource(alSources[i], lowpassFilterId);
ALHelper.Check();
}
}
}
}
public static void UpdateSoundPosition(int sourceIndex, Vector2 position, float baseVolume = 1.0f)
{
if (sourceIndex < 1 || Disabled) return;
if (!MathUtils.IsValid(position))
{
position = Vector2.Zero;
}
position /= 1000.0f;
OpenTK.Audio.OpenAL.AL.Source(alSources[sourceIndex], OpenTK.Audio.OpenAL.ALSourcef.Gain, baseVolume * MasterVolume);
OpenTK.Audio.OpenAL.AL.Source(alSources[sourceIndex], OpenTK.Audio.OpenAL.ALSource3f.Position, position.X, position.Y, 0.0f);
float lowPassGain = lowPassHfGain / Math.Max(position.Length() * 5.0f, 1.0f);
ALHelper.Efx.Filter(lowpassFilterId, OpenTK.Audio.OpenAL.EfxFilterf.LowpassGainHF, lowPassGain);
ALHelper.Efx.BindFilterToSource(alSources[sourceIndex], lowpassFilterId);
ALHelper.Check();
}
public static OggStream StartStream(string file, float volume = 1.0f)
{
if (Disabled) return null;
if (oggStreamer == null)
oggStreamer = new OggStreamer();
oggStream = new OggStream(file);
oggStreamer.AddStream(oggStream);
oggStream.Play(volume);
ALHelper.Check();
return oggStream;
}
public static void StopStream()
{
if (oggStream != null) oggStream.Stop();
}
public static void ClearAlSource(int bufferId)
{
for (int i = 1; i < DefaultSourceCount; i++)
{
if (alBuffers[i] != bufferId) continue;
OpenTK.Audio.OpenAL.AL.Source(alSources[i], OpenTK.Audio.OpenAL.ALSourceb.Looping, false);
OpenTK.Audio.OpenAL.AL.Source(alSources[i], OpenTK.Audio.OpenAL.ALSourcei.Buffer, 0);
}
}
public static void Dispose()
{
if (Disabled) return;
if (ALHelper.Efx.IsInitialized)
ALHelper.Efx.DeleteFilter(lowpassFilterId);
for (int i = 0; i < DefaultSourceCount; i++)
{
var state = OpenTK.Audio.OpenAL.AL.GetSourceState(alSources[i]);
if (state == OpenTK.Audio.OpenAL.ALSourceState.Playing || state == OpenTK.Audio.OpenAL.ALSourceState.Paused)
{
Stop(i);
}
OpenTK.Audio.OpenAL.AL.DeleteSource(alSources[i]);
ALHelper.Check();
}
if (oggStream != null)
{
oggStream.Stop();
oggStream.Dispose();
oggStream = null;
}
if (oggStreamer != null)
{
oggStreamer.Dispose();
oggStreamer = null;
}
}
}
}
@@ -0,0 +1,428 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using Barotrauma.Sounds;
using System.Collections.Generic;
using System.IO;
namespace Barotrauma
{
public enum DamageSoundType
{
None,
StructureBlunt, StructureSlash,
LimbBlunt, LimbSlash, LimbArmor
}
public struct DamageSound
{
//the range of inflicted damage where the sound can be played
//(10.0f, 30.0f) would be played when the inflicted damage is between 10 and 30
public readonly Vector2 damageRange;
public readonly DamageSoundType damageType;
public readonly Sound sound;
public readonly string requiredTag;
public DamageSound(Sound sound, Vector2 damageRange, DamageSoundType damageType, string requiredTag = "")
{
this.sound = sound;
this.damageRange = damageRange;
this.damageType = damageType;
this.requiredTag = requiredTag;
}
}
public class BackgroundMusic
{
public readonly string file;
public readonly string type;
public readonly Vector2 priorityRange;
public BackgroundMusic(string file, string type, Vector2 priorityRange)
{
this.file = file;
this.type = type;
this.priorityRange = priorityRange;
}
}
static class SoundPlayer
{
private static ILookup<string, Sound> miscSounds;
//music
public static float MusicVolume = 1.0f;
private const float MusicLerpSpeed = 0.1f;
private static BackgroundMusic currentMusic;
private static BackgroundMusic targetMusic;
private static BackgroundMusic[] musicClips;
private static float currMusicVolume;
//ambience
private static Sound[] waterAmbiences = new Sound[2];
private static int[] waterAmbienceIndexes = new int[2];
private static float ambientSoundTimer;
private static Vector2 ambientSoundInterval = new Vector2(20.0f, 40.0f); //x = min, y = max
//misc
public static Sound[] flowSounds = new Sound[3];
public static Sound[] SplashSounds = new Sound[10];
private static List<DamageSound> damageSounds;
private static Sound startDrone;
public static bool Initialized;
public static string OverrideMusicType
{
get;
set;
}
public static int SoundCount;
public static IEnumerable<object> Init()
{
OverrideMusicType = null;
XDocument doc = ToolBox.TryLoadXml("Content/Sounds/sounds.xml");
if (doc == null) yield return CoroutineStatus.Failure;
SoundCount = 16 + doc.Root.Elements().Count();
startDrone = Sound.Load("Content/Sounds/startDrone.ogg", false);
startDrone.Play();
yield return CoroutineStatus.Running;
waterAmbiences[0] = Sound.Load("Content/Sounds/Water/WaterAmbience1.ogg", false);
yield return CoroutineStatus.Running;
waterAmbiences[1] = Sound.Load("Content/Sounds/Water/WaterAmbience2.ogg", false);
yield return CoroutineStatus.Running;
flowSounds[0] = Sound.Load("Content/Sounds/Water/FlowSmall.ogg", false);
yield return CoroutineStatus.Running;
flowSounds[1] = Sound.Load("Content/Sounds/Water/FlowMedium.ogg", false);
yield return CoroutineStatus.Running;
flowSounds[2] = Sound.Load("Content/Sounds/Water/FlowLarge.ogg", false);
yield return CoroutineStatus.Running;
for (int i = 0; i < 10; i++ )
{
SplashSounds[i] = Sound.Load("Content/Sounds/Water/Splash"+(i)+".ogg", false);
yield return CoroutineStatus.Running;
}
var xMusic = doc.Root.Elements("music").ToList();
if (xMusic.Any())
{
musicClips = new BackgroundMusic[xMusic.Count];
int i = 0;
foreach (XElement element in xMusic)
{
string file = ToolBox.GetAttributeString(element, "file", "");
string type = ToolBox.GetAttributeString(element, "type", "").ToLowerInvariant();
Vector2 priority = ToolBox.GetAttributeVector2(element, "priorityrange", new Vector2(0.0f, 100.0f));
musicClips[i] = new BackgroundMusic(file, type, priority);
yield return CoroutineStatus.Running;
i++;
}
}
List<KeyValuePair<string, Sound>> miscSoundList = new List<KeyValuePair<string, Sound>>();
damageSounds = new List<DamageSound>();
foreach (XElement subElement in doc.Root.Elements())
{
yield return CoroutineStatus.Running;
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "music":
continue;
case "damagesound":
Sound damageSound = Sound.Load(ToolBox.GetAttributeString(subElement, "file", ""), false);
if (damageSound == null) continue;
DamageSoundType damageSoundType = DamageSoundType.None;
Enum.TryParse<DamageSoundType>(ToolBox.GetAttributeString(subElement, "damagesoundtype", "None"), false, out damageSoundType);
damageSounds.Add(new DamageSound(
damageSound,
ToolBox.GetAttributeVector2(subElement, "damagerange", new Vector2(0.0f, 100.0f)),
damageSoundType,
ToolBox.GetAttributeString(subElement, "requiredtag", "")));
break;
default:
Sound sound = Sound.Load(ToolBox.GetAttributeString(subElement, "file", ""), false);
if (sound != null)
{
miscSoundList.Add(new KeyValuePair<string, Sound>(subElement.Name.ToString().ToLowerInvariant(), sound));
}
break;
}
}
miscSounds = miscSoundList.ToLookup(kvp => kvp.Key, kvp => kvp.Value);
Initialized = true;
yield return CoroutineStatus.Success;
}
public static void Update()
{
UpdateMusic();
if (startDrone != null && !startDrone.IsPlaying)
{
startDrone.Remove();
startDrone = null;
}
//stop submarine ambient sounds if no sub is loaded
if (Submarine.MainSub == null)
{
for (int i = 0; i < waterAmbienceIndexes.Length; i++)
{
if (waterAmbienceIndexes[i] <= 0) continue;
SoundManager.Stop(waterAmbienceIndexes[i]);
waterAmbienceIndexes[i] = 0;
}
return;
}
float ambienceVolume = 0.8f;
float lowpassHFGain = 1.0f;
if (Character.Controlled != null)
{
AnimController animController = Character.Controlled.AnimController;
if (animController.HeadInWater)
{
ambienceVolume = 1.0f;
ambienceVolume += animController.Limbs[0].LinearVelocity.Length();
lowpassHFGain = 0.2f;
}
lowpassHFGain *= Character.Controlled.LowPassMultiplier;
}
//how fast the sub is moving, scaled to 0.0 -> 1.0
float movementSoundVolume = 0.0f;
foreach (Submarine sub in Submarine.Loaded)
{
float movementFactor = (sub.Velocity == Vector2.Zero) ? 0.0f : sub.Velocity.Length() / 10.0f;
movementFactor = MathHelper.Clamp(movementFactor, 0.0f, 1.0f);
if (Character.Controlled==null || Character.Controlled.Submarine != sub)
{
float dist = Vector2.Distance(GameMain.GameScreen.Cam.WorldViewCenter, sub.WorldPosition);
movementFactor = movementFactor / Math.Max(dist / 1000.0f, 1.0f);
}
movementSoundVolume = Math.Max(movementSoundVolume, movementFactor);
}
if (ambientSoundTimer > 0.0f)
{
ambientSoundTimer -= (float)Timing.Step;
}
else
{
PlaySound(
"ambient",
Rand.Range(0.5f, 1.0f),
1000.0f,
new Vector2(Sound.CameraPos.X, Sound.CameraPos.Y) + Rand.Vector(100.0f));
ambientSoundTimer = Rand.Range(ambientSoundInterval.X, ambientSoundInterval.Y);
}
SoundManager.LowPassHFGain = lowpassHFGain;
waterAmbienceIndexes[0] = waterAmbiences[0].Loop(waterAmbienceIndexes[0], ambienceVolume * (1.0f - movementSoundVolume));
waterAmbienceIndexes[1] = waterAmbiences[1].Loop(waterAmbienceIndexes[1], ambienceVolume * movementSoundVolume);
}
public static Sound GetSound(string soundTag)
{
var matchingSounds = miscSounds[soundTag].ToList();
if (matchingSounds.Count == 0) return null;
return matchingSounds[Rand.Int(matchingSounds.Count)];
}
public static void PlaySound(string soundTag, float volume = 1.0f)
{
var sound = GetSound(soundTag);
if (sound != null) sound.Play(volume);
}
public static void PlaySound(string soundTag, float volume, float range, Vector2 position)
{
var sound = GetSound(soundTag);
if (sound != null) sound.Play(volume, range, position);
}
private static void UpdateMusic()
{
if (musicClips == null) return;
List<BackgroundMusic> suitableMusic = GetSuitableMusicClips();
if (suitableMusic.Count == 0)
{
targetMusic = null;
}
else if (!suitableMusic.Contains(currentMusic))
{
int index = Rand.Int(suitableMusic.Count);
if (currentMusic == null || suitableMusic[index].file != currentMusic.file)
{
targetMusic = suitableMusic[index];
}
}
if (targetMusic == null || currentMusic == null || targetMusic.file != currentMusic.file)
{
currMusicVolume = MathHelper.Lerp(currMusicVolume, 0.0f, MusicLerpSpeed);
if (currentMusic != null) Sound.StreamVolume(currMusicVolume);
if (currMusicVolume < 0.01f)
{
Sound.StopStream();
try
{
if (targetMusic != null) Sound.StartStream(targetMusic.file, currMusicVolume);
}
catch (FileNotFoundException e)
{
DebugConsole.ThrowError("Music clip " + targetMusic.file + " not found!", e);
}
currentMusic = targetMusic;
}
}
else
{
currMusicVolume = MathHelper.Lerp(currMusicVolume, MusicVolume, MusicLerpSpeed);
Sound.StreamVolume(currMusicVolume);
}
}
public static void SwitchMusic()
{
var suitableMusic = GetSuitableMusicClips();
if (suitableMusic.Count > 1)
{
targetMusic = suitableMusic.Find(m => m != currentMusic);
}
}
private static List<BackgroundMusic> GetSuitableMusicClips()
{
string musicType = "default";
if (OverrideMusicType != null)
{
musicType = OverrideMusicType;
}
else if (Character.Controlled != null &&
Level.Loaded != null && Level.Loaded.Ruins != null &&
Level.Loaded.Ruins.Any(r => r.Area.Contains(Character.Controlled.WorldPosition)))
{
musicType = "ruins";
}
else if ((Character.Controlled != null && Character.Controlled.Submarine != null && Character.Controlled.Submarine.AtDamageDepth) ||
(Screen.Selected == GameMain.GameScreen && GameMain.GameScreen.Cam.Position.Y < SubmarineBody.DamageDepth))
{
musicType = "deep";
}
else
{
Task criticalTask = null;
if (GameMain.GameSession != null && GameMain.GameSession.TaskManager != null)
{
foreach (Task task in GameMain.GameSession.TaskManager.Tasks)
{
if (!task.IsStarted) continue;
if (criticalTask == null || task.Priority > criticalTask.Priority)
{
criticalTask = task;
}
}
}
if (criticalTask != null)
{
var suitableClips =
musicClips.Where(music =>
music != null &&
music.type == criticalTask.MusicType &&
music.priorityRange.X < criticalTask.Priority &&
music.priorityRange.Y > criticalTask.Priority).ToList();
if (suitableClips.Count > 0) return suitableClips;
}
}
return musicClips.Where(music => music != null && music.type == musicType).ToList();
}
public static void PlaySplashSound(Vector2 worldPosition, float strength)
{
int splashIndex = MathHelper.Clamp((int)(strength + Rand.Range(-2,2)), 0, SplashSounds.Length-1);
SplashSounds[splashIndex].Play(1.0f, 800.0f, worldPosition);
}
public static void PlayDamageSound(DamageSoundType damageType, float damage, PhysicsBody body)
{
Vector2 bodyPosition = body.DrawPosition;
PlayDamageSound(damageType, damage, bodyPosition, 800.0f);
}
public static void PlayDamageSound(DamageSoundType damageType, float damage, Vector2 position, float range = 2000.0f, List<string> tags = null)
{
damage = MathHelper.Clamp(damage+Rand.Range(-10.0f, 10.0f), 0.0f, 100.0f);
var sounds = damageSounds.FindAll(s =>
damage >= s.damageRange.X &&
damage <= s.damageRange.Y &&
s.damageType == damageType &&
(string.IsNullOrEmpty(s.requiredTag) || (tags != null && tags.Contains(s.requiredTag))));
if (!sounds.Any()) return;
int selectedSound = Rand.Int(sounds.Count);
sounds[selectedSound].sound.Play(1.0f, range, position);
Debug.WriteLine("playing: " + sounds[selectedSound].sound);
}
}
}
+399
View File
@@ -0,0 +1,399 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Xml.Linq;
namespace Barotrauma
{
public class Sprite
{
static List<Sprite> list = new List<Sprite>();
//the file from which the texture is loaded
//if two sprites use the same file, they share the same texture
string file;
protected Texture2D texture;
//the area in the texture that is supposed to be drawn
Rectangle sourceRect;
//the offset used when drawing the sprite
protected Vector2 offset;
protected Vector2 origin;
//the size of the drawn sprite, if larger than the source,
//the sprite is tiled to fill the target size
public Vector2 size;
public float rotation;
public SpriteEffects effects;
protected float depth;
public Rectangle SourceRect
{
get { return sourceRect; }
set { sourceRect = value; }
}
public float Depth
{
get { return depth; }
set { depth = MathHelper.Clamp(value, 0.0f, 1.0f); }
}
public Vector2 Origin
{
get { return origin; }
set { origin = value; }
}
public Texture2D Texture
{
get { return texture; }
}
public string FilePath
{
get { return file; }
}
public override string ToString()
{
return FilePath + ": " + sourceRect;
}
public Sprite(XElement element, string path = "", string file = "")
{
if (file == "")
{
file = ToolBox.GetAttributeString(element, "texture", "");
}
if (file == "")
{
DebugConsole.ThrowError("Sprite " + element + " doesn't have a texture specified!");
return;
}
if (!string.IsNullOrEmpty(path))
{
if (!path.EndsWith("/")) path += "/";
}
this.file = path + file;
texture = LoadTexture(this.file);
if (texture == null) return;
Vector4 sourceVector = ToolBox.GetAttributeVector4(element, "sourcerect", Vector4.Zero);
if (sourceVector.Z == 0.0f) sourceVector.Z = texture.Width;
if (sourceVector.W == 0.0f) sourceVector.W = texture.Height;
sourceRect = new Rectangle(
(int)sourceVector.X, (int)sourceVector.Y,
(int)sourceVector.Z, (int)sourceVector.W);
origin = ToolBox.GetAttributeVector2(element, "origin", new Vector2(0.5f, 0.5f));
origin.X = origin.X * sourceRect.Width;
origin.Y = origin.Y * sourceRect.Height;
size = ToolBox.GetAttributeVector2(element, "size", Vector2.One);
size.X *= sourceRect.Width;
size.Y *= sourceRect.Height;
Depth = ToolBox.GetAttributeFloat(element, "depth", 0.0f);
list.Add(this);
}
public Sprite(string newFile, Vector2 newOrigin)
{
file = newFile;
texture = LoadTexture(file);
if (texture == null) return;
sourceRect = new Rectangle(0, 0, texture.Width, texture.Height);
size = new Vector2(sourceRect.Width, sourceRect.Height);
origin = new Vector2((float)texture.Width * newOrigin.X, (float)texture.Height * newOrigin.Y);
effects = SpriteEffects.None;
list.Add(this);
}
public Sprite(Texture2D texture, Rectangle? sourceRectangle, Vector2? newOffset, float newRotation = 0.0f)
{
this.texture = texture;
sourceRect = sourceRectangle ?? new Rectangle(0, 0, texture.Width, texture.Height);
offset = newOffset ?? Vector2.Zero;
size = new Vector2(sourceRect.Width, sourceRect.Height);
origin = Vector2.Zero;
effects = SpriteEffects.None;
rotation = newRotation;
list.Add(this);
}
public Sprite(string newFile, Rectangle? sourceRectangle, Vector2? newOffset, float newRotation = 0.0f)
{
file = newFile;
texture = LoadTexture(file);
sourceRect = sourceRectangle ?? new Rectangle(0, 0, texture.Width, texture.Height);
offset = newOffset ?? Vector2.Zero;
size = new Vector2(sourceRect.Width, sourceRect.Height);
origin = Vector2.Zero;
rotation = newRotation;
list.Add(this);
}
public static Texture2D LoadTexture(string file)
{
foreach (Sprite s in list)
{
if (s.file == file) return s.texture;
}
if (File.Exists(file))
{
return TextureLoader.FromFile(file);
}
else
{
DebugConsole.ThrowError("Sprite \""+file+"\" not found!");
}
return null;
}
public void Draw(SpriteBatch spriteBatch, Vector2 pos, float rotate=0.0f, float scale=1.0f, SpriteEffects spriteEffect = SpriteEffects.None)
{
this.Draw(spriteBatch, pos, Color.White, rotate, scale, spriteEffect);
}
public void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null)
{
this.Draw(spriteBatch, pos, color, this.origin, rotate, new Vector2(scale,scale), spriteEffect, depth);
}
public void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null)
{
this.Draw(spriteBatch, pos, color, origin, rotate, new Vector2(scale, scale), spriteEffect, depth);
}
public virtual void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null)
{
//for (int x = -1; x <= 1; x += 2)
//{
// for (int y = -1; y <= 1; y += 2)
// {
// spriteBatch.Draw(texture, pos + offset + new Vector2(x, y) * 1.0f, sourceRect, Color.Black, rotation + rotate, origin, scale, spriteEffect, (depth == null ? this.depth : (float)depth) + 0.0001f);
// }
//}
if (texture == null) return;
spriteBatch.Draw(texture, pos + offset, sourceRect, color, rotation + rotate, origin, scale, spriteEffect, depth == null ? this.depth : (float)depth);
}
public void DrawTiled(SpriteBatch spriteBatch, Vector2 pos, Vector2 targetSize, Color color)
{
DrawTiled(spriteBatch, pos, targetSize, Vector2.Zero, color);
}
public void DrawTiled(SpriteBatch spriteBatch, Vector2 pos, Vector2 targetSize, Color color, Point offset, float? overrideDepth = null)
{
//how many times the texture needs to be drawn on the x-axis
int xTiles = (int)Math.Ceiling(targetSize.X / sourceRect.Width);
//how many times the texture needs to be drawn on the y-axis
int yTiles = (int)Math.Ceiling(targetSize.Y / sourceRect.Height);
float depth = overrideDepth == null ? this.depth : (float)overrideDepth;
Rectangle texPerspective = sourceRect;
texPerspective.Location += offset;
while (texPerspective.X >= sourceRect.Right)
texPerspective.X = sourceRect.X + (texPerspective.X - sourceRect.Right);
while (texPerspective.Y >= sourceRect.Bottom)
texPerspective.Y = sourceRect.Y + (texPerspective.Y - sourceRect.Bottom);
float top = pos.Y;
texPerspective.Height = (int)Math.Min(targetSize.Y, sourceRect.Height);
for (int y = 0; y < yTiles; y++)
{
var movementY = texPerspective.Height;
texPerspective.Height = Math.Min((int)(targetSize.Y - texPerspective.Height * y), texPerspective.Height);
float left = pos.X;
texPerspective.Width = (int)Math.Min(targetSize.X, sourceRect.Width);
for (int x = 0; x < xTiles; x++)
{
var movementX = texPerspective.Width;
texPerspective.Width = Math.Min((int)(targetSize.X - texPerspective.Width * x), texPerspective.Width);
if (texPerspective.Right > sourceRect.Right)
{
int diff = texPerspective.Right - sourceRect.Right;
if (effects.HasFlag(SpriteEffects.FlipHorizontally))
{
spriteBatch.Draw(texture,
new Vector2(left, top),
new Rectangle(sourceRect.X, texPerspective.Y, diff, texPerspective.Height),
color, rotation, Vector2.Zero, 1.0f, effects, depth);
texPerspective.Width -= diff;
left += diff;
}
else
{
texPerspective.Width -= (int)diff;
spriteBatch.Draw(texture,
new Vector2(left + texPerspective.Width, top),
new Rectangle(sourceRect.X, texPerspective.Y, (int)diff, texPerspective.Height),
color, rotation, Vector2.Zero, 1.0f, effects, depth);
}
}
else if (texPerspective.Bottom > sourceRect.Bottom)
{
int diff = texPerspective.Bottom - sourceRect.Bottom;
texPerspective.Height -= diff;
spriteBatch.Draw(texture,
new Vector2(left, top + texPerspective.Height),
new Rectangle(texPerspective.X, sourceRect.Y, texPerspective.Width, diff),
color, rotation, Vector2.Zero, 1.0f, effects, depth);
}
spriteBatch.Draw(texture, new Vector2(left, top), texPerspective, color, rotation, Vector2.Zero, 1.0f, effects, depth);
if (texPerspective.X + movementX >= sourceRect.Right) texPerspective.X = sourceRect.X;
left += movementX;
}
if (texPerspective.Y + movementY >= sourceRect.Bottom) texPerspective.Y = sourceRect.Y;
top += movementY;
}
}
public void DrawTiled(SpriteBatch spriteBatch, Vector2 pos, Vector2 targetSize, Vector2 startOffset, Color color)
{
DrawTiled(spriteBatch, pos, targetSize, startOffset, sourceRect, color);
}
public void DrawTiled(SpriteBatch spriteBatch, Vector2 pos, Vector2 targetSize, Vector2 startOffset, Rectangle sourceRect, Color color)
{
//pos.X = (int)pos.X;
//pos.Y = (int)pos.Y;
//how many times the texture needs to be drawn on the x-axis
int xTiles = (int)Math.Ceiling((targetSize.X+startOffset.X) / sourceRect.Width);
//how many times the texture needs to be drawn on the y-axis
int yTiles = (int)Math.Ceiling((targetSize.Y+startOffset.Y) / sourceRect.Height);
Vector2 position = pos - startOffset;
Rectangle drawRect = sourceRect;
position.X = pos.X;
for (int x = 0; x < xTiles; x++)
{
drawRect.X = sourceRect.X;
drawRect.Height = sourceRect.Height;
if (x == xTiles - 1)
{
drawRect.Width -= (int)((position.X + sourceRect.Width) - (pos.X + targetSize.X));
}
else
{
drawRect.Width = sourceRect.Width;
}
if (position.X < pos.X)
{
float diff = pos.X - position.X;
position.X += diff;
drawRect.Width -= (int)diff;
drawRect.X += (int)diff;
}
position.Y = pos.Y;
for (int y = 0; y < yTiles; y++)
{
drawRect.Y = sourceRect.Y;
if (y == yTiles - 1)
{
drawRect.Height -= (int)((position.Y + sourceRect.Height) - (pos.Y + targetSize.Y));
}
else
{
drawRect.Height = sourceRect.Height;
}
if (position.Y < pos.Y)
{
int diff = (int)(pos.Y - position.Y);
position.Y += diff;
drawRect.Height -= diff;
drawRect.Y += diff;
}
spriteBatch.Draw(texture, position,
drawRect, color, rotation, Vector2.Zero, 1.0f, effects, depth);
position.Y += sourceRect.Height;
}
position.X += sourceRect.Width;
}
}
public void Remove()
{
list.Remove(this);
//check if another sprite is using the same texture
foreach (Sprite s in list)
{
if (s.file == file) return;
}
//if not, free the texture
if (texture != null)
{
texture.Dispose();
texture = null;
}
}
}
}
+55
View File
@@ -0,0 +1,55 @@
using Microsoft.Xna.Framework;
using System;
using System.Xml.Linq;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
{
class SpriteSheet : Sprite
{
private Rectangle[] sourceRects;
public int FrameCount
{
get { return sourceRects.Length; }
}
public SpriteSheet(XElement element, string path = "", string file = "")
: base(element, path, file)
{
int columnCount = Math.Max(ToolBox.GetAttributeInt(element, "columns", 1), 1);
int rowCount = Math.Max(ToolBox.GetAttributeInt(element, "rows", 1), 1);
sourceRects = new Rectangle[rowCount * columnCount];
int cellWidth = SourceRect.Width / columnCount;
int cellHeight = SourceRect.Height / rowCount;
for (int x = 0; x < columnCount; x++)
{
for (int y = 0; y < rowCount; y++)
{
sourceRects[x + y * columnCount] = new Rectangle(x * cellWidth, y * cellHeight, cellWidth, cellHeight);
}
}
origin = ToolBox.GetAttributeVector2(element, "origin", new Vector2(0.5f, 0.5f));
origin.X = origin.X * cellWidth;
origin.Y = origin.Y * cellHeight;
}
public override void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = default(float?))
{
if (texture == null) return;
spriteBatch.Draw(texture, pos + offset, sourceRects[0], color, rotation + rotate, origin, scale, spriteEffect, depth == null ? this.depth : (float)depth);
}
public void Draw(SpriteBatch spriteBatch, int spriteIndex, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = default(float?))
{
if (texture == null) return;
spriteBatch.Draw(texture, pos + offset, sourceRects[spriteIndex], color, rotation + rotate, origin, scale, spriteEffect, depth == null ? this.depth : (float)depth);
}
}
}
@@ -0,0 +1,106 @@
using System.IO;
using Microsoft.Xna.Framework.Graphics;
using Color = Microsoft.Xna.Framework.Color;
using System;
namespace Barotrauma
{
/// <summary>
/// Based on http://jakepoz.com/jake_poznanski__background_load_xna.html
/// </summary>
public static class TextureLoader
{
static TextureLoader()
{
BlendColorBlendState = new BlendState
{
ColorDestinationBlend = Blend.Zero,
ColorWriteChannels = ColorWriteChannels.Red | ColorWriteChannels.Green | ColorWriteChannels.Blue,
AlphaDestinationBlend = Blend.Zero,
AlphaSourceBlend = Blend.SourceAlpha,
ColorSourceBlend = Blend.SourceAlpha
};
BlendAlphaBlendState = new BlendState
{
ColorWriteChannels = ColorWriteChannels.Alpha,
AlphaDestinationBlend = Blend.Zero,
ColorDestinationBlend = Blend.Zero,
AlphaSourceBlend = Blend.One,
ColorSourceBlend = Blend.One
};
}
public static void Init(GraphicsDevice graphicsDevice, bool needsBmp = false)
{
_graphicsDevice = graphicsDevice;
_needsBmp = needsBmp;
_spriteBatch = new SpriteBatch(_graphicsDevice);
}
public static Texture2D FromFile(string path, bool preMultiplyAlpha = true)
{
try
{
using (Stream fileStream = File.OpenRead(path))
{
var texture = Texture2D.FromStream(_graphicsDevice, fileStream);
texture = PreMultiplyAlpha(texture);
return texture;
}
}
catch (Exception e)
{
DebugConsole.ThrowError("Loading texture \""+path+"\" failed!", e);
return null;
}
}
private static Texture2D PreMultiplyAlpha(Texture2D texture)
{
// Setup a render target to hold our final texture which will have premulitplied alpha values
using (RenderTarget2D renderTarget = new RenderTarget2D(_graphicsDevice, texture.Width, texture.Height))
{
Viewport viewportBackup = _graphicsDevice.Viewport;
_graphicsDevice.SetRenderTarget(renderTarget);
_graphicsDevice.Clear(Color.Black);
// Multiply each color by the source alpha, and write in just the color values into the final texture
_spriteBatch.Begin(SpriteSortMode.Immediate, BlendColorBlendState);
_spriteBatch.Draw(texture, texture.Bounds, Color.White);
_spriteBatch.End();
// Now copy over the alpha values from the source texture to the final one, without multiplying them
_spriteBatch.Begin(SpriteSortMode.Immediate, BlendAlphaBlendState);
_spriteBatch.Draw(texture, texture.Bounds, Color.White);
_spriteBatch.End();
// Release the GPU back to drawing to the screen
_graphicsDevice.SetRenderTarget(null);
_graphicsDevice.Viewport = viewportBackup;
// Store data from render target because the RenderTarget2D is volatile
Color[] data = new Color[texture.Width * texture.Height];
renderTarget.GetData(data);
// Unset texture from graphic device and set modified data back to it
_graphicsDevice.Textures[0] = null;
texture.SetData(data);
}
return texture;
}
private static readonly BlendState BlendColorBlendState;
private static readonly BlendState BlendAlphaBlendState;
private static GraphicsDevice _graphicsDevice;
private static SpriteBatch _spriteBatch;
private static bool _needsBmp;
}
}