diff --git a/Barotrauma/BarotraumaClient/BarotraumaClient.csproj b/Barotrauma/BarotraumaClient/BarotraumaClient.csproj
index f845f84d6..5fe307587 100644
--- a/Barotrauma/BarotraumaClient/BarotraumaClient.csproj
+++ b/Barotrauma/BarotraumaClient/BarotraumaClient.csproj
@@ -184,6 +184,9 @@
+
+
+
diff --git a/Barotrauma/BarotraumaClient/Source/GameMain.cs b/Barotrauma/BarotraumaClient/Source/GameMain.cs
index b39f450a8..a22e302fa 100644
--- a/Barotrauma/BarotraumaClient/Source/GameMain.cs
+++ b/Barotrauma/BarotraumaClient/Source/GameMain.cs
@@ -42,6 +42,7 @@ namespace Barotrauma
public static NetworkMember NetworkMember;
public static ParticleManager ParticleManager;
+ public static DecalManager DecalManager;
public static World World;
@@ -274,6 +275,7 @@ namespace Barotrauma
yield return CoroutineStatus.Running;
ParticleManager = new ParticleManager("Content/Particles/ParticlePrefabs.xml", GameScreen.Cam);
+ DecalManager = new DecalManager("Content/Particles/DecalPrefabs.xml");
yield return CoroutineStatus.Running;
LocationType.Init();
diff --git a/Barotrauma/BarotraumaClient/Source/Map/Hull.cs b/Barotrauma/BarotraumaClient/Source/Map/Hull.cs
index e5255d49b..ee0f53fed 100644
--- a/Barotrauma/BarotraumaClient/Source/Map/Hull.cs
+++ b/Barotrauma/BarotraumaClient/Source/Map/Hull.cs
@@ -1,19 +1,41 @@
using Barotrauma.Networking;
+using Barotrauma.Particles;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
+using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma
{
partial class Hull : MapEntity, IPropertyObject, IServerSerializable
{
+ public const int MaxDecalsPerHull = 10;
+
public static WaterRenderer renderer;
+ private List decals = new List();
+
private Sound currentFlowSound;
private int soundIndex;
private float soundVolume;
+ public override bool DrawBelowWater
+ {
+ get
+ {
+ return decals.Count > 0;
+ }
+ }
+
+ public override bool DrawOverWater
+ {
+ get
+ {
+ return true;
+ }
+ }
+
public override bool IsMouseOn(Vector2 position)
{
if (!GameMain.DebugDraw && !ShowHulls) return false;
@@ -22,9 +44,169 @@ namespace Barotrauma
!Submarine.RectContains(MathUtils.ExpandRect(WorldRect, -8), position));
}
+ public void AddDecal(string decalName, Vector2 position, float scale)
+ {
+ if (decals.Count >= MaxDecalsPerHull) return;
+
+ var decal = GameMain.DecalManager.CreateDecal(decalName, scale, position, this);
+
+ if (decal != null)
+ {
+ decals.Add(decal);
+ }
+ }
+
+ partial void UpdateProjSpecific(float deltaTime, Camera cam)
+ {
+ if (EditWater)
+ {
+ Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
+ if (Submarine.RectContains(WorldRect, position))
+ {
+ if (PlayerInput.LeftButtonHeld())
+ {
+ //waveY[GetWaveIndex(position.X - rect.X - Submarine.Position.X) / WaveWidth] = 100.0f;
+ Volume = Volume + 1500.0f;
+ }
+ else if (PlayerInput.RightButtonHeld())
+ {
+ Volume = Volume - 1500.0f;
+ }
+ }
+ }
+ else if (EditFire)
+ {
+ Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
+ if (Submarine.RectContains(WorldRect, position))
+ {
+ if (PlayerInput.LeftButtonClicked())
+ {
+ new FireSource(position, this);
+ }
+ }
+ }
+
+ foreach (Decal decal in decals)
+ {
+ decal.Update(deltaTime);
+ }
+
+ decals.RemoveAll(d => d.FadeTimer >= d.LifeTime);
+
+ float strongestFlow = 0.0f;
+ foreach (Gap gap in ConnectedGaps)
+ {
+ if (gap.IsRoomToRoom)
+ {
+ //only the first linked hull plays the flow sound
+ if (gap.linkedTo[1] == this) continue;
+ }
+
+ float gapFlow = gap.LerpedFlowForce.Length();
+
+ if (gapFlow > strongestFlow)
+ {
+ strongestFlow = gapFlow;
+ }
+ }
+
+ if (strongestFlow > 1.0f)
+ {
+ soundVolume = soundVolume + ((strongestFlow < 100.0f) ? -deltaTime * 0.5f : deltaTime * 0.5f);
+ soundVolume = MathHelper.Clamp(soundVolume, 0.0f, 1.0f);
+
+ int index = (int)Math.Floor(strongestFlow / 100.0f);
+ index = Math.Min(index, 2);
+
+ var flowSound = SoundPlayer.flowSounds[index];
+ if (flowSound != currentFlowSound && soundIndex > -1)
+ {
+ Sounds.SoundManager.Stop(soundIndex);
+ currentFlowSound = null;
+ soundIndex = -1;
+ }
+
+ currentFlowSound = flowSound;
+ soundIndex = currentFlowSound.Loop(soundIndex, soundVolume, WorldPosition, Math.Min(strongestFlow * 5.0f, 2000.0f));
+ }
+ else
+ {
+ if (soundIndex > -1)
+ {
+ Sounds.SoundManager.Stop(soundIndex);
+ currentFlowSound = null;
+ soundIndex = -1;
+ }
+ }
+
+ for (int i = 0; i < waveY.Length; i++)
+ {
+ float maxDelta = Math.Max(Math.Abs(rightDelta[i]), Math.Abs(leftDelta[i]));
+ if (maxDelta > Rand.Range(1.0f, 10.0f))
+ {
+ var particlePos = new Vector2(rect.X + WaveWidth * i, surface + waveY[i]);
+ if (Submarine != null) particlePos += Submarine.Position;
+
+ GameMain.ParticleManager.CreateParticle("mist",
+ particlePos,
+ new Vector2(0.0f, -50.0f), 0.0f, this);
+ }
+ }
+ }
+
+ private void DrawDecals(SpriteBatch spriteBatch)
+ {
+ Rectangle hullDrawRect = rect;
+ if (Submarine != null) hullDrawRect.Location += Submarine.DrawPosition.ToPoint();
+
+
+
+ foreach (Decal d in decals)
+ {
+ d.Draw(spriteBatch, this);
+ continue;
+
+ Vector2 drawPos = d.Position + rect.Location.ToVector2();
+
+ Rectangle drawRect = new Rectangle(
+ (int)(drawPos.X - d.Sprite.size.X / 2),
+ (int)(drawPos.Y + d.Sprite.size.Y / 2),
+ (int)d.Sprite.size.X,
+ (int)d.Sprite.size.Y);
+
+ Rectangle overFlowAmount = new Rectangle(
+ (int)Math.Max(hullDrawRect.X - drawRect.X, 0.0f),
+ (int)Math.Max(drawRect.Y - hullDrawRect.Y, 0.0f),
+ (int)Math.Max(drawRect.Right - hullDrawRect.Right, 0.0f),
+ (int)Math.Max((hullDrawRect.Y - hullDrawRect.Height) - (drawRect.Y - drawRect.Height), 0.0f));
+
+ var clippedSourceRect = new Rectangle(d.Sprite.SourceRect.X + overFlowAmount.X,
+ d.Sprite.SourceRect.Y + overFlowAmount.Y,
+ d.Sprite.SourceRect.Width - overFlowAmount.X - overFlowAmount.Width,
+ d.Sprite.SourceRect.Height - overFlowAmount.Y - overFlowAmount.Height);
+
+ drawRect = new Rectangle(
+ drawRect.X + overFlowAmount.X,
+ drawRect.Y - overFlowAmount.Y,
+ drawRect.Width - overFlowAmount.X - overFlowAmount.Width,
+ drawRect.Height - overFlowAmount.Y - overFlowAmount.Height);
+
+ drawPos.Y = -drawPos.Y;
+ drawRect.Y = -drawRect.Y;
+ GUI.DrawRectangle(spriteBatch, drawRect, Color.Red);
+ GUI.DrawRectangle(spriteBatch, drawPos, Vector2.One * 3, Color.Red);
+ spriteBatch.Draw(d.Sprite.Texture, drawRect, clippedSourceRect, d.Color, 0, Vector2.Zero, SpriteEffects.None, 1.0f);
+ }
+ }
+
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
{
- //if (back) return;
+ if (back)
+ {
+ DrawDecals(spriteBatch);
+ return;
+ }
+
Rectangle drawRect;
if (!Visible)
{
diff --git a/Barotrauma/BarotraumaClient/Source/Particles/Decal.cs b/Barotrauma/BarotraumaClient/Source/Particles/Decal.cs
new file mode 100644
index 000000000..ea0907b45
--- /dev/null
+++ b/Barotrauma/BarotraumaClient/Source/Particles/Decal.cs
@@ -0,0 +1,89 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using System;
+
+namespace Barotrauma.Particles
+{
+ class Decal
+ {
+ public readonly DecalPrefab Prefab;
+ public Vector2 Position;
+
+ public readonly Sprite Sprite;
+
+ private float fadeTimer;
+
+ public Color Color
+ {
+ get { return Prefab.Color; }
+ }
+
+ public float FadeTimer
+ {
+ get { return fadeTimer; }
+ }
+
+ public float LifeTime
+ {
+ get { return Prefab.LifeTime; }
+ }
+
+ private float scale;
+
+ private Rectangle clippedSourceRect;
+
+ public Decal(DecalPrefab prefab, float scale, Vector2 worldPosition, Hull hull)
+ {
+ Prefab = prefab;
+
+ //transform to hull-relative coordinates so we don't have to worry about the hull moving
+ Position = worldPosition - hull.WorldRect.Location.ToVector2();
+
+ Vector2 drawPos = Position + hull.Rect.Location.ToVector2();
+
+ Sprite = prefab.Sprites[Rand.Range(0, prefab.Sprites.Count, Rand.RandSync.Unsynced)];
+
+ Rectangle drawRect = new Rectangle(
+ (int)(drawPos.X - Sprite.size.X / 2 * scale),
+ (int)(drawPos.Y + Sprite.size.Y / 2 * scale),
+ (int)(Sprite.size.X * scale),
+ (int)(Sprite.size.Y * scale));
+
+ Rectangle overFlowAmount = new Rectangle(
+ (int)Math.Max(hull.Rect.X - drawRect.X, 0.0f),
+ (int)Math.Max(drawRect.Y - hull.Rect.Y, 0.0f),
+ (int)Math.Max(drawRect.Right - hull.Rect.Right, 0.0f),
+ (int)Math.Max((hull.Rect.Y - hull.Rect.Height) - (drawRect.Y - drawRect.Height), 0.0f));
+
+ clippedSourceRect = new Rectangle(
+ Sprite.SourceRect.X + (int)(overFlowAmount.X / scale),
+ Sprite.SourceRect.Y + (int)(overFlowAmount.Y / scale),
+ Sprite.SourceRect.Width - (int)((overFlowAmount.X + overFlowAmount.Width) / scale),
+ Sprite.SourceRect.Height - (int)((overFlowAmount.Y + overFlowAmount.Height) / scale));
+
+ Position -= new Vector2(Sprite.size.X / 2 * scale - overFlowAmount.X, -Sprite.size.Y / 2 * scale + overFlowAmount.Y);
+
+ this.scale = scale;
+ }
+
+ public void Update(float deltaTime)
+ {
+ fadeTimer += deltaTime;
+ }
+
+ public void Draw(SpriteBatch spriteBatch, Hull hull)
+ {
+ Vector2 drawPos = Position + hull.Rect.Location.ToVector2();
+ drawPos += hull.Submarine.DrawPosition;
+ drawPos.Y = -drawPos.Y;
+
+ float a = 1.0f;
+ if (fadeTimer > Prefab.LifeTime - Prefab.FadeTime)
+ {
+ a = (Prefab.LifeTime - fadeTimer) / Prefab.FadeTime;
+ }
+
+ spriteBatch.Draw(Sprite.Texture, drawPos, clippedSourceRect, Color * a, 0, Vector2.Zero , scale, SpriteEffects.None, 1);
+ }
+ }
+}
diff --git a/Barotrauma/BarotraumaClient/Source/Particles/DecalManager.cs b/Barotrauma/BarotraumaClient/Source/Particles/DecalManager.cs
new file mode 100644
index 000000000..61c2b2e14
--- /dev/null
+++ b/Barotrauma/BarotraumaClient/Source/Particles/DecalManager.cs
@@ -0,0 +1,43 @@
+using Microsoft.Xna.Framework;
+using System.Collections.Generic;
+using System.Xml.Linq;
+
+namespace Barotrauma.Particles
+{
+ class DecalManager
+ {
+ private Dictionary prefabs;
+
+ public DecalManager(string configFile)
+ {
+ XDocument doc = ToolBox.TryLoadXml(configFile);
+ if (doc == null || doc.Root == null) return;
+
+ prefabs = new Dictionary();
+
+ foreach (XElement element in doc.Root.Elements())
+ {
+ if (prefabs.ContainsKey(element.Name.ToString()))
+ {
+ DebugConsole.ThrowError("Error in " + configFile + "! Each decal prefab must have a unique name.");
+ continue;
+ }
+ prefabs.Add(element.Name.ToString(), new DecalPrefab(element));
+ }
+ }
+
+ public Decal CreateDecal(string decalName, float scale, Vector2 worldPosition, Hull hull)
+ {
+ DecalPrefab prefab;
+ prefabs.TryGetValue(decalName, out prefab);
+
+ if (prefab == null)
+ {
+ DebugConsole.ThrowError("Decal prefab " + decalName + " not found!");
+ return null;
+ }
+
+ return new Decal(prefab, scale, worldPosition, hull);
+ }
+ }
+}
diff --git a/Barotrauma/BarotraumaClient/Source/Particles/DecalPrefab.cs b/Barotrauma/BarotraumaClient/Source/Particles/DecalPrefab.cs
new file mode 100644
index 000000000..9ee8d0e08
--- /dev/null
+++ b/Barotrauma/BarotraumaClient/Source/Particles/DecalPrefab.cs
@@ -0,0 +1,39 @@
+using Microsoft.Xna.Framework;
+using System;
+using System.Collections.Generic;
+using System.Xml.Linq;
+
+namespace Barotrauma.Particles
+{
+ class DecalPrefab
+ {
+ public readonly string Name;
+
+ public readonly List Sprites;
+
+ public readonly Color Color;
+
+ public readonly float LifeTime;
+ public readonly float FadeTime;
+
+ public DecalPrefab(XElement element)
+ {
+ Name = element.Name.ToString();
+
+ Sprites = new List();
+
+ foreach (XElement subElement in element.Elements())
+ {
+ if (subElement.Name.ToString().ToLowerInvariant() == "sprite")
+ {
+ Sprites.Add(new Sprite(subElement));
+ }
+ }
+
+ Color = new Color(ToolBox.GetAttributeVector4(element, "color", Vector4.One));
+
+ LifeTime = ToolBox.GetAttributeFloat(element, "lifetime", 10.0f);
+ FadeTime = Math.Min(LifeTime, ToolBox.GetAttributeFloat(element, "fadetime", 1.0f));
+ }
+ }
+}
diff --git a/Barotrauma/BarotraumaShared/BarotraumaShared.projitems b/Barotrauma/BarotraumaShared/BarotraumaShared.projitems
index f4ba0d930..9c6b52de7 100644
--- a/Barotrauma/BarotraumaShared/BarotraumaShared.projitems
+++ b/Barotrauma/BarotraumaShared/BarotraumaShared.projitems
@@ -616,6 +616,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
diff --git a/Barotrauma/BarotraumaShared/Content/Particles/DecalPrefabs.xml b/Barotrauma/BarotraumaShared/Content/Particles/DecalPrefabs.xml
new file mode 100644
index 000000000..57459dc8a
--- /dev/null
+++ b/Barotrauma/BarotraumaShared/Content/Particles/DecalPrefabs.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs
index 1c5da477e..045877ef9 100644
--- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs
+++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs
@@ -1511,6 +1511,13 @@ namespace Barotrauma
{
if (joint.CanBeSevered && (joint.LimbA == limbHit || joint.LimbB == limbHit))
{
+#if CLIENT
+ if (CurrentHull != null)
+ {
+ CurrentHull.AddDecal("blood", WorldPosition, Rand.Range(0.5f, 1.5f));
+ }
+#endif
+
AnimController.SeverLimbJoint(joint);
if (joint.LimbA == limbHit)
diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs b/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs
index cec973422..ea63b8483 100644
--- a/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs
+++ b/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs
@@ -394,6 +394,11 @@ namespace Barotrauma
}
}
+ if (bloodParticleAmount > 0 && character.CurrentHull != null)
+ {
+ character.CurrentHull.AddDecal("blood", WorldPosition, MathHelper.Clamp(bloodParticleSize, 0.5f, 1.0f));
+ }
+
#endif
damage += Math.Max(amount,bleedingAmount) / character.MaxHealth * 100.0f;
diff --git a/Barotrauma/BarotraumaShared/Source/Map/Hull.cs b/Barotrauma/BarotraumaShared/Source/Map/Hull.cs
index 23f4fed3a..0e9e56fd7 100644
--- a/Barotrauma/BarotraumaShared/Source/Map/Hull.cs
+++ b/Barotrauma/BarotraumaShared/Source/Map/Hull.cs
@@ -27,7 +27,7 @@ namespace Barotrauma
public static bool EditWater, EditFire;
private List fireSources;
-
+
public const float OxygenDistributionSpeed = 500.0f;
public const float OxygenDetoriationSpeed = 0.3f;
public const float OxygenConsumptionSpeed = 1000.0f;
@@ -392,89 +392,15 @@ namespace Barotrauma
public override void Update(float deltaTime, Camera cam)
{
- Oxygen -= OxygenDetoriationSpeed * deltaTime;
+ UpdateProjSpecific(deltaTime, cam);
- if (EditWater)
- {
- Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
- if (Submarine.RectContains(WorldRect, position))
- {
- if (PlayerInput.LeftButtonHeld())
- {
- //waveY[GetWaveIndex(position.X - rect.X - Submarine.Position.X) / WaveWidth] = 100.0f;
- Volume = Volume + 1500.0f;
- }
- else if (PlayerInput.RightButtonHeld())
- {
- Volume = Volume - 1500.0f;
- }
- }
- }
- else if (EditFire)
- {
- Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
- if (Submarine.RectContains(WorldRect, position))
- {
- if (PlayerInput.LeftButtonClicked())
- {
- new FireSource(position, this);
- }
- }
- }
+ Oxygen -= OxygenDetoriationSpeed * deltaTime;
FireSource.UpdateAll(fireSources, deltaTime);
aiTarget.SightRange = Submarine == null ? 0.0f : Math.Max(Submarine.Velocity.Length() * 500.0f, 500.0f);
aiTarget.SoundRange -= deltaTime * 1000.0f;
-
- float strongestFlow = 0.0f;
- foreach (Gap gap in ConnectedGaps)
- {
- if (gap.IsRoomToRoom)
- {
- //only the first linked hull plays the flow sound
- if (gap.linkedTo[1] == this) continue;
- }
-
- float gapFlow = gap.LerpedFlowForce.Length();
-
- if (gapFlow > strongestFlow)
- {
- strongestFlow = gapFlow;
- }
- }
-
-#if CLIENT
- if (strongestFlow > 1.0f)
- {
- soundVolume = soundVolume + ((strongestFlow < 100.0f) ? -deltaTime * 0.5f : deltaTime * 0.5f);
- soundVolume = MathHelper.Clamp(soundVolume, 0.0f, 1.0f);
-
- int index = (int)Math.Floor(strongestFlow / 100.0f);
- index = Math.Min(index, 2);
-
- var flowSound = SoundPlayer.flowSounds[index];
- if (flowSound != currentFlowSound && soundIndex > -1)
- {
- Sounds.SoundManager.Stop(soundIndex);
- currentFlowSound = null;
- soundIndex = -1;
- }
-
- currentFlowSound = flowSound;
- soundIndex = currentFlowSound.Loop(soundIndex, soundVolume, WorldPosition, Math.Min(strongestFlow*5.0f, 2000.0f));
- }
- else
- {
- if (soundIndex > -1)
- {
- Sounds.SoundManager.Stop(soundIndex);
- currentFlowSound = null;
- soundIndex = -1;
- }
- }
-#endif
-
+
//update client hulls if the amount of water has changed by >10%
//or if oxygen percentage has changed by 5%
if (Math.Abs(lastSentVolume - volume) > FullVolume * 0.1f ||
@@ -499,23 +425,9 @@ namespace Barotrauma
return;
}
-
float surfaceY = rect.Y - rect.Height + Volume / rect.Width;
for (int i = 0; i < waveY.Length; i++)
{
- float maxDelta = Math.Max(Math.Abs(rightDelta[i]), Math.Abs(leftDelta[i]));
-#if CLIENT
- if (maxDelta > Rand.Range(1.0f,10.0f))
- {
- var particlePos = new Vector2(rect.X + WaveWidth * i, surface + waveY[i]);
- if (Submarine != null) particlePos += Submarine.Position;
-
- GameMain.ParticleManager.CreateParticle("mist",
- particlePos,
- new Vector2(0.0f, -50.0f), 0.0f, this);
- }
-#endif
-
waveY[i] = waveY[i] + waveVel[i];
if (surfaceY + waveY[i] > rect.Y)
@@ -572,6 +484,8 @@ namespace Barotrauma
}
}
+ partial void UpdateProjSpecific(float deltaTime, Camera cam);
+
public void ApplyFlowForces(float deltaTime, Item item)
{
foreach (var gap in ConnectedGaps.Where(gap => gap.Open > 0))