diff --git a/BuildChecker/BuildChecker.csproj b/BuildChecker/BuildChecker.csproj
index e2d01a14f6..49d17dca77 100644
--- a/BuildChecker/BuildChecker.csproj
+++ b/BuildChecker/BuildChecker.csproj
@@ -17,7 +17,7 @@ https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild
python3
py -3
{C899FCA4-7037-4E49-ABC2-44DE72487110}
- .NETFramework, Version=v4.5.1
+ .NETFramework, Version=v4.7.1
Library
diff --git a/Content.Client/Content.Client.csproj b/Content.Client/Content.Client.csproj
index 4f035dd9a9..01eac19f9b 100644
--- a/Content.Client/Content.Client.csproj
+++ b/Content.Client/Content.Client.csproj
@@ -9,7 +9,7 @@
Properties
Content.Client
Content.Client
- v4.5.1
+ v4.7.1
512
..\engine\bin\Client\Resources\Assemblies\
7.2
@@ -53,8 +53,12 @@
portable
+
+
+
+
@@ -81,6 +85,10 @@
+
+
+
+
@@ -112,7 +120,7 @@
-
+
@@ -131,4 +139,4 @@
-
\ No newline at end of file
+
diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs
index 14854641ca..556f6149bb 100644
--- a/Content.Client/EntryPoint.cs
+++ b/Content.Client/EntryPoint.cs
@@ -8,8 +8,12 @@ using Content.Client.GameTicking;
using Content.Client.Input;
using Content.Client.Interfaces;
using Content.Client.Interfaces.GameObjects;
+using Content.Client.Interfaces.Parallax;
+using Content.Client.Parallax;
using Content.Shared.Interfaces;
using SS14.Client;
+using SS14.Client.Interfaces;
+using SS14.Client.Interfaces.Graphics.Overlays;
using SS14.Client.Interfaces.Input;
using SS14.Client.Utility;
using SS14.Shared.ContentPack;
@@ -79,7 +83,10 @@ namespace Content.Client
IoCManager.Register();
IoCManager.Register();
IoCManager.Register();
+ IoCManager.Register();
IoCManager.BuildGraph();
+
+ IoCManager.Resolve().LoadParallax();
}
public override void PostInit()
@@ -92,6 +99,7 @@ namespace Content.Client
IoCManager.Resolve().Initialize();
IoCManager.Resolve().Initialize();
+ IoCManager.Resolve().AddOverlay(new ParallaxOverlay());
}
public override void Update(AssemblyLoader.UpdateLevel level, float frameTime)
diff --git a/Content.Client/Interfaces/Parallax/IParallaxManager.cs b/Content.Client/Interfaces/Parallax/IParallaxManager.cs
new file mode 100644
index 0000000000..8956eade7a
--- /dev/null
+++ b/Content.Client/Interfaces/Parallax/IParallaxManager.cs
@@ -0,0 +1,12 @@
+using System;
+using SS14.Client.Graphics;
+
+namespace Content.Client.Interfaces.Parallax
+{
+ public interface IParallaxManager
+ {
+ event Action OnTextureLoaded;
+ Texture ParallaxTexture { get; }
+ void LoadParallax();
+ }
+}
diff --git a/Content.Client/Parallax/ParallaxGenerator.cs b/Content.Client/Parallax/ParallaxGenerator.cs
new file mode 100644
index 0000000000..b974182c37
--- /dev/null
+++ b/Content.Client/Parallax/ParallaxGenerator.cs
@@ -0,0 +1,417 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using Nett;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.Primitives;
+using SS14.Client.Utility;
+using SS14.Shared.Log;
+using SS14.Shared.Maths;
+using SS14.Shared.Noise;
+using BlendFactor = SS14.Shared.Maths.Color.BlendFactor;
+
+namespace Content.Client.Parallax
+{
+ public class ParallaxGenerator
+ {
+ private readonly List Layers = new List();
+
+ public static Image GenerateParallax(TomlTable config, Size size)
+ {
+ Logger.DebugS("parallax", "Generating parallax!");
+ var generator = new ParallaxGenerator();
+ generator._loadConfig(config);
+
+ var image = new Image(Configuration.Default, size.Width, size.Height, Rgba32.Black);
+ var count = 0;
+ foreach (var layer in generator.Layers)
+ {
+ layer.Apply(image);
+ Logger.DebugS("parallax", "Layer {0} done!", count++);
+ }
+
+ return image;
+ }
+
+ private void _loadConfig(TomlTable config)
+ {
+ foreach (var layerArray in config.Get("layers").Items)
+ {
+ var layer = layerArray.Get();
+ switch (layer.Get("type"))
+ {
+ case "noise":
+ var layerNoise = new LayerNoise(layer);
+ Layers.Add(layerNoise);
+ break;
+
+ case "points":
+ var layerPoint = new LayerPoints(layer);
+ Layers.Add(layerPoint);
+ break;
+
+ default:
+ throw new NotSupportedException();
+ }
+ }
+ }
+
+ private abstract class Layer
+ {
+ public abstract void Apply(Image bitmap);
+ }
+
+ private class LayerNoise : Layer
+ {
+ private readonly Color InnerColor = Color.White;
+ private readonly Color OuterColor = Color.Black;
+ private readonly NoiseGenerator.NoiseType NoiseType = NoiseGenerator.NoiseType.Fbm;
+ private readonly uint Seed = 1234;
+ private readonly double Persistence = 0.5;
+ private readonly double Lacunarity = Math.PI * 2 / 3;
+ private readonly double Frequency = 1;
+ private readonly uint Octaves = 3;
+ private readonly double Threshold;
+ private readonly double Power = 1;
+ private readonly BlendFactor SrcFactor = BlendFactor.One;
+ private readonly BlendFactor DstFactor = BlendFactor.One;
+
+ public LayerNoise(TomlTable table)
+ {
+ if (table.TryGetValue("innercolor", out var tomlObject))
+ {
+ InnerColor = Color.FromHex(tomlObject.Get());
+ }
+
+ if (table.TryGetValue("outercolor", out tomlObject))
+ {
+ OuterColor = Color.FromHex(tomlObject.Get());
+ }
+
+ if (table.TryGetValue("seed", out tomlObject))
+ {
+ Seed = (uint) tomlObject.Get();
+ }
+
+ if (table.TryGetValue("persistence", out tomlObject))
+ {
+ Persistence = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture);
+ }
+
+ if (table.TryGetValue("lacunarity", out tomlObject))
+ {
+ Lacunarity = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture);
+ }
+
+ if (table.TryGetValue("frequency", out tomlObject))
+ {
+ Frequency = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture);
+ }
+
+ if (table.TryGetValue("octaves", out tomlObject))
+ {
+ Octaves = (uint) tomlObject.Get();
+ }
+
+ if (table.TryGetValue("threshold", out tomlObject))
+ {
+ Threshold = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture);
+ }
+
+ if (table.TryGetValue("sourcefactor", out tomlObject))
+ {
+ SrcFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get());
+ }
+
+ if (table.TryGetValue("destfactor", out tomlObject))
+ {
+ DstFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get());
+ }
+
+ if (table.TryGetValue("power", out tomlObject))
+ {
+ Power = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture);
+ }
+
+ if (table.TryGetValue("noise_type", out tomlObject))
+ {
+ switch (tomlObject.Get())
+ {
+ case "fbm":
+ NoiseType = NoiseGenerator.NoiseType.Fbm;
+ break;
+ case "ridged":
+ NoiseType = NoiseGenerator.NoiseType.Ridged;
+ break;
+ default:
+ throw new InvalidOperationException();
+ }
+ }
+ }
+
+ public override void Apply(Image bitmap)
+ {
+ var noise = new NoiseGenerator(NoiseType);
+ noise.SetSeed(Seed);
+ noise.SetFrequency(Frequency);
+ noise.SetPersistence(Persistence);
+ noise.SetLacunarity(Lacunarity);
+ noise.SetOctaves(Octaves);
+ noise.SetPeriodX(bitmap.Width);
+ noise.SetPeriodY(bitmap.Height);
+ var threshVal = 1 / (1 - Threshold);
+ var powFactor = 1 / Power;
+ for (var x = 0; x < bitmap.Width; x++)
+ {
+ for (var y = 0; y < bitmap.Height; y++)
+ {
+ // Do noise calculations.
+ var noiseVal = Math.Min(1, Math.Max(0, (noise.GetNoiseTiled(x, y) + 1) / 2));
+
+ // Threshold
+ noiseVal = Math.Max(0, noiseVal - Threshold);
+ noiseVal *= threshVal;
+ noiseVal = Math.Pow(noiseVal, powFactor);
+
+ // Get colors based on noise values.
+ var srcColor = Color.InterpolateBetween(InnerColor, OuterColor, (float) noiseVal)
+ .WithAlpha((float) noiseVal);
+
+ // Apply blending factors & write back.
+ var dstColor = bitmap[x, y].ConvertImgSharp();
+ bitmap[x, y] = Color.Blend(dstColor, srcColor, DstFactor, SrcFactor).ConvertImgSharp();
+ }
+ }
+ }
+ }
+
+ private class LayerPoints : Layer
+ {
+ private readonly int Seed = 1234;
+ private readonly int PointCount = 100;
+
+ private readonly Color CloseColor = Color.White;
+ private readonly Color FarColor = Color.Black;
+
+ private readonly BlendFactor SrcFactor = BlendFactor.One;
+ private readonly BlendFactor DstFactor = BlendFactor.One;
+
+ // Noise mask stuff.
+ private readonly bool Masked;
+ private readonly NoiseGenerator.NoiseType MaskNoiseType = NoiseGenerator.NoiseType.Fbm;
+ private readonly uint MaskSeed = 1234;
+ private readonly double MaskPersistence = 0.5;
+ private readonly double MaskLacunarity = Math.PI * 2 / 3;
+ private readonly double MaskFrequency = 1;
+ private readonly uint MaskOctaves = 3;
+ private readonly double MaskThreshold;
+ private readonly int PointSize = 1;
+ private readonly double MaskPower = 1;
+
+
+ public LayerPoints(TomlTable table)
+ {
+ if (table.TryGetValue("seed", out var tomlObject))
+ {
+ Seed = tomlObject.Get();
+ }
+
+ if (table.TryGetValue("count", out tomlObject))
+ {
+ PointCount = tomlObject.Get();
+ }
+
+ if (table.TryGetValue("sourcefactor", out tomlObject))
+ {
+ SrcFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get());
+ }
+
+ if (table.TryGetValue("destfactor", out tomlObject))
+ {
+ DstFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get());
+ }
+
+ if (table.TryGetValue("farcolor", out tomlObject))
+ {
+ FarColor = Color.FromHex(tomlObject.Get());
+ }
+
+ if (table.TryGetValue("closecolor", out tomlObject))
+ {
+ CloseColor = Color.FromHex(tomlObject.Get());
+ }
+
+ if (table.TryGetValue("pointsize", out tomlObject))
+ {
+ PointSize = tomlObject.Get();
+ }
+
+ // Noise mask stuff.
+ if (table.TryGetValue("mask", out tomlObject))
+ {
+ Masked = tomlObject.Get();
+ }
+
+ if (table.TryGetValue("maskseed", out tomlObject))
+ {
+ MaskSeed = (uint) tomlObject.Get();
+ }
+
+ if (table.TryGetValue("maskpersistence", out tomlObject))
+ {
+ MaskPersistence = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture);
+ }
+
+ if (table.TryGetValue("masklacunarity", out tomlObject))
+ {
+ MaskLacunarity = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture);
+ }
+
+ if (table.TryGetValue("maskfrequency", out tomlObject))
+ {
+ MaskFrequency = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture);
+ }
+
+ if (table.TryGetValue("maskoctaves", out tomlObject))
+ {
+ MaskOctaves = (uint) tomlObject.Get();
+ }
+
+ if (table.TryGetValue("maskthreshold", out tomlObject))
+ {
+ MaskThreshold = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture);
+ }
+
+ if (table.TryGetValue("masknoise_type", out tomlObject))
+ {
+ switch (tomlObject.Get())
+ {
+ case "fbm":
+ MaskNoiseType = NoiseGenerator.NoiseType.Fbm;
+ break;
+ case "ridged":
+ MaskNoiseType = NoiseGenerator.NoiseType.Ridged;
+ break;
+ default:
+ throw new InvalidOperationException();
+ }
+ }
+
+ if (table.TryGetValue("maskpower", out tomlObject))
+ {
+ MaskPower = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture);
+ }
+ }
+
+ public override void Apply(Image bitmap)
+ {
+ // Temporary buffer so we don't mess up blending.
+ var buffer = new Image(Configuration.Default, bitmap.Width, bitmap.Height, Rgba32.Black);
+
+ if (Masked)
+ {
+ GenPointsMasked(buffer);
+ }
+ else
+ {
+ GenPoints(buffer);
+ }
+
+ for (var x = 0; x < bitmap.Width; x++)
+ {
+ for (var y = 0; y < bitmap.Height; y++)
+ {
+ var dstColor = bitmap[x, y].ConvertImgSharp();
+ var srcColor = buffer[x, y].ConvertImgSharp();
+
+ bitmap[x, y] = Color.Blend(dstColor, srcColor, DstFactor, SrcFactor).ConvertImgSharp();
+ }
+ }
+ }
+
+ private void GenPoints(Image buffer)
+ {
+ var o = PointSize - 1;
+ var random = new Random(Seed);
+ for (var i = 0; i < PointCount; i++)
+ {
+ var relX = random.NextDouble();
+ var relY = random.NextDouble();
+
+ var x = (int) (relX * buffer.Width);
+ var y = (int) (relY * buffer.Height);
+
+ var dist = random.NextDouble();
+
+ for (var ox = x - o; ox <= x + o; ox++)
+ {
+ for (var oy = y - o; oy <= y + o; oy++)
+ {
+ var color = Color.InterpolateBetween(FarColor, CloseColor, (float) dist).ConvertImgSharp();
+ buffer[MathHelper.Mod(ox, buffer.Width), MathHelper.Mod(oy, buffer.Width)] = color;
+ }
+ }
+ }
+ }
+
+ void GenPointsMasked(Image buffer)
+ {
+ var o = PointSize - 1;
+ var random = new Random(Seed);
+ var noise = new NoiseGenerator(MaskNoiseType);
+ noise.SetSeed(MaskSeed);
+ noise.SetFrequency(MaskFrequency);
+ noise.SetPersistence(MaskPersistence);
+ noise.SetLacunarity(MaskLacunarity);
+ noise.SetOctaves(MaskOctaves);
+ noise.SetPeriodX(buffer.Width);
+ noise.SetPeriodY(buffer.Height);
+
+ var threshVal = 1 / (1 - MaskThreshold);
+ var powFactor = 1 / MaskPower;
+
+ const int maxPointAttemptCount = 9999;
+ var pointAttemptCount = 0;
+
+ for (var i = 0; i < PointCount; i++)
+ {
+ var relX = random.NextDouble();
+ var relY = random.NextDouble();
+
+ var x = (int) (relX * buffer.Width);
+ var y = (int) (relY * buffer.Height);
+
+ // Grab noise at this point.
+ var noiseVal = Math.Min(1, Math.Max(0, (noise.GetNoiseTiled(x, y) + 1) / 2));
+ // Threshold
+ noiseVal = Math.Max(0, noiseVal - MaskThreshold);
+ noiseVal *= threshVal;
+ noiseVal = Math.Pow(noiseVal, powFactor);
+
+ var randomThresh = random.NextDouble();
+ if (randomThresh > noiseVal)
+ {
+ if (++pointAttemptCount <= maxPointAttemptCount)
+ {
+ i--;
+ }
+
+ continue;
+ }
+
+ var dist = random.NextDouble();
+
+ for (var ox = x - o; ox <= x + o; ox++)
+ {
+ for (var oy = y - o; oy <= y + o; oy++)
+ {
+ var color = Color.InterpolateBetween(FarColor, CloseColor, (float) dist).ConvertImgSharp();
+ buffer[MathHelper.Mod(ox, buffer.Width), MathHelper.Mod(oy, buffer.Height)] = color;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Content.Client/Parallax/ParallaxManager.cs b/Content.Client/Parallax/ParallaxManager.cs
new file mode 100644
index 0000000000..a99eed0f68
--- /dev/null
+++ b/Content.Client/Parallax/ParallaxManager.cs
@@ -0,0 +1,107 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using Content.Client.Interfaces.Parallax;
+using ICSharpCode.SharpZipLib.Checksum;
+using Nett;
+using SixLabors.ImageSharp;
+using SixLabors.Primitives;
+using SS14.Client.Graphics;
+using SS14.Client.Interfaces.ResourceManagement;
+using SS14.Shared.IoC;
+using SS14.Shared.Log;
+using SS14.Shared.Utility;
+
+namespace Content.Client.Parallax
+{
+ public class ParallaxManager : IParallaxManager
+ {
+#pragma warning disable 649
+ [Dependency] private readonly IResourceCache _resourceCache;
+#pragma warning restore 649
+
+ private static readonly ResourcePath ParallaxConfigPath = new ResourcePath("/parallax_config.toml");
+
+ // Both of these below are in the user directory.
+ private static readonly ResourcePath ParallaxPath = new ResourcePath("/parallax_cache.png");
+ private static readonly ResourcePath ParallaxConfigCrcPath = new ResourcePath("/parallax_config_crc");
+
+ public event Action OnTextureLoaded;
+ public Texture ParallaxTexture { get; private set; }
+
+ public async void LoadParallax()
+ {
+ MemoryStream configStream = null;
+ long crcValue;
+ TomlTable table;
+ try
+ {
+ // Load normal config into memory
+ if (!_resourceCache.TryContentFileRead(ParallaxConfigPath, out configStream))
+ {
+ Logger.ErrorS("parallax", "Parallax config not found.");
+ return;
+ }
+
+ // Calculate CRC32 of the config file.
+ var crc = new Crc32();
+ crc.Update(configStream.ToArray());
+ crcValue = crc.Value;
+
+ // See if we we have a previous CRC stored.
+ if (_resourceCache.UserData.Exists(ParallaxConfigCrcPath))
+ {
+ bool match;
+ using (var data = _resourceCache.UserData.Open(ParallaxConfigCrcPath, FileMode.Open))
+ using (var binaryReader = new BinaryReader(data))
+ {
+ match = binaryReader.ReadInt64() == crcValue;
+ }
+
+ // If the previous CRC matches, just load the old texture.
+ if (match)
+ {
+ using (var stream = _resourceCache.UserData.Open(ParallaxPath, FileMode.Open))
+ {
+ ParallaxTexture = Texture.LoadFromPNGStream(stream);
+ }
+
+ OnTextureLoaded?.Invoke(ParallaxTexture);
+ return;
+ }
+ }
+
+ // Well turns out the CRC does not match so the config changed.
+ // Read the new config and get rid of the config memory stream.
+ using (var reader = new StreamReader(configStream, Encoding.UTF8))
+ {
+ table = Toml.ReadString(reader.ReadToEnd());
+ }
+ }
+ finally
+ {
+ configStream?.Dispose();
+ }
+
+ // Generate the parallax in the thread pool.
+ var image = await Task.Run(() => ParallaxGenerator.GenerateParallax(table, new Size(1920, 1080)));
+ // And load it in the main thread for safety reasons.
+ ParallaxTexture = Texture.LoadFromImage(image);
+
+ // Store it and CRC so further game starts don't need to regenerate it.
+ using (var stream = _resourceCache.UserData.Open(ParallaxPath, FileMode.Create))
+ {
+ image.SaveAsPng(stream);
+ }
+
+ using (var stream = _resourceCache.UserData.Open(ParallaxConfigCrcPath, FileMode.Create))
+ using (var writer = new BinaryWriter(stream))
+ {
+ writer.Write(crcValue);
+ }
+
+ OnTextureLoaded?.Invoke(ParallaxTexture);
+ }
+ }
+}
diff --git a/Content.Client/Parallax/ParallaxOverlay.cs b/Content.Client/Parallax/ParallaxOverlay.cs
new file mode 100644
index 0000000000..368de07454
--- /dev/null
+++ b/Content.Client/Parallax/ParallaxOverlay.cs
@@ -0,0 +1,63 @@
+using Content.Client.Interfaces.Parallax;
+using SS14.Client.Graphics;
+using SS14.Client.Graphics.Drawing;
+using SS14.Client.Graphics.Overlays;
+using SS14.Client.Graphics.Shaders;
+using SS14.Client.Interfaces.Graphics.ClientEye;
+using SS14.Client.Interfaces.Graphics.Overlays;
+using SS14.Shared.IoC;
+using SS14.Shared.Maths;
+using SS14.Shared.Prototypes;
+
+namespace Content.Client.Parallax
+{
+ public class ParallaxOverlay : Overlay
+ {
+#pragma warning disable 649
+ [Dependency] private readonly IParallaxManager _parallaxManager;
+ [Dependency] private readonly IEyeManager _eyeManager;
+ [Dependency] private readonly IPrototypeManager _prototypeManager;
+#pragma warning restore 649
+
+ public override bool AlwaysDirty => true;
+ private const float Slowness = 0.5f;
+
+ private Texture _parallaxTexture;
+
+ public override OverlaySpace Space => OverlaySpace.ScreenSpaceBelowWorld;
+
+ public ParallaxOverlay() : base(nameof(ParallaxOverlay))
+ {
+ IoCManager.InjectDependencies(this);
+ Shader = _prototypeManager.Index("unshaded").Instance();
+
+ if (_parallaxManager.ParallaxTexture == null)
+ {
+ _parallaxManager.OnTextureLoaded += texture => _parallaxTexture = texture;
+ }
+ else
+ {
+ _parallaxTexture = _parallaxManager.ParallaxTexture;
+ }
+ }
+
+ protected override void Draw(DrawingHandle handle)
+ {
+ if (_parallaxTexture == null)
+ {
+ return;
+ }
+
+ var (sizeX, sizeY) = _parallaxTexture.Size;
+ var (posX, posY) = _eyeManager.ScreenToWorld(Vector2.Zero).ToWorld().Position;
+ var (ox, oy) = (Vector2i) new Vector2(-posX / Slowness, posY / Slowness);
+ ox = MathHelper.Mod(ox, sizeX);
+ oy = MathHelper.Mod(oy, sizeY);
+
+ handle.DrawTexture(_parallaxTexture, new Vector2(ox, oy));
+ handle.DrawTexture(_parallaxTexture, new Vector2(ox - sizeX, oy));
+ handle.DrawTexture(_parallaxTexture, new Vector2(ox, oy - sizeY));
+ handle.DrawTexture(_parallaxTexture, new Vector2(ox - sizeX, oy - sizeY));
+ }
+ }
+}
diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj
index 7af2670fd3..95f3b9189b 100644
--- a/Content.Server/Content.Server.csproj
+++ b/Content.Server/Content.Server.csproj
@@ -9,7 +9,7 @@
Properties
Content.Server
Content.Server
- v4.5.1
+ v4.7.1
512
..\engine\bin\Server\Resources\Assemblies\
..\bin\Content.Server\
@@ -159,7 +159,7 @@
-
+
diff --git a/Content.Shared/Content.Shared.csproj b/Content.Shared/Content.Shared.csproj
index 1d625ba45f..9d2d3b124f 100644
--- a/Content.Shared/Content.Shared.csproj
+++ b/Content.Shared/Content.Shared.csproj
@@ -9,7 +9,7 @@
Properties
Content.Shared
Content.Shared
- v4.5.1
+ v4.7.1
512
bin\x86\Debug\
prompt
diff --git a/Content.Tests/Content.Tests.csproj b/Content.Tests/Content.Tests.csproj
index 3d3ccf2ec6..e16c82418c 100644
--- a/Content.Tests/Content.Tests.csproj
+++ b/Content.Tests/Content.Tests.csproj
@@ -12,7 +12,7 @@
Properties
Content.Tests
Content.Tests
- v4.5.1
+ v4.7.1
512
UnitTest
..\bin\Content.Tests\
@@ -124,4 +124,4 @@
-->
-
+
\ No newline at end of file
diff --git a/Resources/parallax_config.toml b/Resources/parallax_config.toml
new file mode 100644
index 0000000000..b0458b29d9
--- /dev/null
+++ b/Resources/parallax_config.toml
@@ -0,0 +1,89 @@
+# Background nebula detail.
+[[layers]]
+type = "noise"
+seed = 7832
+innercolor = "#A020E1"
+outercolor = "#490070"
+noise_type = "ridged"
+frequency = "4"
+octaves = 8
+power = "0.25"
+threshold = "0.40"
+
+# Mask background nebula.
+[[layers]]
+type = "noise"
+noise_type = "fbm"
+innercolor = "#000000"
+outercolor = "#000000"
+destfactor = "SrcAlpha"
+seed = 3551
+octaves = 4
+power = "0.35"
+lacunarity = "1.5"
+frequency = "3"
+threshold = "0.0"
+
+# Dim background nebula stars.
+[[layers]]
+type = "points"
+seed = 3909
+closecolor = "#4B5072"
+count = 1500
+mask = true
+masknoise_type = "fbm"
+maskoctaves = 4
+maskpersistence = "0.5"
+maskpower = "0.35"
+masklacunarity = "1.5"
+maskfrequency = "3"
+maskthreshold = "0.0"
+maskseed = 3551
+
+# Bright background nebula stars.
+[[layers]]
+type = "points"
+closecolor = "#7E86BF"
+count = 1000
+seed = 3472
+mask = true
+masknoise_type = "fbm"
+maskoctaves = 4
+maskpersistence = "0.5"
+maskpower = "0.35"
+masklacunarity = "1.5"
+maskfrequency = "3"
+maskthreshold = "0.37"
+maskseed = 3551
+
+# Bright background nebula stars, dim edge.
+[[layers]]
+type = "points"
+closecolor = "#3D415C"
+pointsize = 2
+count = 1000
+seed = 3472
+mask = true
+masknoise_type = "fbm"
+maskoctaves = 4
+maskpersistence = "0.5"
+maskpower = "0.35"
+masklacunarity = "1.5"
+maskfrequency = "3"
+maskthreshold = "0.37"
+maskseed = 3551
+
+# Couple of odd bright yellow-ish stars.
+[[layers]]
+type = "points"
+closecolor = "#FFD363"
+count = 100
+seed = 6454
+
+# And their dim edge.
+[[layers]]
+type = "points"
+closecolor = "#43371A"
+pointsize = 2
+count = 100
+seed = 6454
diff --git a/engine b/engine
index 21fd3e5d96..7c4eeb136f 160000
--- a/engine
+++ b/engine
@@ -1 +1 @@
-Subproject commit 21fd3e5d9620f5193ab75040248d359b99557851
+Subproject commit 7c4eeb136fc61a1a39f82e6197a9bc2053c2b826