diff --git a/BarotraumaClient/BarotraumaClient.csproj b/BarotraumaClient/BarotraumaClient.csproj
index 50f7f43e8..51cad3291 100644
--- a/BarotraumaClient/BarotraumaClient.csproj
+++ b/BarotraumaClient/BarotraumaClient.csproj
@@ -63,6 +63,8 @@
+
+
@@ -107,18 +109,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -141,7 +179,9 @@
+
+
diff --git a/BarotraumaShared/Source/Characters/AI/CrewCommander.cs b/BarotraumaClient/Source/Characters/AI/CrewCommander.cs
similarity index 100%
rename from BarotraumaShared/Source/Characters/AI/CrewCommander.cs
rename to BarotraumaClient/Source/Characters/AI/CrewCommander.cs
diff --git a/BarotraumaClient/Source/Characters/AICharacter.cs b/BarotraumaClient/Source/Characters/AICharacter.cs
new file mode 100644
index 000000000..9758b9833
--- /dev/null
+++ b/BarotraumaClient/Source/Characters/AICharacter.cs
@@ -0,0 +1,16 @@
+using Microsoft.Xna.Framework;
+using System;
+
+namespace Barotrauma
+{
+ partial class AICharacter : Character
+ {
+ public override void DrawFront(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch, Camera cam)
+ {
+ base.DrawFront(spriteBatch, cam);
+
+ if (GameMain.DebugDraw && !IsDead) aiController.DebugDraw(spriteBatch);
+ }
+
+ }
+}
diff --git a/BarotraumaClient/Source/Characters/Character.cs b/BarotraumaClient/Source/Characters/Character.cs
index f55956cac..51bb1e3c1 100644
--- a/BarotraumaClient/Source/Characters/Character.cs
+++ b/BarotraumaClient/Source/Characters/Character.cs
@@ -14,6 +14,9 @@ namespace Barotrauma
{
partial class Character : Entity, IDamageable, IPropertyObject, IClientSerializable, IServerSerializable
{
+ protected float soundTimer;
+ protected float soundInterval;
+
private List sounds;
//the Character that the player is currently controlling
diff --git a/BarotraumaClient/Source/Characters/Limb.cs b/BarotraumaClient/Source/Characters/Limb.cs
index 1e5fe7d4c..c8b86b7bd 100644
--- a/BarotraumaClient/Source/Characters/Limb.cs
+++ b/BarotraumaClient/Source/Characters/Limb.cs
@@ -15,6 +15,8 @@ namespace Barotrauma
{
partial class Limb
{
+ public readonly LightSource LightSource;
+
Sound hitSound;
public Sound HitSound
diff --git a/BarotraumaClient/Source/GUI/GUI.cs b/BarotraumaClient/Source/GUI/GUI.cs
index fca97836a..8625ad4b5 100644
--- a/BarotraumaClient/Source/GUI/GUI.cs
+++ b/BarotraumaClient/Source/GUI/GUI.cs
@@ -6,15 +6,6 @@ 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,
diff --git a/BarotraumaClient/Source/Items/Components/Door.cs b/BarotraumaClient/Source/Items/Components/Door.cs
new file mode 100644
index 000000000..7f3c8c79a
--- /dev/null
+++ b/BarotraumaClient/Source/Items/Components/Door.cs
@@ -0,0 +1,142 @@
+using Barotrauma.Lights;
+using Barotrauma.Networking;
+using FarseerPhysics;
+using FarseerPhysics.Dynamics;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml.Linq;
+
+
+namespace Barotrauma.Items.Components
+{
+ partial class Door : ItemComponent, IDrawableComponent, IServerSerializable
+ {
+ private ConvexHull convexHull;
+ private ConvexHull convexHull2;
+
+ private Vector2[] GetConvexHullCorners(Rectangle rect)
+ {
+ Vector2[] corners = new Vector2[4];
+ corners[0] = new Vector2(rect.X, rect.Y - rect.Height);
+ corners[1] = new Vector2(rect.X, rect.Y);
+ corners[2] = new Vector2(rect.Right, rect.Y);
+ corners[3] = new Vector2(rect.Right, rect.Y - rect.Height);
+
+ return corners;
+ }
+
+ private void UpdateConvexHulls()
+ {
+ doorRect = new Rectangle(
+ item.Rect.Center.X - (int)(doorSprite.size.X / 2),
+ item.Rect.Y - item.Rect.Height / 2 + (int)(doorSprite.size.Y / 2.0f),
+ (int)doorSprite.size.X,
+ (int)doorSprite.size.Y);
+
+ Rectangle rect = doorRect;
+ if (isHorizontal)
+ {
+ rect.Width = (int)(rect.Width * (1.0f - openState));
+ }
+ else
+ {
+ rect.Height = (int)(rect.Height * (1.0f - openState));
+ }
+
+ if (window.Height > 0 && window.Width > 0)
+ {
+ rect.Height = -window.Y;
+
+ rect.Y += (int)(doorRect.Height * openState);
+ rect.Height = Math.Max(rect.Height - (rect.Y - doorRect.Y), 0);
+ rect.Y = Math.Min(doorRect.Y, rect.Y);
+
+ if (convexHull2 != null)
+ {
+ Rectangle rect2 = doorRect;
+ rect2.Y = rect2.Y + window.Y - window.Height;
+
+ rect2.Y += (int)(doorRect.Height * openState);
+ rect2.Y = Math.Min(doorRect.Y, rect2.Y);
+ rect2.Height = rect2.Y - (doorRect.Y - (int)(doorRect.Height * (1.0f - openState)));
+ //convexHull2.SetVertices(GetConvexHullCorners(rect2));
+
+ if (rect2.Height == 0)
+ {
+ convexHull2.Enabled = false;
+ }
+ else
+ {
+ convexHull2.Enabled = true;
+ convexHull2.SetVertices(GetConvexHullCorners(rect2));
+ }
+ }
+ }
+
+ if (convexHull == null) return;
+
+ if (rect.Height == 0 || rect.Width == 0)
+ {
+ convexHull.Enabled = false;
+ }
+ else
+ {
+ convexHull.Enabled = true;
+ convexHull.SetVertices(GetConvexHullCorners(rect));
+ }
+ }
+
+ public void Draw(SpriteBatch spriteBatch, bool editing)
+ {
+ Color color = (item.IsSelected) ? Color.Green : Color.White;
+ color = color * (item.Condition / 100.0f);
+ color.A = 255;
+
+ //prefab.sprite.Draw(spriteBatch, new Vector2(rect.X, -rect.Y), new Vector2(rect.Width, rect.Height), color);
+
+ if (stuck > 0.0f && weldedSprite != null)
+ {
+ Vector2 weldSpritePos = new Vector2(item.Rect.Center.X, item.Rect.Y - item.Rect.Height / 2.0f);
+ if (item.Submarine != null) weldSpritePos += item.Submarine.Position;
+ weldSpritePos.Y = -weldSpritePos.Y;
+
+ weldedSprite.Draw(spriteBatch,
+ weldSpritePos, Color.White * (stuck / 100.0f), 0.0f, 1.0f);
+ }
+
+ if (openState == 1.0f)
+ {
+ body.Enabled = false;
+ return;
+ }
+
+ if (isHorizontal)
+ {
+ Vector2 pos = new Vector2(item.Rect.X, item.Rect.Y - item.Rect.Height / 2);
+ if (item.Submarine != null) pos += item.Submarine.DrawPosition;
+ pos.Y = -pos.Y;
+
+ spriteBatch.Draw(doorSprite.Texture, pos,
+ new Rectangle((int)(doorSprite.SourceRect.X + doorSprite.size.X * openState), (int)doorSprite.SourceRect.Y,
+ (int)(doorSprite.size.X * (1.0f - openState)), (int)doorSprite.size.Y),
+ color, 0.0f, doorSprite.Origin, 1.0f, SpriteEffects.None, doorSprite.Depth);
+ }
+ else
+ {
+ Vector2 pos = new Vector2(item.Rect.Center.X, item.Rect.Y);
+ if (item.Submarine != null) pos += item.Submarine.DrawPosition;
+ pos.Y = -pos.Y;
+
+ spriteBatch.Draw(doorSprite.Texture, pos,
+ new Rectangle(doorSprite.SourceRect.X, (int)(doorSprite.SourceRect.Y + doorSprite.size.Y * openState),
+ (int)doorSprite.size.X, (int)(doorSprite.size.Y * (1.0f - openState))),
+ color, 0.0f, doorSprite.Origin, 1.0f, SpriteEffects.None, doorSprite.Depth);
+ }
+
+ }
+ }
+}
diff --git a/BarotraumaClient/Source/Items/Components/ItemComponent.cs b/BarotraumaClient/Source/Items/Components/ItemComponent.cs
new file mode 100644
index 000000000..72140c4ba
--- /dev/null
+++ b/BarotraumaClient/Source/Items/Components/ItemComponent.cs
@@ -0,0 +1,273 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Xml.Linq;
+using Lidgren.Network;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Barotrauma.Networking;
+using System.IO;
+
+namespace Barotrauma.Items.Components
+{
+ class ItemSound
+ {
+ public readonly Sound Sound;
+ public readonly ActionType Type;
+
+ public string VolumeProperty;
+
+ public float VolumeMultiplier;
+
+ public readonly float Range;
+
+ public readonly bool Loop;
+
+ public ItemSound(Sound sound, ActionType type, float range, bool loop = false)
+ {
+ this.Sound = sound;
+ this.Type = type;
+ this.Range = range;
+
+ this.Loop = loop;
+ }
+ }
+
+ partial class ItemComponent : IPropertyObject
+ {
+ private Dictionary> sounds;
+
+ private GUIFrame guiFrame;
+
+ protected GUIFrame GuiFrame
+ {
+ get
+ {
+ if (guiFrame == null)
+ {
+ DebugConsole.ThrowError("Error: the component " + name + " in " + item.Name + " doesn't have a GuiFrame component");
+ guiFrame = new GUIFrame(new Rectangle(0, 0, 100, 100), Color.Black);
+ }
+ return guiFrame;
+ }
+ }
+
+ private ItemSound loopingSound;
+ private int loopingSoundIndex;
+ public void PlaySound(ActionType type, Vector2 position)
+ {
+ if (loopingSound != null)
+ {
+ loopingSoundIndex = loopingSound.Sound.Loop(loopingSoundIndex, GetSoundVolume(loopingSound), position, loopingSound.Range);
+ return;
+ }
+
+ List matchingSounds;
+ if (!sounds.TryGetValue(type, out matchingSounds)) return;
+
+ ItemSound itemSound = null;
+ if (!Sounds.SoundManager.IsPlaying(loopingSoundIndex))
+ {
+ int index = Rand.Int(matchingSounds.Count);
+ itemSound = matchingSounds[index];
+ }
+
+ if (itemSound == null) return;
+
+ if (itemSound.Loop)
+ {
+ loopingSound = itemSound;
+
+ loopingSoundIndex = loopingSound.Sound.Loop(loopingSoundIndex, GetSoundVolume(loopingSound), position, loopingSound.Range);
+ }
+ else
+ {
+ float volume = GetSoundVolume(itemSound);
+ if (volume == 0.0f) return;
+ itemSound.Sound.Play(volume, itemSound.Range, position);
+ }
+ }
+
+ public void StopSounds(ActionType type)
+ {
+ if (loopingSoundIndex <= 0) return;
+
+ if (loopingSound == null) return;
+
+ if (loopingSound.Type != type) return;
+
+ if (Sounds.SoundManager.IsPlaying(loopingSoundIndex))
+ {
+ Sounds.SoundManager.Stop(loopingSoundIndex);
+ loopingSound = null;
+ loopingSoundIndex = -1;
+ }
+ }
+
+ private float GetSoundVolume(ItemSound sound)
+ {
+ if (sound == null) return 0.0f;
+ if (sound.VolumeProperty == "") return 1.0f;
+
+ ObjectProperty op = null;
+ if (properties.TryGetValue(sound.VolumeProperty.ToLowerInvariant(), out op))
+ {
+ float newVolume = 0.0f;
+ try
+ {
+ newVolume = (float)op.GetValue();
+ }
+ catch
+ {
+ return 0.0f;
+ }
+ newVolume *= sound.VolumeMultiplier;
+
+ return MathHelper.Clamp(newVolume, 0.0f, 1.0f);
+ }
+
+ return 0.0f;
+ }
+
+ //public virtual void Draw(SpriteBatch spriteBatch, bool editing = false)
+ //{
+ // item.drawableComponents = Array.FindAll(item.drawableComponents, i => i != this);
+ //}
+
+ public virtual void DrawHUD(SpriteBatch spriteBatch, Character character) { }
+
+ public virtual void AddToGUIUpdateList() { }
+
+ public virtual void UpdateHUD(Character character) { }
+
+ private bool LoadElemProjSpecific(XElement subElement)
+ {
+ switch (subElement.Name.ToString().ToLowerInvariant())
+ {
+ case "guiframe":
+ string rectStr = ToolBox.GetAttributeString(subElement, "rect", "0.0,0.0,0.5,0.5");
+
+ string[] components = rectStr.Split(',');
+ if (components.Length < 4) break;
+
+ Vector4 rect = ToolBox.GetAttributeVector4(subElement, "rect", Vector4.One);
+ if (components[0].Contains(".")) rect.X *= GameMain.GraphicsWidth;
+ if (components[1].Contains(".")) rect.Y *= GameMain.GraphicsHeight;
+ if (components[2].Contains(".")) rect.Z *= GameMain.GraphicsWidth;
+ if (components[3].Contains(".")) rect.W *= GameMain.GraphicsHeight;
+
+ string style = ToolBox.GetAttributeString(subElement, "style", "");
+
+ Vector4 color = ToolBox.GetAttributeVector4(subElement, "color", Vector4.One);
+
+ Alignment alignment = Alignment.Center;
+ try
+ {
+ alignment = (Alignment)Enum.Parse(typeof(Alignment),
+ ToolBox.GetAttributeString(subElement, "alignment", "Center"), true);
+ }
+ catch
+ {
+ DebugConsole.ThrowError("Error in " + subElement.Parent + "! \"" + subElement.Parent.Attribute("type").Value + "\" is not a valid alignment");
+ }
+
+ guiFrame = new GUIFrame(
+ new Rectangle((int)rect.X, (int)rect.Y, (int)rect.Z, (int)rect.W),
+ new Color(color.X, color.Y, color.Z) * color.W,
+ alignment, style);
+
+ break;
+ case "sound":
+ string filePath = ToolBox.GetAttributeString(subElement, "file", "");
+
+ if (filePath == "") filePath = ToolBox.GetAttributeString(subElement, "sound", "");
+
+ if (filePath == "")
+ {
+ DebugConsole.ThrowError("Error when instantiating item \"" + item.Name + "\" - sound with no file path set");
+ break;
+ }
+
+ if (!filePath.Contains("/") && !filePath.Contains("\\") && !filePath.Contains(Path.DirectorySeparatorChar))
+ {
+ filePath = Path.Combine(Path.GetDirectoryName(item.Prefab.ConfigFile), filePath);
+ }
+
+ ActionType type;
+
+ try
+ {
+ type = (ActionType)Enum.Parse(typeof(ActionType), ToolBox.GetAttributeString(subElement, "type", ""), true);
+ }
+ catch (Exception e)
+ {
+ DebugConsole.ThrowError("Invalid sound type in " + subElement + "!", e);
+ break;
+ }
+
+ Sound sound = Sound.Load(filePath);
+
+ float range = ToolBox.GetAttributeFloat(subElement, "range", 800.0f);
+ bool loop = ToolBox.GetAttributeBool(subElement, "loop", false);
+ ItemSound itemSound = new ItemSound(sound, type, range, loop);
+ itemSound.VolumeProperty = ToolBox.GetAttributeString(subElement, "volume", "");
+ itemSound.VolumeMultiplier = ToolBox.GetAttributeFloat(subElement, "volumemultiplier", 1.0f);
+
+ List soundList = null;
+ if (!sounds.TryGetValue(itemSound.Type, out soundList))
+ {
+ soundList = new List();
+ sounds.Add(itemSound.Type, soundList);
+ }
+
+ soundList.Add(itemSound);
+ break;
+ default:
+ return false; //unknown element
+ }
+ return true; //element processed
+ }
+
+ //Starts a coroutine that will read the correct state of the component from the NetBuffer when correctionTimer reaches zero.
+ protected void StartDelayedCorrection(ServerNetObject type, NetBuffer buffer, float sendingTime)
+ {
+ if (delayedCorrectionCoroutine != null) CoroutineManager.StopCoroutines(delayedCorrectionCoroutine);
+
+ delayedCorrectionCoroutine = CoroutineManager.StartCoroutine(DoDelayedCorrection(type, buffer, sendingTime));
+ }
+
+ private IEnumerable