v0.10.5.1

This commit is contained in:
Juan Pablo Arce
2020-09-22 11:31:56 -03:00
parent 44032d0ae0
commit 0002ad2c50
343 changed files with 12276 additions and 5023 deletions
@@ -4,11 +4,100 @@ using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
partial class BackgroundSection
{
public Rectangle Rect;
public int Index;
public int RowIndex;
private Vector4 colorVector4;
private Color color;
public readonly Vector2 Noise;
public readonly Color DirtColor;
public float ColorStrength
{
get;
protected set;
}
public Color Color
{
get { return color; }
protected set
{
color = value;
colorVector4 = new Vector4(value.R / 255.0f, value.G / 255.0f, value.B / 255.0f, value.A / 255.0f);
}
}
public BackgroundSection(Rectangle rect, int index, int rowIndex)
{
Rect = rect;
Index = index;
ColorStrength = 0.0f;
RowIndex = rowIndex;
Noise = new Vector2(
PerlinNoise.GetPerlin(Rect.X / 1000.0f, Rect.Y / 1000.0f),
PerlinNoise.GetPerlin(Rect.Y / 1000.0f + 0.5f, Rect.X / 1000.0f + 0.5f));
Color = DirtColor = Color.Lerp(new Color(10, 10, 10, 100), new Color(54, 57, 28, 200), Noise.X);
}
public BackgroundSection(Rectangle rect, int index, float colorStrength, Color color, int rowIndex)
{
System.Diagnostics.Debug.Assert(rect.Width > 0 && rect.Height > 0);
Rect = rect;
Index = index;
ColorStrength = colorStrength;
Color = color;
RowIndex = rowIndex;
Noise = new Vector2(
PerlinNoise.GetPerlin(Rect.X / 1000.0f, Rect.Y / 1000.0f),
PerlinNoise.GetPerlin(Rect.Y / 1000.0f + 0.5f, Rect.X / 1000.0f + 0.5f));
Color = DirtColor = Color.Lerp(new Color(10, 10, 10, 100), new Color(54, 57, 28, 200), Noise.X);
}
public bool SetColor(Color color)
{
if (Color == color) { return false; }
Color = color;
return true;
}
public float SetColorStrength(float colorStrength)
{
if (ColorStrength == colorStrength) { return -1f; }
float previous = ColorStrength;
ColorStrength = colorStrength;
return previous;
}
public bool LerpColor(Color to, float amount)
{
if (Color == to) { return false; }
colorVector4 = Vector4.Lerp(colorVector4, to.ToVector4(), amount);
color = new Color(colorVector4);
return true;
}
public Color GetStrengthAdjustedColor()
{
return Color * ColorStrength;
}
}
partial class Hull : MapEntity, ISerializableEntity, IServerSerializable
{
public static List<Hull> hullList = new List<Hull>();
@@ -29,7 +118,11 @@ namespace Barotrauma
//how much excess water the room can contain, relative to the volume of the room.
//needed to make it possible for pressure to "push" water up through U-shaped hull configurations
public const float MaxCompress = 1.05f;
public const int BackgroundSectionSize = 16;
public const int BackgroundSectionsPerNetworkEvent = 16;
public readonly Dictionary<string, SerializableProperty> properties;
public Dictionary<string, SerializableProperty> SerializableProperties
{
@@ -54,6 +147,11 @@ namespace Barotrauma
private float[] leftDelta;
private float[] rightDelta;
public const int MaxDecalsPerHull = 10;
private readonly List<Decal> decals = new List<Decal>();
public readonly List<Gap> ConnectedGaps = new List<Gap>();
public override string Name
@@ -140,6 +238,8 @@ namespace Barotrauma
OxygenPercentage = prevOxygenPercentage;
surface = drawSurface = rect.Y - rect.Height + WaterVolume / rect.Width;
Pressure = surface;
CreateBackgroundSections();
}
}
@@ -186,6 +286,8 @@ namespace Barotrauma
get { return Submarine == null ? surface : surface + Submarine.Position.Y; }
}
private float dirtiedVolume = 0.0f;
public float WaterVolume
{
get { return waterVolume; }
@@ -193,8 +295,25 @@ namespace Barotrauma
{
if (!MathUtils.IsValid(value)) return;
waterVolume = MathHelper.Clamp(value, 0.0f, Volume * MaxCompress);
if (waterVolume < Volume) Pressure = rect.Y - rect.Height + waterVolume / rect.Width;
if (waterVolume > 0.0f) update = true;
if (waterVolume < Volume) { Pressure = rect.Y - rect.Height + waterVolume / rect.Width; }
if (waterVolume > 0.0f)
{
update = true;
if (BackgroundSections != null)
{
float volumeMultiplier = Math.Clamp(waterVolume / Volume, 0f, 1f);
if (Math.Abs(volumeMultiplier - dirtiedVolume) > 0.075f)
{
RefreshSubmergedSections(new Rectangle(new Point(0, -rect.Height), new Point(rect.Width, (int)(rect.Height * volumeMultiplier))));
dirtiedVolume = volumeMultiplier;
}
}
}
else
{
submergedSections.Clear();
dirtiedVolume = 0.0f;
}
}
}
@@ -238,6 +357,36 @@ namespace Barotrauma
get { return waveVel; }
}
// sections of a decorative background that can be painted
public List<BackgroundSection> BackgroundSections
{
get;
private set;
}
private readonly HashSet<int> pendingSectionUpdates = new HashSet<int>();
private readonly List<BackgroundSection> submergedSections = new List<BackgroundSection>();
public int xBackgroundMax, yBackgroundMax;
public bool SupportsPaintedColors
{
get
{
return BackgroundSections != null;
}
}
private const int sectorWidth = 4;
private const int sectorHeight = 4;
private const float minColorStrength = 0.0f;
private const float maxColorStrength = 0.7f;
private bool networkUpdatePending;
private float networkUpdateTimer;
public List<FireSource> FireSources { get; private set; }
public Hull(MapEntityPrefab prefab, Rectangle rectangle)
@@ -250,6 +399,8 @@ namespace Barotrauma
: base (prefab, submarine)
{
rect = rectangle;
if (BackgroundSections == null) { CreateBackgroundSections(); }
OxygenPercentage = 100.0f;
@@ -284,6 +435,8 @@ namespace Barotrauma
Gap.UpdateHulls();
}
CreateBackgroundSections();
WaterVolume = 0.0f;
InsertToList();
@@ -399,6 +552,12 @@ namespace Barotrauma
public override void Move(Vector2 amount)
{
if (!MathUtils.IsValid(amount))
{
DebugConsole.ThrowError($"Attempted to move a hull by an invalid amount ({amount})\n{Environment.StackTrace}");
return;
}
rect.X += (int)amount.X;
rect.Y += (int)amount.Y;
@@ -471,6 +630,48 @@ namespace Barotrauma
FireSources.Add(fireSource);
}
public Decal AddDecal(UInt32 decalId, Vector2 worldPosition, float scale, bool isNetworkEvent)
{
//clients are only allowed to create decals when the server says so
if (!isNetworkEvent && GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
{
return null;
}
var decal = GameMain.DecalManager.Prefabs.Find(p => p.UIntIdentifier == decalId);
if (decal == null)
{
DebugConsole.ThrowError($"Could not find a decal prefab with the UInt identifier {decalId}!");
return null;
}
return AddDecal(decal.Name, worldPosition, scale, isNetworkEvent);
}
public Decal AddDecal(string decalName, Vector2 worldPosition, float scale, bool isNetworkEvent)
{
//clients are only allowed to create decals when the server says so
if (!isNetworkEvent && GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
{
return null;
}
if (decals.Count >= MaxDecalsPerHull) { return null; }
var decal = GameMain.DecalManager.CreateDecal(decalName, scale, worldPosition, this);
if (decal != null)
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { false });
}
decals.Add(decal);
}
return decal;
}
public override void Update(float deltaTime, Camera cam)
{
base.Update(deltaTime, cam);
@@ -480,6 +681,13 @@ namespace Barotrauma
FireSource.UpdateAll(FireSources, deltaTime);
foreach (Decal decal in decals)
{
decal.Update(deltaTime);
}
decals.RemoveAll(d => d.FadeTimer >= d.LifeTime || d.BaseAlpha <= 0.001f);
if (aiTarget != null)
{
aiTarget.SightRange = Submarine == null ? aiTarget.MinSightRange : Submarine.Velocity.Length() / 2 * aiTarget.MaxSightRange;
@@ -596,6 +804,12 @@ namespace Barotrauma
}
}
//0.01 increase every ~1000 frames = reaches full dirtiness in ~27 minutes
if (submergedSections.Count > 0 && Submarine != null && Submarine.Info.Type == SubmarineType.Player && Rand.Int(1000) == 1)
{
DirtySections(submergedSections, 0.01f);
}
if (waterVolume < Volume)
{
LethalPressure -= 10.0f * deltaTime;
@@ -906,9 +1120,180 @@ namespace Barotrauma
return "RoomName.Sub" + roomPos.ToString();
}
#region BackgroundSections
private void CreateBackgroundSections()
{
int sectionWidth, sectionHeight;
sectionWidth = sectionHeight = BackgroundSectionSize;
xBackgroundMax = rect.Width / sectionWidth;
yBackgroundMax = rect.Height / sectionHeight;
BackgroundSections = new List<BackgroundSection>(xBackgroundMax * yBackgroundMax);
int sections = xBackgroundMax * yBackgroundMax;
float xSectors = xBackgroundMax / (float)sectorWidth;
for (int y = 0; y < yBackgroundMax; y++)
{
for (int x = 0; x < xBackgroundMax; x++)
{
int index = BackgroundSections.Count;
int sector = (int)Math.Floor(index / (float)sectorWidth - xSectors * y) + y / sectorHeight * (int)Math.Ceiling(xSectors);
BackgroundSections.Add(new BackgroundSection(new Rectangle(x * sectionWidth, y * -sectionHeight, sectionWidth, sectionHeight), index, y));
}
}
#if CLIENT
minimumPaintAmountToDraw = maxColorStrength / BackgroundSections.Count;
#endif
}
public static Hull GetCleanTarget(Vector2 worldPosition)
{
foreach (Hull hull in hullList)
{
Rectangle worldRect = hull.WorldRect;
if (worldPosition.X < worldRect.X || worldPosition.X > worldRect.Right) { continue; }
if (worldPosition.Y > worldRect.Y || worldPosition.Y < worldRect.Y - worldRect.Height) { continue; }
return hull;
}
return null;
}
public BackgroundSection GetBackgroundSection(Vector2 worldPosition)
{
if (!SupportsPaintedColors) { return null; }
Vector2 subOffset = Submarine == null ? Vector2.Zero : Submarine.Position;
Vector2 relativePosition = new Vector2(worldPosition.X - subOffset.X - rect.X, worldPosition.Y - subOffset.Y - rect.Y);
int xIndex = (int)Math.Floor(relativePosition.X / BackgroundSectionSize);
if (xIndex < 0 || xIndex >= xBackgroundMax) { return null; }
int yIndex = (int)Math.Floor(-relativePosition.Y / BackgroundSectionSize);
if (yIndex < 0 || yIndex >= yBackgroundMax) { return null; }
return BackgroundSections[xIndex + yIndex * xBackgroundMax];
}
public IEnumerable<BackgroundSection> GetBackgroundSectionsViaContaining(Rectangle rectArea)
{
if (BackgroundSections == null || BackgroundSections.Count == 0)
{
yield break;
}
else
{
int xMin = Math.Max(rectArea.X / BackgroundSectionSize, 0);
if (xMin >= xBackgroundMax) { yield break; }
int xMax = Math.Min(rectArea.Right / BackgroundSectionSize, xBackgroundMax - 1);
if (xMax < 0) { yield break; }
int yMin = Math.Max(-rectArea.Bottom / BackgroundSectionSize, 0);
if (yMin >= yBackgroundMax) { yield break; }
int yMax = Math.Min(-rectArea.Y / BackgroundSectionSize, yBackgroundMax - 1);
if (yMax < 0) { yield break; }
for (int x = xMin; x <= xMax; x++)
{
for (int y = yMin; y <= yMax; y++)
{
yield return BackgroundSections[x + y * xBackgroundMax];
}
}
}
}
public void RefreshSubmergedSections(Rectangle waterArea)
{
if (BackgroundSections == null) { return; }
submergedSections.Clear();
foreach (var section in GetBackgroundSectionsViaContaining(waterArea))
{
submergedSections.Add(section);
}
}
public bool DoesSectionMatch(int index, int row)
{
return index >= 0 && row >= 0 && BackgroundSections.Count > index && BackgroundSections[index] != null && BackgroundSections[index].RowIndex == row;
}
public void SetSectionColorOrStrength(BackgroundSection section, Color? color, float? strength, bool requiresUpdate, bool isCleaning)
{
bool sectionUpdated = isCleaning;
if (color != null)
{
if (section.Color != color.Value && strength.HasValue)
{
//already painted with a different color -> interpolate towards the new one
//an ad-hoc formula that makes the color changes faster when the current strength is low
//(-> a barely dirty wall gets recolored almost immediately, while a more heavily colored one takes a while)
float changeSpeed = strength.Value / Math.Max(section.ColorStrength * section.ColorStrength, 0.001f) * 0.1f;
if (section.LerpColor(color.Value, changeSpeed)) { sectionUpdated = true; }
}
else
{
if (section.SetColor(color.Value)) { sectionUpdated = true; }
}
}
if (strength != null)
{
float previous = section.SetColorStrength(Math.Max(minColorStrength, Math.Min(maxColorStrength, section.ColorStrength + strength.Value)));
if (previous != -1f)
{
#if CLIENT
paintAmount = Math.Max(0, paintAmount + (section.ColorStrength - previous) / BackgroundSections.Count);
#endif
sectionUpdated = true;
}
}
if (sectionUpdated && GameMain.NetworkMember != null && requiresUpdate)
{
networkUpdatePending = true;
pendingSectionUpdates.Add((int)Math.Floor(section.Index / (float)BackgroundSectionsPerNetworkEvent));
#if CLIENT
serverUpdateDelay = 0.5f;
#endif
}
}
public void DirtySections(List<BackgroundSection> sections, float dirtyVal)
{
if (sections == null) { return; }
for (int i = 0; i < sections.Count; i++)
{
float sectionDirtyVal = dirtyVal;
SetSectionColorOrStrength(sections[i], sections[i].DirtColor, sectionDirtyVal, false, false);
}
}
public void CleanSection(BackgroundSection section, float cleanVal, bool updateRequired)
{
bool decalsCleaned = false;
for (int i = 0; i < decals.Count; i++)
{
Decal decal = decals[i];
if (decal.AffectsSection(section))
{
decal.Clean(cleanVal);
decalsCleaned = true;
}
}
if (section.ColorStrength == 0 && !decalsCleaned) { return; }
SetSectionColorOrStrength(section, null, cleanVal, updateRequired, true);
}
#endregion
public static Hull Load(XElement element, Submarine submarine)
{
Rectangle rect = Rectangle.Empty;
Rectangle rect;
if (element.Attribute("rect") != null)
{
rect = element.GetAttributeRect("rect", Rectangle.Empty);
@@ -947,6 +1332,41 @@ namespace Barotrauma
hull.OriginalAmbientLight = XMLExtensions.ParseColor(originalAmbientLight, false);
}
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "decal":
string id = subElement.GetAttributeString("id", "");
Vector2 pos = subElement.GetAttributeVector2("pos", Vector2.Zero);
float scale = subElement.GetAttributeFloat("scale", 1.0f);
float timer = subElement.GetAttributeFloat("timer", 1.0f);
var decal = hull.AddDecal(id, pos + hull.WorldRect.Location.ToVector2(), scale, true);
if (decal != null)
{
decal.FadeTimer = timer;
}
break;
}
}
string backgroundSectionStr = element.GetAttributeString("backgroundsections", "");
if (!string.IsNullOrEmpty(backgroundSectionStr))
{
string[] backgroundSectionStrSplit = backgroundSectionStr.Split(';');
foreach (string str in backgroundSectionStrSplit)
{
string[] backgroundSectionData = str.Split(':');
if (backgroundSectionData.Length != 3) { continue; }
Color color = XMLExtensions.ParseColor(backgroundSectionData[1]);
if (int.TryParse(backgroundSectionData[0], out int index) &&
float.TryParse(backgroundSectionData[2], NumberStyles.Any, CultureInfo.InvariantCulture, out float strength))
{
hull.SetSectionColorOrStrength(hull.BackgroundSections[index], color, strength, false, false);
}
}
}
SerializableProperty.DeserializeProperties(hull, element);
if (element.Attribute("oxygen") == null) { hull.Oxygen = hull.Volume; }
@@ -976,7 +1396,7 @@ namespace Barotrauma
if (linkedTo != null && linkedTo.Count > 0)
{
var saveableLinked = linkedTo.Where(l => l.ShouldBeSaved && !l.Removed).ToList();
var saveableLinked = linkedTo.Where(l => l.ShouldBeSaved && (l.Removed == Removed)).ToList();
element.Add(new XAttribute("linked", string.Join(",", saveableLinked.Select(l => l.ID.ToString()))));
}
@@ -985,6 +1405,25 @@ namespace Barotrauma
element.Add(new XAttribute("originalambientlight", XMLExtensions.ColorToString(OriginalAmbientLight.Value)));
}
if (BackgroundSections != null && BackgroundSections.Count > 0)
{
element.Add(
new XAttribute(
"backgroundsections",
string.Join(';', BackgroundSections.Where(b => b.ColorStrength > 0.01f).Select(b => b.Index + ":" + XMLExtensions.ColorToString(b.Color) + ":" + b.ColorStrength.ToString("G", CultureInfo.InvariantCulture)))));
}
foreach (Decal decal in decals)
{
element.Add(
new XElement("decal",
new XAttribute("id", decal.Prefab.Identifier),
new XAttribute("pos", XMLExtensions.Vector2ToString(decal.NonClampedPosition)),
new XAttribute("scale", decal.Scale.ToString("G", CultureInfo.InvariantCulture)),
new XAttribute("timer", decal.FadeTimer.ToString("G", CultureInfo.InvariantCulture))
));
}
SerializableProperty.SerializeProperties(this, element);
parentElement.Add(element);
return element;