diff --git a/Content.Client/Administration/UI/Tabs/AdminTab/AdminSectorWindow.xaml b/Content.Client/Administration/UI/Tabs/AdminTab/AdminSectorWindow.xaml
new file mode 100644
index 00000000000..5161c22b054
--- /dev/null
+++ b/Content.Client/Administration/UI/Tabs/AdminTab/AdminSectorWindow.xaml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Administration/UI/Tabs/AdminTab/AdminSectorWindow.xaml.cs b/Content.Client/Administration/UI/Tabs/AdminTab/AdminSectorWindow.xaml.cs
new file mode 100644
index 00000000000..758fdbfcaec
--- /dev/null
+++ b/Content.Client/Administration/UI/Tabs/AdminTab/AdminSectorWindow.xaml.cs
@@ -0,0 +1,136 @@
+using System.Globalization;
+using System.Linq;
+using Content.Shared.CCVar;
+using Content.Shared.Sectors;
+using Content.Shared.Sectors.Prototypes;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Configuration;
+using Robust.Shared.Console;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Administration.UI.Tabs.AdminTab;
+
+[GenerateTypedNameReferences]
+public sealed partial class AdminSectorWindow : DefaultWindow
+{
+ [Dependency] private readonly IConsoleHost _console = default!;
+ [Dependency] private readonly IConfigurationManager _configuration = default!;
+ [Dependency] private readonly IPrototypeManager _prototypes = default!;
+
+ public AdminSectorWindow()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ var sectors = Enum.GetNames();
+ for (var i = 0; i < sectors.Length; i++)
+ {
+ var sector = sectors[i];
+ SectorSelector.AddItem(sector);
+ SectorSelector.SetItemMetadata(i, sector);
+ }
+
+ var weatherPrototypes = _prototypes.EnumeratePrototypes()
+ .Select(p => p.ID)
+ .OrderBy(id => id)
+ .ToList();
+
+ for (var i = 0; i < weatherPrototypes.Count; i++)
+ {
+ var weatherId = weatherPrototypes[i];
+ var displayName = weatherId;
+ if (_prototypes.TryIndex(weatherId, out var proto) &&
+ !string.IsNullOrWhiteSpace(proto.Name))
+ {
+ displayName = Loc.GetString(proto.Name);
+ }
+ WeatherIdSelector.AddItem(displayName);
+ WeatherIdSelector.SetItemMetadata(i, weatherId);
+ }
+
+ WeatherIdSelector.OnItemSelected += args => WeatherIdSelector.SelectId(args.Id);
+
+ SectorSelector.OnItemSelected += args =>
+ {
+ SectorSelector.SelectId(args.Id);
+ UpdateSectorNameEdit();
+ };
+ SectorSelector.SelectId(0);
+ if (weatherPrototypes.Count > 0)
+ WeatherIdSelector.SelectId(0);
+ UpdateSectorNameEdit();
+
+ SetWeatherButton.OnPressed += _ => ApplyWeather();
+ ClearWeatherButton.OnPressed += _ => ClearWeather();
+ SetSectorNameButton.OnPressed += _ => SetSectorName();
+ ClearSectorNameButton.OnPressed += _ => ClearSectorName();
+ SetCenterRadiusButton.OnPressed += _ => SetCVarRadius("sector.center_radius", CenterRadiusEdit.Text);
+ SetMaxRadiusButton.OnPressed += _ => SetCVarRadius("sector.max_radius", MaxRadiusEdit.Text);
+ }
+
+ private string SelectedSector => SectorSelector.SelectedMetadata as string ?? SpaceSector.Center.ToString();
+
+ private void ApplyWeather()
+ {
+ var weatherId = WeatherIdSelector.SelectedMetadata as string;
+ if (string.IsNullOrWhiteSpace(weatherId))
+ return;
+
+ _console.ExecuteCommand($"sudo sectorweather {SelectedSector} {weatherId}");
+ }
+
+ private void ClearWeather()
+ {
+ _console.ExecuteCommand($"sudo sectorweather {SelectedSector} clear");
+ }
+
+ private void SetSectorName()
+ {
+ var name = SectorNameEdit.Text.Trim();
+ if (string.IsNullOrWhiteSpace(name))
+ return;
+
+ SetStringCVar(GetSectorNameCVarName(), name);
+ }
+
+ private void ClearSectorName()
+ {
+ SetStringCVar(GetSectorNameCVarName(), string.Empty);
+ SectorNameEdit.Text = string.Empty;
+ }
+
+ private void SetCVarRadius(string cvarName, string radiusText)
+ {
+ if (!float.TryParse(radiusText, NumberStyles.Float, CultureInfo.InvariantCulture, out var radius))
+ return;
+
+ _console.ExecuteCommand($"sudo cvar {cvarName} {radius.ToString(CultureInfo.InvariantCulture)}");
+ }
+
+ private void SetStringCVar(string cvarName, string value)
+ {
+ var escaped = value.Replace("\\", "\\\\").Replace("\"", "\\\"");
+ _console.ExecuteCommand($"sudo cvar {cvarName} \"{escaped}\"");
+ }
+
+ private void UpdateSectorNameEdit()
+ {
+ SectorNameEdit.Text = _configuration.GetCVar(GetSectorNameCVarDef());
+ }
+
+ private string GetSectorNameCVarName()
+ {
+ return GetSectorNameCVarDef().Name;
+ }
+
+ private CVarDef GetSectorNameCVarDef()
+ {
+ if (!Enum.TryParse(SelectedSector, out var sector))
+ sector = SpaceSector.Center;
+
+ return SharedSectorSystem.GetSectorNameCVar(sector);
+ }
+}
diff --git a/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml b/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml
index 6b8bf3ae7d8..a90706b114b 100644
--- a/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml
+++ b/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml
@@ -12,6 +12,7 @@
+
diff --git a/Content.Client/MessageBoard/UI/CreateEntry.xaml.cs b/Content.Client/MessageBoard/UI/CreateEntry.xaml.cs
index bf2b3357d9c..b59b9e26e95 100644
--- a/Content.Client/MessageBoard/UI/CreateEntry.xaml.cs
+++ b/Content.Client/MessageBoard/UI/CreateEntry.xaml.cs
@@ -1,5 +1,6 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
namespace Content.Client.MessageBoard.UI;
[GenerateTypedNameReferences]
@@ -13,5 +14,9 @@ public enum EntryType : byte
public EntryType CurrentEntryType = EntryType.Public;
+ public CreateEntry()
+ {
+ RobustXamlLoader.Load(this);
+ }
}
diff --git a/Content.Client/MessageBoard/UI/EntryWindow.xaml b/Content.Client/MessageBoard/UI/EntryWindow.xaml
index 88ac918ed0a..590620537a0 100644
--- a/Content.Client/MessageBoard/UI/EntryWindow.xaml
+++ b/Content.Client/MessageBoard/UI/EntryWindow.xaml
@@ -9,9 +9,9 @@
-
+
-
+
diff --git a/Content.Client/MessageBoard/UI/MessageBoard.xaml b/Content.Client/MessageBoard/UI/MessageBoard.xaml
index 8f5cc6dd0f8..0519c40bf33 100644
--- a/Content.Client/MessageBoard/UI/MessageBoard.xaml
+++ b/Content.Client/MessageBoard/UI/MessageBoard.xaml
@@ -5,7 +5,7 @@
MinSize="600 690">
-
+
-
+
diff --git a/Content.Client/MessageBoard/UI/MessageBoard.xaml.cs b/Content.Client/MessageBoard/UI/MessageBoard.xaml.cs
index 8dc4c001c6b..35001ed6dfc 100644
--- a/Content.Client/MessageBoard/UI/MessageBoard.xaml.cs
+++ b/Content.Client/MessageBoard/UI/MessageBoard.xaml.cs
@@ -24,6 +24,7 @@ public MessageBoard(EntityUid owner, IEntityManager entMan, IPrototypeManager pr
_spriteSystem = spriteSystem;
_owner = owner;
Title = entMan.GetComponent(owner).EntityName;
+ BoardTabContainer.SetTabTitle(0, "Public Board");
}
}
diff --git a/Content.Client/Sectors/Overlays/SectorEventOverlay.cs b/Content.Client/Sectors/Overlays/SectorEventOverlay.cs
new file mode 100644
index 00000000000..ed0a526cc64
--- /dev/null
+++ b/Content.Client/Sectors/Overlays/SectorEventOverlay.cs
@@ -0,0 +1,56 @@
+using System.Numerics;
+using Robust.Client.Graphics;
+using Robust.Client.Player;
+using Robust.Shared.Enums;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Sectors.Overlays;
+
+public sealed class SectorEventOverlay : Overlay
+{
+ private static readonly ProtoId ShaderProto = "SectorEventTint";
+
+ [Dependency] private readonly IEntityManager _entities = default!;
+ [Dependency] private readonly IPlayerManager _players = default!;
+ [Dependency] private readonly IPrototypeManager _prototypes = default!;
+
+ private readonly ShaderInstance _shader;
+
+ public Color TintColor = Color.Transparent;
+ public float TintNoiseStrength;
+
+ public override OverlaySpace Space => OverlaySpace.WorldSpace;
+ public override bool RequestScreenTexture => true;
+
+ public SectorEventOverlay()
+ {
+ IoCManager.InjectDependencies(this);
+ _shader = _prototypes.Index(ShaderProto).InstanceUnique();
+ }
+
+ protected override bool BeforeDraw(in OverlayDrawArgs args)
+ {
+ if (TintColor.A <= 0f)
+ return false;
+
+ if (!_entities.TryGetComponent(_players.LocalEntity, out EyeComponent? eyeComp))
+ return false;
+
+ return args.Viewport.Eye == eyeComp.Eye;
+ }
+
+ protected override void Draw(in OverlayDrawArgs args)
+ {
+ if (ScreenTexture == null)
+ return;
+
+ _shader.SetParameter("SCREEN_TEXTURE", ScreenTexture);
+ _shader.SetParameter("tintColor", new Vector4(TintColor.R, TintColor.G, TintColor.B, TintColor.A));
+ _shader.SetParameter("noiseStrength", Math.Clamp(TintNoiseStrength, 0f, 1f));
+
+ var handle = args.WorldHandle;
+ handle.UseShader(_shader);
+ handle.DrawRect(args.WorldBounds, Color.White);
+ handle.UseShader(null);
+ }
+}
diff --git a/Content.Client/Sectors/Systems/SectorEventVisualsSystem.cs b/Content.Client/Sectors/Systems/SectorEventVisualsSystem.cs
new file mode 100644
index 00000000000..5ef68dfb2ed
--- /dev/null
+++ b/Content.Client/Sectors/Systems/SectorEventVisualsSystem.cs
@@ -0,0 +1,89 @@
+using Content.Client.Sectors.Overlays;
+using Content.Shared.Sectors;
+using Content.Shared.Sectors.Events;
+using Content.Shared.Sectors.Prototypes;
+using Robust.Client.Graphics;
+using Robust.Client.Player;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Sectors.Systems;
+
+public sealed class SectorEventVisualsSystem : EntitySystem
+{
+ [Dependency] private readonly IOverlayManager _overlays = default!;
+ [Dependency] private readonly IPlayerManager _players = default!;
+ [Dependency] private readonly IPrototypeManager _prototypes = default!;
+ [Dependency] private readonly SharedSectorSystem _sectors = default!;
+
+ private readonly Dictionary _activeWeather = new();
+ private SectorEventOverlay _overlay = default!;
+
+ private Color _targetTintColor = Color.Transparent;
+ private float _targetAlpha;
+ private float _currentAlpha;
+ private float _targetNoiseStrength;
+ private float _currentNoiseStrength;
+
+ private const float FadeSpeed = 0.1f;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ _overlay = new SectorEventOverlay();
+ _overlays.AddOverlay(_overlay);
+
+ SubscribeNetworkEvent(OnWeatherStateUpdated);
+ }
+
+ public override void Shutdown()
+ {
+ base.Shutdown();
+ _overlays.RemoveOverlay(_overlay);
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ if (_players.LocalEntity is not { } player)
+ {
+ _targetAlpha = 0f;
+ _targetNoiseStrength = 0f;
+ }
+ else
+ {
+ var sector = _sectors.GetSector(player);
+ if (!_activeWeather.TryGetValue(sector, out var weatherId) ||
+ !_prototypes.TryIndex(weatherId, out var weather))
+ {
+ _targetAlpha = 0f;
+ _targetNoiseStrength = 0f;
+ }
+ else
+ {
+ var color = weather.ScreenTintColor;
+ var strength = Math.Clamp(weather.ScreenTintStrength, 0f, 1f);
+ _targetTintColor = color;
+ _targetAlpha = color.A * strength;
+ _targetNoiseStrength = weather.ScreenTintNoiseStrength;
+ }
+ }
+
+ var t = Math.Min(1f, frameTime * FadeSpeed);
+ _currentAlpha += (_targetAlpha - _currentAlpha) * t;
+ _currentNoiseStrength += (_targetNoiseStrength - _currentNoiseStrength) * t;
+
+ _overlay.TintColor = _targetTintColor.WithAlpha(_currentAlpha);
+ _overlay.TintNoiseStrength = _currentNoiseStrength;
+ }
+
+ private void OnWeatherStateUpdated(SectorWeatherStateUpdateEvent ev)
+ {
+ _activeWeather.Clear();
+ foreach (var (sector, weatherId) in ev.ActiveWeather)
+ {
+ _activeWeather[sector] = weatherId;
+ }
+ }
+}
diff --git a/Content.Client/Shuttles/UI/NavScreen.xaml b/Content.Client/Shuttles/UI/NavScreen.xaml
index d67992f8179..753fa631bb0 100644
--- a/Content.Client/Shuttles/UI/NavScreen.xaml
+++ b/Content.Client/Shuttles/UI/NavScreen.xaml
@@ -35,6 +35,16 @@
Text="0.0, 0.0"
HorizontalExpand="True"
Align="Right"/>
+
+
+
+
_sectorHazards = new();
public Action? OnDampingModeChanged;
@@ -26,6 +32,7 @@ public NavScreen()
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_xformSystem = _entManager.System();
+ _sectorSystem = _entManager.System();
IFFToggle.OnToggled += OnIFFTogglePressed;
IFFToggle.Pressed = NavRadar.ShowIFF;
@@ -72,6 +79,18 @@ public void UpdateState(NavInterfaceState scc)
{
NavRadar.UpdateState(scc);
DampingModeButton.SelectId((int)scc.DampingMode);
+ _sectorHazards = scc.SectorWeatherEvents;
+ }
+
+ private string ResolveHazardName(string prototypeId)
+ {
+ if (_prototypes.TryIndex(prototypeId, out var weather) &&
+ !string.IsNullOrWhiteSpace(weather.Name))
+ {
+ return Loc.GetString(weather.Name);
+ }
+
+ return prototypeId;
}
public void SetMatrix(EntityCoordinates? coordinates, Angle? angle)
@@ -99,6 +118,21 @@ protected override void Draw(DrawingHandleScreen handle)
GridPosition.Text = Loc.GetString("shuttle-console-position-value",
("X", $"{worldPos.X:0.0}"),
("Y", $"{worldPos.Y:0.0}"));
+ var currentSector = _sectorSystem.GetSector(worldPos);
+ GridSector.Text = Loc.GetString("shuttle-console-sector-value",
+ ("sector", _sectorSystem.GetSectorName(currentSector)));
+
+ if (_sectorHazards.TryGetValue(currentSector, out var hazardId))
+ {
+ GridHazardWeather.Text = Loc.GetString("shuttle-console-hazard-event-single", ("event", ResolveHazardName(hazardId)));
+ GridHazardWeather.Modulate = Color.OrangeRed;
+ }
+ else
+ {
+ GridHazardWeather.Text = Loc.GetString("shuttle-console-hazard-event-none");
+ GridHazardWeather.Modulate = Color.White;
+ }
+
GridOrientation.Text = Loc.GetString("shuttle-console-orientation-value",
("angle", $"{displayRot.Degrees:0.0}"));
diff --git a/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs b/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs
index aef872e2905..b1012c44d8f 100644
--- a/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs
+++ b/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs
@@ -1,4 +1,7 @@
using Content.Shared.Movement.Components;
+using Content.Shared.CCVar;
+using Content.Shared.Sectors;
+using Content.Shared.Sectors.Prototypes;
using Content.Shared.Shuttles.BUIStates;
using Content.Shared.Shuttles.Components;
using Content.Shared.Shuttles.Systems;
@@ -7,11 +10,13 @@
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Configuration;
using Robust.Shared.Input;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
+using Robust.Shared.Prototypes;
using System.Numerics;
namespace Content.Client.Shuttles.UI;
@@ -20,7 +25,10 @@ namespace Content.Client.Shuttles.UI;
public sealed partial class ShuttleNavControl : BaseShuttleControl
{
[Dependency] private readonly IMapManager _mapManager = default!;
+ [Dependency] private readonly IConfigurationManager _configuration = default!;
+ [Dependency] private readonly IPrototypeManager _prototypes = default!;
private readonly SharedShuttleSystem _shuttles;
+ private readonly SharedSectorSystem _sectors;
private readonly SharedTransformSystem _transform;
///
@@ -36,6 +44,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
private Angle? _rotation;
private Dictionary> _docks = new();
+ private Dictionary _sectorWeatherEvents = new();
public bool ShowIFF { get; set; } = true;
public bool ShowDocks { get; set; } = true;
@@ -55,6 +64,7 @@ public ShuttleNavControl() : base(64f, 256f, 256f)
{
RobustXamlLoader.Load(this);
_shuttles = EntManager.System();
+ _sectors = EntManager.System();
_transform = EntManager.System();
}
@@ -125,6 +135,7 @@ public void UpdateState(NavInterfaceState state)
RotateWithEntity = state.RotateWithEntity;
_docks = state.Docks;
+ _sectorWeatherEvents = state.SectorWeatherEvents;
}
protected override void Draw(DrawingHandleScreen handle)
@@ -152,6 +163,11 @@ protected override void Draw(DrawingHandleScreen handle)
}
var mapPos = _transform.ToMapCoordinates(_coordinates.Value);
+
+ var sectorName = _sectors.GetSectorName(_sectors.GetSector(mapPos.Position));
+ var sectorText = Loc.GetString("shuttle-console-sector-value", ("sector", sectorName));
+ handle.DrawString(Font, new Vector2(8f, 8f), sectorText, Color.White);
+
var posMatrix = Matrix3Helpers.CreateTransform(_coordinates.Value.Position, _rotation.Value);
var ourEntRot = RotateWithEntity ? _transform.GetWorldRotation(xform) : _rotation.Value;
var ourEntMatrix = Matrix3Helpers.CreateTransform(_transform.GetWorldPosition(xform), ourEntRot);
@@ -159,6 +175,9 @@ protected override void Draw(DrawingHandleScreen handle)
var shuttleToWorld = Matrix3x2.Multiply(posMatrix, ourEntMatrix);
Matrix3x2.Invert(shuttleToWorld, out var worldToShuttle);
var shuttleToView = Matrix3x2.CreateScale(new Vector2(MinimapScale, -MinimapScale)) * Matrix3x2.CreateTranslation(MidPointVector);
+ var worldToView = worldToShuttle * shuttleToView;
+
+ DrawSectorBoundaries(handle, worldToView);
var map = _transform.GetMap(xform.Coordinates);
if (map != null && mapBoundsQuery.TryGetComponent(map, out var mapBounds) && mapBounds != null)
@@ -307,7 +326,7 @@ protected override void Draw(DrawingHandleScreen handle)
if (consoleXform.ParentUid != _coordinates.Value.EntityId)
{
var consolePositionWorld = _transform.GetWorldPosition((EntityUid)_consoleEntity);
- var p = Vector2.Transform(consolePositionWorld, worldToShuttle * shuttleToView);
+ var p = Vector2.Transform(consolePositionWorld, worldToView);
handle.DrawCircle(p, 5, Color.ToSrgb(Color.Cyan), true);
}
}
@@ -319,6 +338,133 @@ private bool ShouldFade(IFFComponent? iff)
return !_shuttles.MatchesSortTag(iff, SortMode);
}
+ private void DrawSectorBoundaries(DrawingHandleScreen handle, Matrix3x2 worldToView)
+ {
+ var centerRadius = _configuration.GetCVar(CCVars.SectorCenterRadius);
+ var maxRadius = _configuration.GetCVar(CCVars.SectorMaxRadius);
+
+ var origin = Vector2.Transform(Vector2.Zero, worldToView);
+ var centerEdge = Vector2.Transform(new Vector2(centerRadius, 0f), worldToView);
+ var scaledCenterRadius = (centerEdge - origin).Length();
+
+ var ringColor = new Color(0.43f, 0.66f, 0.87f, 0.34f);
+ var borderColor = new Color(0.34f, 0.56f, 0.77f, 0.24f);
+
+ handle.DrawCircle(origin, scaledCenterRadius, ringColor, false);
+
+ // Octant boundaries: 22.5 + n * 45 keeps sector centers on cardinals and diagonals.
+ for (var i = 0; i < 8; i++)
+ {
+ DrawSectorBoundaryRay(handle, worldToView, centerRadius, maxRadius, SectorHelpers.EastLowerBoundary + (45f * i), borderColor);
+ }
+
+ foreach (var (sector, weatherId) in _sectorWeatherEvents)
+ {
+ if (!_prototypes.TryIndex(weatherId, out var weather))
+ continue;
+
+ var weatherColor = Color.ToSrgb(weather.BorderColor);
+
+ if (sector == SpaceSector.Center)
+ {
+ handle.DrawCircle(origin, scaledCenterRadius, weatherColor, false);
+ continue;
+ }
+
+ if (!TryGetSectorBoundaryAngles(sector, out var firstBoundary, out var secondBoundary))
+ continue;
+
+ DrawSectorBoundaryRay(handle, worldToView, centerRadius, maxRadius, firstBoundary, weatherColor);
+ DrawSectorBoundaryRay(handle, worldToView, centerRadius, maxRadius, secondBoundary, weatherColor);
+ DrawSectorArc(handle, worldToView, centerRadius, firstBoundary, secondBoundary, weatherColor);
+ }
+ }
+
+ private static bool TryGetSectorBoundaryAngles(SpaceSector sector, out float firstBoundary, out float secondBoundary)
+ {
+ switch (sector)
+ {
+ case SpaceSector.NorthEast:
+ firstBoundary = SectorHelpers.EastLowerBoundary;
+ secondBoundary = SectorHelpers.NorthEastUpperBoundary;
+ return true;
+ case SpaceSector.North:
+ firstBoundary = SectorHelpers.NorthEastUpperBoundary;
+ secondBoundary = SectorHelpers.NorthUpperBoundary;
+ return true;
+ case SpaceSector.NorthWest:
+ firstBoundary = SectorHelpers.NorthUpperBoundary;
+ secondBoundary = SectorHelpers.NorthWestUpperBoundary;
+ return true;
+ case SpaceSector.West:
+ firstBoundary = SectorHelpers.NorthWestUpperBoundary;
+ secondBoundary = SectorHelpers.WestUpperBoundary;
+ return true;
+ case SpaceSector.SouthWest:
+ firstBoundary = SectorHelpers.WestUpperBoundary;
+ secondBoundary = SectorHelpers.SouthWestUpperBoundary;
+ return true;
+ case SpaceSector.South:
+ firstBoundary = SectorHelpers.SouthWestUpperBoundary;
+ secondBoundary = SectorHelpers.SouthUpperBoundary;
+ return true;
+ case SpaceSector.SouthEast:
+ firstBoundary = SectorHelpers.SouthUpperBoundary;
+ secondBoundary = SectorHelpers.SouthEastUpperBoundary;
+ return true;
+ case SpaceSector.East:
+ firstBoundary = SectorHelpers.SouthEastUpperBoundary;
+ secondBoundary = SectorHelpers.EastLowerBoundary;
+ return true;
+ default:
+ firstBoundary = 0f;
+ secondBoundary = 0f;
+ return false;
+ }
+ }
+
+ private static void DrawSectorBoundaryRay(
+ DrawingHandleScreen handle,
+ Matrix3x2 worldToView,
+ float centerRadius,
+ float maxRadius,
+ float boundaryDegrees,
+ Color color)
+ {
+ var boundaryDirection = Angle.FromDegrees(boundaryDegrees).ToVec();
+ var startWorld = boundaryDirection * centerRadius;
+ var endpointWorld = boundaryDirection * maxRadius;
+ var startView = Vector2.Transform(startWorld, worldToView);
+ var endpointView = Vector2.Transform(endpointWorld, worldToView);
+ handle.DrawLine(startView, endpointView, color);
+ }
+
+ private static void DrawSectorArc(
+ DrawingHandleScreen handle,
+ Matrix3x2 worldToView,
+ float centerRadius,
+ float startDegrees,
+ float endDegrees,
+ Color color)
+ {
+ const int segments = 12;
+ // Handle wrap-around (e.g. East sector: 337.5° → 22.5°)
+ var range = endDegrees > startDegrees
+ ? endDegrees - startDegrees
+ : endDegrees + 360f - startDegrees;
+
+ Vector2? prev = null;
+ for (var i = 0; i <= segments; i++)
+ {
+ var t = i / (float) segments;
+ var dir = Angle.FromDegrees(startDegrees + range * t).ToVec();
+ var viewPos = Vector2.Transform(dir * centerRadius, worldToView);
+ if (prev.HasValue)
+ handle.DrawLine(prev.Value, viewPos, color);
+ prev = viewPos;
+ }
+ }
+
private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3x2 gridToView)
{
if (!ShowDocks)
diff --git a/Content.Server/Administration/Commands/SectorWeatherCommand.cs b/Content.Server/Administration/Commands/SectorWeatherCommand.cs
new file mode 100644
index 00000000000..a35bf345064
--- /dev/null
+++ b/Content.Server/Administration/Commands/SectorWeatherCommand.cs
@@ -0,0 +1,64 @@
+using System.Linq;
+using Content.Server.Sectors.Systems;
+using Content.Shared.Administration;
+using Content.Shared.Sectors;
+using Content.Shared.Sectors.Prototypes;
+using Robust.Shared.Console;
+
+namespace Content.Server.Administration.Commands;
+
+[AdminCommand(AdminFlags.Admin)]
+public sealed class SectorWeatherCommand : LocalizedEntityCommands
+{
+ [Dependency] private readonly SectorWeatherSystem _sectorWeather = default!;
+
+ public override string Command => "sectorweather";
+
+ public override void Execute(IConsoleShell shell, string argStr, string[] args)
+ {
+ if (args.Length != 2)
+ {
+ shell.WriteLine("Usage: sectorweather ");
+ return;
+ }
+
+ if (!Enum.TryParse(args[0], true, out var sector))
+ {
+ shell.WriteError($"Invalid sector '{args[0]}'.");
+ return;
+ }
+
+ var value = args[1];
+ if (string.Equals(value, "clear", StringComparison.OrdinalIgnoreCase))
+ {
+ if (_sectorWeather.ClearWeather(sector))
+ shell.WriteLine($"Cleared sector weather for {sector}.");
+ else
+ shell.WriteLine($"No active weather found for {sector}.");
+
+ return;
+ }
+
+ if (!_sectorWeather.TrySetWeather(sector, value))
+ {
+ shell.WriteError($"Unknown sector weather prototype '{value}'.");
+ return;
+ }
+
+ shell.WriteLine($"Set {sector} weather to {value}.");
+ }
+
+ public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
+ {
+ var sectorOptions = Enum.GetNames();
+
+ return args.Length switch
+ {
+ 1 => CompletionResult.FromHintOptions(sectorOptions, "Sector name"),
+ 2 => CompletionResult.FromHintOptions(
+ CompletionHelper.PrototypeIDs().Append(new CompletionOption("clear")),
+ "Weather prototype ID or clear"),
+ _ => CompletionResult.Empty,
+ };
+ }
+}
diff --git a/Content.Server/Salvage/Magnet/SalvageMagnetDataComponent.cs b/Content.Server/Salvage/Magnet/SalvageMagnetDataComponent.cs
index 91331db126d..6933537f40b 100644
--- a/Content.Server/Salvage/Magnet/SalvageMagnetDataComponent.cs
+++ b/Content.Server/Salvage/Magnet/SalvageMagnetDataComponent.cs
@@ -35,7 +35,7 @@ public sealed partial class SalvageMagnetDataComponent : Component
/// Cooldown between offerings after one ends.
///
[DataField]
- public TimeSpan OfferCooldown = TimeSpan.FromMinutes(3);
+ public TimeSpan OfferCooldown = TimeSpan.FromMinutes(15);
///
/// Seeds currently offered
diff --git a/Content.Server/Sectors/Events/SectorWeatherChangedEvent.cs b/Content.Server/Sectors/Events/SectorWeatherChangedEvent.cs
new file mode 100644
index 00000000000..ae1bc53cbd6
--- /dev/null
+++ b/Content.Server/Sectors/Events/SectorWeatherChangedEvent.cs
@@ -0,0 +1,15 @@
+using Content.Shared.Sectors;
+
+namespace Content.Server.Sectors.Events;
+
+public sealed class SectorWeatherChangedEvent : EntityEventArgs
+{
+ public SpaceSector Sector { get; }
+ public string? WeatherId { get; }
+
+ public SectorWeatherChangedEvent(SpaceSector sector, string? weatherId)
+ {
+ Sector = sector;
+ WeatherId = weatherId;
+ }
+}
diff --git a/Content.Server/Sectors/Systems/SectorWeatherSystem.cs b/Content.Server/Sectors/Systems/SectorWeatherSystem.cs
new file mode 100644
index 00000000000..088384ddd12
--- /dev/null
+++ b/Content.Server/Sectors/Systems/SectorWeatherSystem.cs
@@ -0,0 +1,97 @@
+using Content.Server.Administration.Logs;
+using Content.Server.Sectors.Events;
+using Content.Shared.Database;
+using Content.Shared.Sectors.Events;
+using Content.Shared.Sectors;
+using Content.Shared.Sectors.Prototypes;
+using Robust.Server.Player;
+using Robust.Shared.Enums;
+using Robust.Shared.Player;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Log;
+
+namespace Content.Server.Sectors.Systems;
+
+///
+/// Tracks active sector weather events and broadcasts changes for UI systems.
+///
+public sealed class SectorWeatherSystem : EntitySystem
+{
+ [Dependency] private readonly IPlayerManager _players = default!;
+ [Dependency] private readonly IPrototypeManager _prototypes = default!;
+ [Dependency] private readonly IAdminLogManager _adminLog = default!;
+
+ private readonly Dictionary _activeWeather = new();
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ _activeWeather.Clear();
+ _players.PlayerStatusChanged += OnPlayerStatusChanged;
+ }
+
+ public override void Shutdown()
+ {
+ base.Shutdown();
+ _players.PlayerStatusChanged -= OnPlayerStatusChanged;
+ _activeWeather.Clear();
+ BroadcastWeatherState();
+ }
+
+ public Dictionary GetWeatherSnapshot()
+ {
+ return new Dictionary(_activeWeather);
+ }
+
+ public Dictionary GetHazardWeatherSnapshot()
+ {
+ var snapshot = new Dictionary();
+
+ foreach (var (sector, weatherId) in _activeWeather)
+ {
+ if (!_prototypes.TryIndex(weatherId, out var weather))
+ continue;
+
+ if (weather.Hazard)
+ snapshot[sector] = weatherId;
+ }
+
+ return snapshot;
+ }
+
+ public bool TrySetWeather(SpaceSector sector, string weatherId)
+ {
+ if (!_prototypes.HasIndex(weatherId))
+ return false;
+
+ _activeWeather[sector] = weatherId;
+ RaiseLocalEvent(new SectorWeatherChangedEvent(sector, weatherId));
+ BroadcastWeatherState();
+ _adminLog.Add(LogType.Action, LogImpact.Medium, $"Sector weather event '{weatherId}' set for sector {sector}.");
+ return true;
+ }
+
+ public bool ClearWeather(SpaceSector sector)
+ {
+ if (!_activeWeather.Remove(sector, out var clearedId))
+ return false;
+
+ RaiseLocalEvent(new SectorWeatherChangedEvent(sector, null));
+ BroadcastWeatherState();
+ _adminLog.Add(LogType.Action, LogImpact.Medium, $"Sector weather event '{clearedId}' cleared from sector {sector}.");
+ return true;
+ }
+
+ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs args)
+ {
+ if (args.NewStatus != SessionStatus.Connected)
+ return;
+
+ RaiseNetworkEvent(new SectorWeatherStateUpdateEvent(GetWeatherSnapshot()), args.Session.Channel);
+ }
+
+ private void BroadcastWeatherState()
+ {
+ RaiseNetworkEvent(new SectorWeatherStateUpdateEvent(GetWeatherSnapshot()));
+ }
+}
diff --git a/Content.Server/Shuttles/Systems/RadarConsoleSystem.cs b/Content.Server/Shuttles/Systems/RadarConsoleSystem.cs
index 3ac93e702dd..8d6aeeb19b0 100644
--- a/Content.Server/Shuttles/Systems/RadarConsoleSystem.cs
+++ b/Content.Server/Shuttles/Systems/RadarConsoleSystem.cs
@@ -4,6 +4,8 @@
using Robust.Server.GameObjects;
using Robust.Shared.Map;
using System.Numerics;
+using Content.Server.Sectors.Events;
+using Content.Server.Sectors.Systems;
namespace Content.Server.Shuttles.Systems;
@@ -11,6 +13,7 @@ public sealed class RadarConsoleSystem : SharedRadarConsoleSystem
{
[Dependency] private readonly ShuttleConsoleSystem _console = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
+ [Dependency] private readonly SectorWeatherSystem _sectorWeather = default!;
public override void Initialize()
{
@@ -21,6 +24,8 @@ public override void Initialize()
{
subs.Event(UpdateUserInterface);
});
+
+ SubscribeLocalEvent(OnSectorWeatherChanged);
}
private void UpdateUserInterface(EntityUid uid, RadarConsoleComponent component, BoundUIOpenedEvent args)
@@ -33,6 +38,15 @@ private void OnRadarStartup(EntityUid uid, RadarConsoleComponent component, Comp
UpdateState(uid, component);
}
+ private void OnSectorWeatherChanged(SectorWeatherChangedEvent ev)
+ {
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var comp))
+ {
+ UpdateState(uid, comp);
+ }
+ }
+
protected override void UpdateState(EntityUid uid, RadarConsoleComponent component)
{
var xform = Transform(uid);
diff --git a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs
index a8b07791edc..0d1ca146b73 100644
--- a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs
+++ b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs
@@ -1,4 +1,6 @@
using Content.Server.Power.EntitySystems;
+using Content.Server.Sectors.Events;
+using Content.Server.Sectors.Systems;
using Content.Server.Shuttles.Components;
using Content.Server.Shuttles.Events;
using Content.Server.Station.Systems;
@@ -37,6 +39,7 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
[Dependency] private readonly TagSystem _tags = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!;
[Dependency] private readonly SharedContentEyeSystem _eyeSystem = default!;
+ [Dependency] private readonly SectorWeatherSystem _sectorWeather = default!;
private EntityQuery _metaQuery;
private EntityQuery _xformQuery;
@@ -74,6 +77,7 @@ public override void Initialize()
SubscribeLocalEvent(OnDock);
SubscribeLocalEvent(OnUndock);
+ SubscribeLocalEvent(OnSectorWeatherChanged);
SubscribeLocalEvent(OnGetState);
SubscribeLocalEvent(OnStopPilotingAlert);
@@ -104,6 +108,11 @@ private void OnUndock(UndockEvent ev)
RefreshShuttleConsoles();
}
+ private void OnSectorWeatherChanged(SectorWeatherChangedEvent ev)
+ {
+ RefreshShuttleConsoles();
+ }
+
///
/// Refreshes all the shuttle console data for a particular grid.
///
@@ -277,7 +286,13 @@ private void UpdateState(EntityUid consoleUid, ref DockingInterfaceState? dockSt
}
else
{
- navState = new NavInterfaceState(0f, null, null, new Dictionary>(), ShuttleDampingMode.Normal);
+ navState = new NavInterfaceState(
+ 0f,
+ null,
+ null,
+ new Dictionary>(),
+ ShuttleDampingMode.Normal,
+ _sectorWeather.GetHazardWeatherSnapshot());
mapState = new ShuttleMapInterfaceState(
FTLState.Invalid,
default,
@@ -399,7 +414,7 @@ public NavInterfaceState GetNavState(Entity
diff --git a/Content.Server/Shuttles/Systems/StationAnchorSystem.cs b/Content.Server/Shuttles/Systems/StationAnchorSystem.cs
index d2dd9732572..def1796836e 100644
--- a/Content.Server/Shuttles/Systems/StationAnchorSystem.cs
+++ b/Content.Server/Shuttles/Systems/StationAnchorSystem.cs
@@ -1,3 +1,4 @@
+using Content.Server.Cargo.Components;
using Content.Server.Popups;
using Content.Server.Power.EntitySystems;
using Content.Server.Shuttles.Components;
@@ -86,7 +87,8 @@ private void SetStatus(Entity ent, bool enabled, Shuttle
}
else
{
- _shuttleSystem.Enable(grid.Value);
+ if(!TryComp(grid, out _))
+ _shuttleSystem.Enable(grid.Value);
}
shuttleComponent.Enabled = !enabled;
diff --git a/Content.Server/Spawners/Components/EntityTableSpawnerComponent.cs b/Content.Server/Spawners/Components/EntityTableSpawnerComponent.cs
index d122eb098b6..739f0d3ce68 100644
--- a/Content.Server/Spawners/Components/EntityTableSpawnerComponent.cs
+++ b/Content.Server/Spawners/Components/EntityTableSpawnerComponent.cs
@@ -19,6 +19,12 @@ public sealed partial class EntityTableSpawnerComponent : Component
[DataField]
public float Offset = 0.2f;
+ ///
+ /// If true, will spawn at the coordinates of the spawner, but detached from them.
+ ///
+ [DataField]
+ public bool SpawnDetached = false;
+
///
/// A variable meaning whether the spawn will
/// be able to be used again or whether
diff --git a/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs b/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs
index 4613a4e8e1f..a8b04f80985 100644
--- a/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs
+++ b/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs
@@ -135,7 +135,12 @@ private void Spawn(Entity ent)
var yOffset = _robustRandom.NextFloat(-offset, offset);
var trueCoords = coords.Offset(new Vector2(xOffset, yOffset));
- SpawnAttachedTo(proto, trueCoords);
+ if (ent.Comp.SpawnDetached)
+ {
+ SpawnAtPosition(proto, trueCoords); // Currently ignores offset... would be nice to have.
+ }
+ else
+ SpawnAttachedTo(proto, trueCoords);
}
}
}
diff --git a/Content.Shared/CCVar/CCVars.Sectors.cs b/Content.Shared/CCVar/CCVars.Sectors.cs
new file mode 100644
index 00000000000..894532968af
--- /dev/null
+++ b/Content.Shared/CCVar/CCVars.Sectors.cs
@@ -0,0 +1,40 @@
+using Robust.Shared.Configuration;
+
+namespace Content.Shared.CCVar;
+
+public sealed partial class CCVars
+{
+ ///
+ /// Radius around the world origin that resolves to the center sector.
+ ///
+ public static readonly CVarDef SectorCenterRadius =
+ CVarDef.Create("sector.center_radius", 1250f, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
+
+ ///
+ /// Reserved maximum radius for future sector-based systems.
+ ///
+ public static readonly CVarDef SectorMaxRadius =
+ CVarDef.Create("sector.max_radius", 50000f, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
+
+ ///
+ /// Optional display name overrides for each sector. Empty string uses localization defaults.
+ ///
+ public static readonly CVarDef SectorNameCenter =
+ CVarDef.Create("sector.name.center", string.Empty, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
+ public static readonly CVarDef SectorNameNorth =
+ CVarDef.Create("sector.name.north", string.Empty, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
+ public static readonly CVarDef SectorNameNorthEast =
+ CVarDef.Create("sector.name.northeast", string.Empty, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
+ public static readonly CVarDef SectorNameEast =
+ CVarDef.Create("sector.name.east", string.Empty, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
+ public static readonly CVarDef SectorNameSouthEast =
+ CVarDef.Create("sector.name.southeast", string.Empty, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
+ public static readonly CVarDef SectorNameSouth =
+ CVarDef.Create("sector.name.south", string.Empty, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
+ public static readonly CVarDef SectorNameSouthWest =
+ CVarDef.Create("sector.name.southwest", string.Empty, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
+ public static readonly CVarDef SectorNameWest =
+ CVarDef.Create("sector.name.west", string.Empty, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
+ public static readonly CVarDef SectorNameNorthWest =
+ CVarDef.Create("sector.name.northwest", string.Empty, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
+}
\ No newline at end of file
diff --git a/Content.Shared/CrewAssignments/Components/CrewAssignmentsComponent.cs b/Content.Shared/CrewAssignments/Components/CrewAssignmentsComponent.cs
index 38bae780abd..6d57e2be0f7 100644
--- a/Content.Shared/CrewAssignments/Components/CrewAssignmentsComponent.cs
+++ b/Content.Shared/CrewAssignments/Components/CrewAssignmentsComponent.cs
@@ -2,12 +2,15 @@
namespace Content.Shared.CrewAssignments.Components;
-[RegisterComponent]
+[RegisterComponent, NetworkedComponent]
+[AutoGenerateComponentState]
public sealed partial class CrewAssignmentsComponent : Component
{
[DataField]
+ [AutoNetworkedField]
public Dictionary CrewAssignments { get; set; } = new();
[DataField]
+ [AutoNetworkedField]
public int NextID = 1;
public bool TryGetAssignment(int id, out CrewAssignment? assignment)
{
diff --git a/Content.Shared/CrewRecords/Components/CrewRecordsComponent.cs b/Content.Shared/CrewRecords/Components/CrewRecordsComponent.cs
index 338c159b844..97896f2f7a4 100644
--- a/Content.Shared/CrewRecords/Components/CrewRecordsComponent.cs
+++ b/Content.Shared/CrewRecords/Components/CrewRecordsComponent.cs
@@ -2,10 +2,12 @@
namespace Content.Shared.CrewRecords.Components;
-[RegisterComponent]
+[RegisterComponent, NetworkedComponent]
+[AutoGenerateComponentState]
public sealed partial class CrewRecordsComponent : Component
{
[DataField]
+ [AutoNetworkedField]
public Dictionary CrewRecords { get; set; } = new();
public bool TryGetRecord(string name, out CrewRecord? record)
diff --git a/Content.Shared/DeltaV/TapeRecorder/Components/TapeRecorderComponent.cs b/Content.Shared/DeltaV/TapeRecorder/Components/TapeRecorderComponent.cs
index 991e4f0c5a8..2184a6e7286 100644
--- a/Content.Shared/DeltaV/TapeRecorder/Components/TapeRecorderComponent.cs
+++ b/Content.Shared/DeltaV/TapeRecorder/Components/TapeRecorderComponent.cs
@@ -1,4 +1,5 @@
using Content.Shared.DeltaV.TapeRecorder.Systems;
+using Content.Shared.DeviceLinking;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
@@ -79,4 +80,20 @@ public sealed partial class TapeRecorderComponent : Component
{
Params = AudioParams.Default.WithVolume(-2f).WithMaxDistance(3f)
};
+
+ ///
+ /// Ports for signal control
+ ///
+ [DataField, AutoNetworkedField]
+ public ProtoId PausePort = "Pause";
+
+ [DataField, AutoNetworkedField]
+ public ProtoId RecordPort = "Record";
+
+ [DataField, AutoNetworkedField]
+ public ProtoId PlaybackPort = "Playback";
+
+ [DataField, AutoNetworkedField]
+ public ProtoId RewindPort = "Rewind";
+
}
diff --git a/Content.Shared/DeltaV/TapeRecorder/Systems/SharedTapeRecorderSystem.cs b/Content.Shared/DeltaV/TapeRecorder/Systems/SharedTapeRecorderSystem.cs
index d672372fe87..8ce2c35a4fa 100644
--- a/Content.Shared/DeltaV/TapeRecorder/Systems/SharedTapeRecorderSystem.cs
+++ b/Content.Shared/DeltaV/TapeRecorder/Systems/SharedTapeRecorderSystem.cs
@@ -9,6 +9,8 @@
using Content.Shared.Toggleable;
using Content.Shared.UserInterface;
using Content.Shared.Whitelist;
+using Content.Shared.DeviceLinking;
+using Content.Shared.DeviceLinking.Events;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Random;
@@ -42,6 +44,7 @@ public override void Initialize()
SubscribeLocalEvent(OnRecorderExamined);
SubscribeLocalEvent(OnChangeModeMessage);
SubscribeLocalEvent(OnUIOpened);
+ SubscribeLocalEvent(OnSignalReceived);
SubscribeLocalEvent(OnTapeExamined);
SubscribeLocalEvent(OnDamagedChanged);
@@ -412,6 +415,26 @@ private void UpdateUI(Entity ent)
_ui.SetUiState(uid, TapeRecorderUIKey.Key, state);
}
+
+ private void OnSignalReceived(Entity ent, ref SignalReceivedEvent args)
+ {
+ if (args.Port == ent.Comp.PausePort)
+ {
+ SetMode(ent, TapeRecorderMode.Stopped);
+ }
+ else if (args.Port == ent.Comp.RecordPort)
+ {
+ SetMode(ent, TapeRecorderMode.Recording);
+ }
+ else if (args.Port == ent.Comp.PlaybackPort)
+ {
+ SetMode(ent, TapeRecorderMode.Playing);
+ }
+ else if (args.Port == ent.Comp.RewindPort)
+ {
+ SetMode(ent, TapeRecorderMode.Rewinding);
+ }
+ }
}
[Serializable, NetSerializable]
diff --git a/Content.Shared/Friends/Systems/PettableFriendSystem.cs b/Content.Shared/Friends/Systems/PettableFriendSystem.cs
index 6e41f4bdea9..35cd109c3ea 100644
--- a/Content.Shared/Friends/Systems/PettableFriendSystem.cs
+++ b/Content.Shared/Friends/Systems/PettableFriendSystem.cs
@@ -56,7 +56,6 @@ private void OnRehydrated(Entity ent, ref GotRehydrated
// can only pet before hydrating, after that the fish cannot be negotiated with
if (!TryComp(ent, out var comp))
return;
-
- _factionException.IgnoreEntities(args.Target, comp.Ignored);
+ // _factionException.IgnoreEntities(args.Target, comp.Ignored);
}
}
diff --git a/Content.Shared/Nutrition/Components/HungerComponent.cs b/Content.Shared/Nutrition/Components/HungerComponent.cs
index d5db991b1a0..48694c2591b 100644
--- a/Content.Shared/Nutrition/Components/HungerComponent.cs
+++ b/Content.Shared/Nutrition/Components/HungerComponent.cs
@@ -34,7 +34,7 @@ public sealed partial class HungerComponent : Component
///
/// Any time this is modified, should be called.
[DataField("baseDecayRate"), ViewVariables(VVAccess.ReadWrite)]
- public float BaseDecayRate = 0.01666666666f;
+ public float BaseDecayRate = 0.008333333f;
///
/// The actual amount at which decays.
@@ -97,7 +97,7 @@ public sealed partial class HungerComponent : Component
[AutoNetworkedField]
public Dictionary HungerThresholdDecayModifiers = new()
{
- { HungerThreshold.Overfed, 1.2f },
+ { HungerThreshold.Overfed, 1.1f },
{ HungerThreshold.Okay, 1f },
{ HungerThreshold.Peckish, 0.8f },
{ HungerThreshold.Starving, 0.6f },
diff --git a/Content.Shared/Nutrition/Components/ThirstComponent.cs b/Content.Shared/Nutrition/Components/ThirstComponent.cs
index 5c32b4af286..4f9e2fd4fe8 100644
--- a/Content.Shared/Nutrition/Components/ThirstComponent.cs
+++ b/Content.Shared/Nutrition/Components/ThirstComponent.cs
@@ -14,7 +14,7 @@ public sealed partial class ThirstComponent : Component
[ViewVariables(VVAccess.ReadWrite)]
[DataField("baseDecayRate")]
[AutoNetworkedField]
- public float BaseDecayRate = 0.1f;
+ public float BaseDecayRate = 0.05f;
[ViewVariables(VVAccess.ReadWrite)]
[AutoNetworkedField]
diff --git a/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs b/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs
index 9e5c34eeb8c..9ed4a0acc30 100644
--- a/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs
+++ b/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs
@@ -225,8 +225,8 @@ private bool GetMovementThreshold(HungerThreshold threshold)
{
case HungerThreshold.Overfed:
case HungerThreshold.Okay:
- return true;
case HungerThreshold.Peckish:
+ return true;
case HungerThreshold.Starving:
case HungerThreshold.Dead:
return false;
diff --git a/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs b/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs
index 1730f7b3f21..51e244043fa 100644
--- a/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs
+++ b/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs
@@ -193,7 +193,7 @@ private void UpdateEffects(EntityUid uid, ThirstComponent component)
{
case ThirstThreshold.OverHydrated:
component.LastThirstThreshold = component.CurrentThirstThreshold;
- component.ActualDecayRate = component.BaseDecayRate * 1.2f;
+ component.ActualDecayRate = component.BaseDecayRate * 1.1f;
return;
case ThirstThreshold.Okay:
diff --git a/Content.Shared/Sectors/Events/SectorWeatherStateUpdateEvent.cs b/Content.Shared/Sectors/Events/SectorWeatherStateUpdateEvent.cs
new file mode 100644
index 00000000000..e6e8a4becb4
--- /dev/null
+++ b/Content.Shared/Sectors/Events/SectorWeatherStateUpdateEvent.cs
@@ -0,0 +1,15 @@
+using Content.Shared.Sectors;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Sectors.Events;
+
+[Serializable, NetSerializable]
+public sealed class SectorWeatherStateUpdateEvent : EntityEventArgs
+{
+ public readonly Dictionary ActiveWeather;
+
+ public SectorWeatherStateUpdateEvent(Dictionary activeWeather)
+ {
+ ActiveWeather = activeWeather;
+ }
+}
\ No newline at end of file
diff --git a/Content.Shared/Sectors/Prototypes/SectorWeatherPrototype.cs b/Content.Shared/Sectors/Prototypes/SectorWeatherPrototype.cs
new file mode 100644
index 00000000000..c6829e955d2
--- /dev/null
+++ b/Content.Shared/Sectors/Prototypes/SectorWeatherPrototype.cs
@@ -0,0 +1,29 @@
+using Robust.Shared.Maths;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Sectors.Prototypes;
+
+[Prototype("sectorWeather")]
+public sealed partial class SectorWeatherPrototype : IPrototype
+{
+ [IdDataField]
+ public string ID { get; private set; } = default!;
+
+ [DataField]
+ public string Name { get; private set; } = string.Empty;
+
+ [DataField(required: true)]
+ public Color BorderColor { get; private set; } = Color.White;
+
+ [DataField]
+ public bool Hazard { get; private set; } = false;
+
+ [DataField]
+ public Color ScreenTintColor { get; private set; } = Color.Transparent;
+
+ [DataField]
+ public float ScreenTintStrength { get; private set; } = 1f;
+
+ [DataField]
+ public float ScreenTintNoiseStrength { get; private set; } = 0f;
+}
diff --git a/Content.Shared/Sectors/SectorHelpers.cs b/Content.Shared/Sectors/SectorHelpers.cs
new file mode 100644
index 00000000000..889f2e53ab2
--- /dev/null
+++ b/Content.Shared/Sectors/SectorHelpers.cs
@@ -0,0 +1,54 @@
+using System.Numerics;
+using Robust.Shared.Maths;
+
+namespace Content.Shared.Sectors;
+
+///
+/// Shared sector math helpers for classifying world positions into compass sectors.
+///
+public static class SectorHelpers
+{
+ public const float EastLowerBoundary = 22.5f;
+ public const float NorthEastUpperBoundary = 67.5f;
+ public const float NorthUpperBoundary = 112.5f;
+ public const float NorthWestUpperBoundary = 157.5f;
+ public const float WestUpperBoundary = 202.5f;
+ public const float SouthWestUpperBoundary = 247.5f;
+ public const float SouthUpperBoundary = 292.5f;
+ public const float SouthEastUpperBoundary = 337.5f;
+
+ ///
+ /// Classifies a world position into a space sector relative to the fixed origin.
+ ///
+ public static SpaceSector GetSector(Vector2 worldPos, float centerRadius)
+ {
+ if (worldPos.Length() <= centerRadius)
+ return SpaceSector.Center;
+
+ var angleRadians = MathF.Atan2(worldPos.Y, worldPos.X);
+ var degrees = (MathHelper.RadiansToDegrees(angleRadians) + 360f) % 360f;
+
+ if (degrees >= SouthEastUpperBoundary || degrees < EastLowerBoundary)
+ return SpaceSector.East;
+
+ if (degrees < NorthEastUpperBoundary)
+ return SpaceSector.NorthEast;
+
+ if (degrees < NorthUpperBoundary)
+ return SpaceSector.North;
+
+ if (degrees < NorthWestUpperBoundary)
+ return SpaceSector.NorthWest;
+
+ if (degrees < WestUpperBoundary)
+ return SpaceSector.West;
+
+ if (degrees < SouthWestUpperBoundary)
+ return SpaceSector.SouthWest;
+
+ if (degrees < SouthUpperBoundary)
+ return SpaceSector.South;
+
+ return SpaceSector.SouthEast;
+ }
+}
\ No newline at end of file
diff --git a/Content.Shared/Sectors/SharedSectorSystem.cs b/Content.Shared/Sectors/SharedSectorSystem.cs
new file mode 100644
index 00000000000..b3d309267f1
--- /dev/null
+++ b/Content.Shared/Sectors/SharedSectorSystem.cs
@@ -0,0 +1,88 @@
+using System.Numerics;
+using Content.Shared.CCVar;
+using Robust.Shared.Configuration;
+
+namespace Content.Shared.Sectors;
+
+///
+/// Shared API for resolving named sectors from world positions and entities.
+///
+public sealed class SharedSectorSystem : EntitySystem
+{
+ [Dependency] private readonly IConfigurationManager _configuration = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+
+ private float CenterRadius => _configuration.GetCVar(CCVars.SectorCenterRadius);
+
+ ///
+ /// Gets the space sector for the provided world position.
+ ///
+ public SpaceSector GetSector(Vector2 worldPos)
+ {
+ return SectorHelpers.GetSector(worldPos, CenterRadius);
+ }
+
+ ///
+ /// Gets the space sector for an entity's world position.
+ ///
+ public SpaceSector GetSector(EntityUid uid)
+ {
+ return GetSector(_transform.GetWorldPosition(uid));
+ }
+
+ ///
+ /// Tries to resolve the space sector for an entity.
+ ///
+ public bool TryGetSector(EntityUid uid, out SpaceSector sector)
+ {
+ if (!Exists(uid))
+ {
+ sector = default;
+ return false;
+ }
+
+ sector = GetSector(uid);
+ return true;
+ }
+
+ ///
+ /// Gets the localized display name for a sector.
+ ///
+ public string GetSectorName(SpaceSector sector)
+ {
+ var overrideName = _configuration.GetCVar(GetSectorNameCVar(sector));
+ if (!string.IsNullOrWhiteSpace(overrideName))
+ return overrideName;
+
+ return Loc.GetString(sector switch
+ {
+ SpaceSector.Center => "sector-center",
+ SpaceSector.North => "sector-north",
+ SpaceSector.NorthEast => "sector-northeast",
+ SpaceSector.East => "sector-east",
+ SpaceSector.SouthEast => "sector-southeast",
+ SpaceSector.South => "sector-south",
+ SpaceSector.SouthWest => "sector-southwest",
+ SpaceSector.West => "sector-west",
+ SpaceSector.NorthWest => "sector-northwest",
+ _ => throw new ArgumentOutOfRangeException(nameof(sector), sector, null),
+ });
+ }
+
+ public static CVarDef GetSectorNameCVar(SpaceSector sector)
+ {
+ return sector switch
+ {
+ SpaceSector.Center => CCVars.SectorNameCenter,
+ SpaceSector.North => CCVars.SectorNameNorth,
+ SpaceSector.NorthEast => CCVars.SectorNameNorthEast,
+ SpaceSector.East => CCVars.SectorNameEast,
+ SpaceSector.SouthEast => CCVars.SectorNameSouthEast,
+ SpaceSector.South => CCVars.SectorNameSouth,
+ SpaceSector.SouthWest => CCVars.SectorNameSouthWest,
+ SpaceSector.West => CCVars.SectorNameWest,
+ SpaceSector.NorthWest => CCVars.SectorNameNorthWest,
+ _ => throw new ArgumentOutOfRangeException(nameof(sector), sector, null),
+ };
+ }
+}
\ No newline at end of file
diff --git a/Content.Shared/Sectors/SpaceSector.cs b/Content.Shared/Sectors/SpaceSector.cs
new file mode 100644
index 00000000000..85184976980
--- /dev/null
+++ b/Content.Shared/Sectors/SpaceSector.cs
@@ -0,0 +1,14 @@
+namespace Content.Shared.Sectors;
+
+public enum SpaceSector
+{
+ Center,
+ North,
+ NorthEast,
+ East,
+ SouthEast,
+ South,
+ SouthWest,
+ West,
+ NorthWest,
+}
\ No newline at end of file
diff --git a/Content.Shared/Shuttles/BUIStates/NavInterfaceState.cs b/Content.Shared/Shuttles/BUIStates/NavInterfaceState.cs
index afda10ff808..5acd5bef296 100644
--- a/Content.Shared/Shuttles/BUIStates/NavInterfaceState.cs
+++ b/Content.Shared/Shuttles/BUIStates/NavInterfaceState.cs
@@ -1,3 +1,4 @@
+using Content.Shared.Sectors;
using Content.Shared.Shuttles.Components;
using Robust.Shared.Map;
using Robust.Shared.Serialization;
@@ -21,6 +22,8 @@ public sealed class NavInterfaceState
public Dictionary> Docks;
+ public Dictionary SectorWeatherEvents;
+
public bool RotateWithEntity = true;
public readonly ShuttleDampingMode DampingMode;
@@ -29,13 +32,16 @@ public NavInterfaceState(
float maxRange,
NetCoordinates? coordinates,
Angle? angle,
- Dictionary> docks, ShuttleDampingMode dampingMode)
+ Dictionary> docks,
+ ShuttleDampingMode dampingMode,
+ Dictionary? sectorWeatherEvents = null)
{
MaxRange = maxRange;
Coordinates = coordinates;
Angle = angle;
Docks = docks;
DampingMode = dampingMode;
+ SectorWeatherEvents = sectorWeatherEvents ?? new Dictionary();
}
}
diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs
index b18f9cb69d7..a8983b639c6 100644
--- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs
+++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs
@@ -52,6 +52,12 @@ public sealed partial class XenoArtifactNodeComponent : Component
[DataField, AutoNetworkedField]
public int MaxDurability = 5;
+ ///
+ /// The maximum factor by which using the durability of an artifact will scale it's Research Value.
+ ///
+ [DataField]
+ public float DurabilityResearchMultiplier = 2f;
+
///
/// The variance from MaxDurability present when a node is created.
///
diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs
index 7958ef955b5..590b317745c 100644
--- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs
+++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs
@@ -370,7 +370,7 @@ List>> otherSegments
///
/// Sets node research point amount that can be extracted.
- /// Used up durability increases amount to be extracted.
+ /// Used up durability increases amount to be extracted. (It looks like the exact opposite is true?)
///
public void UpdateNodeResearchValue(Entity node)
{
@@ -383,13 +383,12 @@ public void UpdateNodeResearchValue(Entity node)
var artifact = _xenoArtifactQuery.Get(nodeComponent.Attached.Value);
- var nonactiveNodes = GetActiveNodes(artifact);
+ var nonactiveNodes = GetActiveNodes(artifact); // This seems like its... wrong...
var durabilityEffect = MathF.Pow((float)nodeComponent.Durability / nodeComponent.MaxDurability, 2);
- var durabilityMultiplier = nonactiveNodes.Contains(node)
- ? 1f - durabilityEffect
- : 1f + durabilityEffect;
+ var durabilityMultiplier = nodeComponent.DurabilityResearchMultiplier - (nodeComponent.DurabilityResearchMultiplier - 1) * durabilityEffect;
var predecessorNodes = GetPredecessorNodes((artifact, artifact), node);
- nodeComponent.ResearchValue = (int)(Math.Pow(1.25, Math.Pow(predecessorNodes.Count, 1.5f)) * nodeComponent.BasePointValue * durabilityMultiplier);
+ var predecessorMultiplier = Math.Pow(1.25, Math.Pow(predecessorNodes.Count, 1.5f));
+ nodeComponent.ResearchValue = (int)(nodeComponent.BasePointValue * predecessorMultiplier * durabilityMultiplier);
}
}
diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAE.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAE.cs
index d98fa059f37..12a1f5be0e7 100644
--- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAE.cs
+++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAE.cs
@@ -5,6 +5,7 @@
using Content.Shared.Timing;
using Content.Shared.Xenoarchaeology.Artifact.Components;
using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
namespace Content.Shared.Xenoarchaeology.Artifact;
@@ -12,6 +13,8 @@ public abstract partial class SharedXenoArtifactSystem
{
[Dependency] private readonly UseDelaySystem _useDelay = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly SharedMapSystem _map = default!;
private void InitializeXAE()
{
@@ -25,7 +28,8 @@ private void OnUseInHand(Entity ent, ref UseInHandEvent a
if (args.Handled)
return;
- args.Handled = TryActivateXenoArtifact(ent, args.User, args.User, Transform(args.User).Coordinates);
+ var coords = GetSafeActivationCoordininates(Transform(args.User).Coordinates);
+ args.Handled = TryActivateXenoArtifact(ent, args.User, args.User, coords);
}
private void OnAfterInteract(Entity ent, ref AfterInteractEvent args)
@@ -134,6 +138,25 @@ public bool ActivateNode(
RaiseLocalEvent(node, ref ev);
return true;
}
+
+ ///
+ /// Cleans the entity coordinates to remove any potential parenting to the activator of an artifact.
+ ///
+ ///
+ ///
+ private EntityCoordinates GetSafeActivationCoordininates(EntityCoordinates coords)
+ {
+ var mapCoords = _transform.ToMapCoordinates(coords);
+ var gridUid = _transform.GetGrid(coords);
+
+ if (gridUid != null && TryComp(gridUid, out var mapGrid))
+ {
+ return new EntityCoordinates(gridUid.Value, _map.WorldToLocal(gridUid.Value, mapGrid, mapCoords.Position));
+ }
+
+ var mapUid = _map.GetMap(mapCoords.MapId);
+ return new EntityCoordinates(mapUid, mapCoords.Position);
+ }
}
///
diff --git a/Content.Tests/Shared/Sectors/SectorHelpersTest.cs b/Content.Tests/Shared/Sectors/SectorHelpersTest.cs
new file mode 100644
index 00000000000..3f4fc94e68e
--- /dev/null
+++ b/Content.Tests/Shared/Sectors/SectorHelpersTest.cs
@@ -0,0 +1,30 @@
+using System.Numerics;
+using Content.Shared.Sectors;
+using NUnit.Framework;
+using Robust.UnitTesting;
+
+namespace Content.Tests.Shared.Sectors;
+
+[TestFixture]
+public sealed class SectorHelpersTest : RobustUnitTest
+{
+ private const float CenterRadius = 1250f;
+
+ [TestCase(0f, 0f, SpaceSector.Center)]
+ [TestCase(10000f, 0f, SpaceSector.East)]
+ [TestCase(-10000f, 0f, SpaceSector.West)]
+ [TestCase(0f, 10000f, SpaceSector.North)]
+ [TestCase(0f, -10000f, SpaceSector.South)]
+ [TestCase(10000f, 10000f, SpaceSector.NorthEast)]
+ [TestCase(-10000f, 10000f, SpaceSector.NorthWest)]
+ [TestCase(10000f, -10000f, SpaceSector.SouthEast)]
+ [TestCase(-10000f, -10000f, SpaceSector.SouthWest)]
+ [TestCase(1250f, 0f, SpaceSector.Center)]
+ [TestCase(1250.1f, 0f, SpaceSector.East)]
+ public void GetSector_ReturnsExpectedSector(float x, float y, SpaceSector expected)
+ {
+ var sector = SectorHelpers.GetSector(new Vector2(x, y), CenterRadius);
+
+ Assert.That(sector, Is.EqualTo(expected));
+ }
+}
\ No newline at end of file
diff --git a/Resources/ConfigPresets/persist.toml b/Resources/ConfigPresets/persist.toml
index 2720089c487..8d16d903142 100644
--- a/Resources/ConfigPresets/persist.toml
+++ b/Resources/ConfigPresets/persist.toml
@@ -3,11 +3,11 @@
# Related docs https://docs.spacestation14.com/en/general-development/tips/config-file-reference.html
[game]
-soft_max_players = 100
+soft_max_players = 150
autosaveenabled = true
autosaveinterval = 60
panic_bunker.show_reason = true
-panic_bunker.custom_reason = "The server is shutting down soon and is no longer accepting new connections."
+panic_bunker.custom_reason = "The server is not accepting new connections at this time."
[hub]
tags = "lang:en,region:am_n_w,rp:high"
@@ -26,6 +26,12 @@ enabled_crit = true
flavor_text = true
punctuation = true
+[lore]
+year_offset = 200
+
+[tips]
+enabled = false
+
[atmos]
max_explosion_range = 4
diff --git a/Resources/Locale/en-US/administration/ui/tabs/admin-tab/player-actions-window.ftl b/Resources/Locale/en-US/administration/ui/tabs/admin-tab/player-actions-window.ftl
index 171e25d29fe..7f9ee1d99de 100644
--- a/Resources/Locale/en-US/administration/ui/tabs/admin-tab/player-actions-window.ftl
+++ b/Resources/Locale/en-US/administration/ui/tabs/admin-tab/player-actions-window.ftl
@@ -3,6 +3,22 @@ admin-player-actions-window-admin-ghost = Admin Ghost
admin-player-actions-window-permissions = Permissions Panel
admin-player-actions-window-announce = Announce
admin-player-actions-window-shuttle = (Re)call Shuttle
+admin-player-actions-window-sector-tools = Sector Tools
admin-player-actions-window-admin-logs = Admin Logs
admin-player-actions-window-admin-fax = Admin Fax
admin-player-actions-window-admin-chat = Admin Chat
+
+admin-sector-title = Sector Tools
+admin-sector-weather-header = Sector Event
+admin-sector-sector-label = Sector:
+admin-sector-weather-id-label = Event ID:
+admin-sector-set-weather = Set Event
+admin-sector-clear-weather = Clear Event
+admin-sector-name-label = Sector Name:
+admin-sector-set-name = Set Name
+admin-sector-clear-name = Clear Name
+admin-sector-radius-header = Sector Radii (CVars)
+admin-sector-center-radius-label = Center Radius:
+admin-sector-set-center-radius = Set Center Radius
+admin-sector-max-radius-label = Max Radius:
+admin-sector-set-max-radius = Set Max Radius
diff --git a/Resources/Locale/en-US/deltav/linkports/link-ports.ftl b/Resources/Locale/en-US/deltav/linkports/link-ports.ftl
new file mode 100644
index 00000000000..63e73db41b9
--- /dev/null
+++ b/Resources/Locale/en-US/deltav/linkports/link-ports.ftl
@@ -0,0 +1,4 @@
+signal-port-description-playback = Start audio playback.
+signal-port-description-record = Start recording audio.
+signal-port-description-rewind = Rewind audio.
+signal-port-description-pause = Stop playing, recording, or rewinding.
diff --git a/Resources/Locale/en-US/sectors/weather.ftl b/Resources/Locale/en-US/sectors/weather.ftl
new file mode 100644
index 00000000000..9d3aed45aa2
--- /dev/null
+++ b/Resources/Locale/en-US/sectors/weather.ftl
@@ -0,0 +1,2 @@
+sector-weather-gravitational-storm = Gravitational Storm
+sector-weather-electron-surge = Electron Surge
diff --git a/Resources/Locale/en-US/shuttles/console.ftl b/Resources/Locale/en-US/shuttles/console.ftl
index 75b82fa69ed..f820578e492 100644
--- a/Resources/Locale/en-US/shuttles/console.ftl
+++ b/Resources/Locale/en-US/shuttles/console.ftl
@@ -11,12 +11,26 @@ shuttle-console-display-label = Display
shuttle-console-position = Position:
shuttle-console-position-value = {$X}, {$Y}
+shuttle-console-sector = Current Sector:
+shuttle-console-sector-value = {$sector}
+shuttle-console-hazard-event = Hazard:
+shuttle-console-hazard-event-none = None
+shuttle-console-hazard-event-single = {$event}
shuttle-console-orientation = Orientation:
shuttle-console-orientation-value = {$angle}
shuttle-console-linear-velocity = Linear velocity:
shuttle-console-linear-velocity-value = {$X}, {$Y}
shuttle-console-angular-velocity = Angular velocity:
shuttle-console-angular-velocity-value = {$angularVelocity}
+sector-center = Center
+sector-north = North
+sector-northeast = NorthEast
+sector-east = East
+sector-southeast = SouthEast
+sector-south = South
+sector-southwest = SouthWest
+sector-west = West
+sector-northwest = NorthWest
shuttle-console-damping-label = Damping
shuttle-console-damping-cruise = Cruise
shuttle-console-damping-normal = Normal
diff --git a/Resources/Prototypes/Body/Species/diona.yml b/Resources/Prototypes/Body/Species/diona.yml
index 7790c67ecf5..e32531cbe27 100644
--- a/Resources/Prototypes/Body/Species/diona.yml
+++ b/Resources/Prototypes/Body/Species/diona.yml
@@ -79,9 +79,9 @@
types:
Asphyxiation: -1.0
- type: Hunger
- baseDecayRate: 0.0083
+ baseDecayRate: 0.00415
- type: Thirst
- baseDecayRate: 0.0083
+ baseDecayRate: 0.00415
- type: Damageable
damageModifierSet: Diona
- type: DamageVisuals
diff --git a/Resources/Prototypes/Catalog/Bounties/hunterbounties b/Resources/Prototypes/Catalog/Bounties/hunterbounties.yml
similarity index 99%
rename from Resources/Prototypes/Catalog/Bounties/hunterbounties
rename to Resources/Prototypes/Catalog/Bounties/hunterbounties.yml
index 946b368e69e..728a7860363 100644
--- a/Resources/Prototypes/Catalog/Bounties/hunterbounties
+++ b/Resources/Prototypes/Catalog/Bounties/hunterbounties.yml
@@ -41,6 +41,7 @@
- ToothSpaceCarp
bountyType: PayPer
+- type: cargoBounty
id: BountyTeethSharkminnow
reward: 3000 # 3000*3=9000
description: bounty-description-tooth-sharkminnow
diff --git a/Resources/Prototypes/Catalog/Bounties/infrastructurelevels.yml b/Resources/Prototypes/Catalog/Bounties/infrastructurelevels.yml
index cfd04324256..009a523f2a4 100644
--- a/Resources/Prototypes/Catalog/Bounties/infrastructurelevels.yml
+++ b/Resources/Prototypes/Catalog/Bounties/infrastructurelevels.yml
@@ -11,7 +11,7 @@
ServiceBounty : 1
GasBounty : 1
ReagentBounty : 1
- income: -5000
+ income: 0
- type: infrastructureLevel
id: ILevelGeneral2
@@ -75,7 +75,6 @@
name: Simple Research Outpost (1)
markets:
- sciencemarket
- - explorermarket
groups:
ScienceBounty : 2
MaterialBounty : 2
@@ -90,8 +89,6 @@
markets:
- sciencemarket
- sciencemarket2
- - explorermarket
- - explorermarket2
groups:
ScienceBounty : 3
MaterialBounty : 2
@@ -110,9 +107,6 @@
- sciencemarket
- sciencemarket2
- sciencemarket3
- - explorermarket
- - explorermarket2
- - explorermarket3
groups:
ScienceBounty : 4
MaterialBounty : 2
@@ -132,10 +126,6 @@
- sciencemarket2
- sciencemarket3
- sciencemarket4
- - explorermarket
- - explorermarket2
- - explorermarket3
- - explorermarket4
groups:
ScienceBounty : 5
MaterialBounty : 3
@@ -145,6 +135,85 @@
income: 30000
level: 4
+- type: infrastructureLevel
+ id: ILevelExploration1
+ demotionXP: 0
+ requiredXP: 0
+ name: Simple Explorer's Post (1)
+ markets:
+ - explorermarket
+ - huntermarket
+ groups:
+ ExplorationBounty : 2
+ HunterBounty : 1
+ ServiceBounty : 1
+ income: 0
+
+- type: infrastructureLevel
+ id: ILevelExploration2
+ demotionXP: 150
+ requiredXP: 200
+ name: Decent Explorer's Outpost (2)
+ markets:
+ - explorermarket
+ - explorermarket2
+ - huntermarket
+ - huntermarket2
+ groups:
+ ExplorationBounty : 3
+ HunterBounty : 1
+ ServiceBounty : 1
+ MaterialBounty : 1
+ StationBounty : 1
+ income: 0
+ level: 2
+
+- type: infrastructureLevel
+ id: ILevelExploration3
+ demotionXP: 650
+ requiredXP: 700
+ name: Established Exploration Station (3)
+ markets:
+ - explorermarket
+ - explorermarket2
+ - explorermarket3
+ - huntermarket
+ - huntermarket2
+ - huntermarket3
+ groups:
+ ExplorationBounty : 4
+ HunterBounty : 2
+ ServiceBounty : 2
+ MaterialBounty : 2
+ StationBounty : 2
+ income: 10000
+ level: 3
+
+- type: infrastructureLevel
+ id: ILevelExploration4
+ demotionXP: 1400
+ requiredXP: 1500
+ name: Beacon of Exploration (4)
+ markets:
+ - explorermarket
+ - explorermarket2
+ - explorermarket3
+ - explorermarket4
+ - huntermarket
+ - huntermarket2
+ - huntermarket3
+ - huntermarket4
+ groups:
+ ExplorationBounty : 5
+ HunterBounty : 3
+ ServiceBounty : 3
+ MaterialBounty : 3
+ StationBounty : 3
+ income: 30000
+ level: 4
+
+
+
- type: infrastructureLevel
id: ILevelHunter1
demotionXP: 0
@@ -173,6 +242,8 @@
income: 0
level: 2
+
+
- type: infrastructureLevel
id: ILevelHunter3
demotionXP: 650
@@ -263,63 +334,3 @@
income: 30000
level: 4
-- type: infrastructureLevel
- id: ILevelExploration1
- demotionXP: 0
- requiredXP: 0
- name: Simple Explorer's Post (1)
- markets:
- - explorermarket
- groups:
- ExplorationBounty : 4
- ScienceBounty : 1
- ServiceBounty : 1
- income: -5000
-
-- type: infrastructureLevel
- id: ILevelExploration2
- demotionXP: 150
- requiredXP: 200
- name: Decent Explorer's Outpost (2)
- markets:
- - explorermarket
- - explorermarket2
- groups:
- ExplorationBounty : 6
- ScienceBounty : 1
- ServiceBounty : 2
- income: 0
- level: 2
-
-- type: infrastructureLevel
- id: ILevelExploration3
- demotionXP: 650
- requiredXP: 700
- name: Established Exploration Station (3)
- markets:
- - explorermarket
- - explorermarket2
- - explorermarket3
- groups:
- ExplorationBounty : 8
- ScienceBounty : 2
- ServiceBounty : 2
- income: 10000
- level: 3
-
-- type: infrastructureLevel
- id: ILevelExploration4
- demotionXP: 1400
- requiredXP: 1500
- name: Beacon of Exploration (4)
- markets:
- - explorermarket
- - explorermarket2
- - explorermarket3
- - explorermarket4
- groups:
- ExplorationBounty : 9
- ScienceBounty : 3
- ServiceBounty : 3
- income: 30000
- level: 4
diff --git a/Resources/Prototypes/Catalog/Cargo/cargo_syndicate.yml b/Resources/Prototypes/Catalog/Cargo/cargo_syndicate.yml
index 0d3546fceee..11d68f83bd2 100644
--- a/Resources/Prototypes/Catalog/Cargo/cargo_syndicate.yml
+++ b/Resources/Prototypes/Catalog/Cargo/cargo_syndicate.yml
@@ -251,7 +251,7 @@
product: BriefcaseSyndieSniperBundleFilled
cost: 30000
category: cargoproduct-category-name-syndicate
- group: syndicatemarket5
+ group: syndicatemarket4
- type: cargoProduct
id: ChemicalSynthesisKit
diff --git a/Resources/Prototypes/Chemistry/injector_modes.yml b/Resources/Prototypes/Chemistry/injector_modes.yml
index 1e2b38aa41b..1233efa0224 100644
--- a/Resources/Prototypes/Chemistry/injector_modes.yml
+++ b/Resources/Prototypes/Chemistry/injector_modes.yml
@@ -108,7 +108,8 @@
abstract: true
parent: BaseHyposprayMode
id: BaseInstantHyposprayMode
- mobTime: 0
+ mobTime: 1
+ delayPerVolume: 0.05
- type: injectorMode
parent: [ BaseInstantHyposprayMode, BaseInjectMode ]
diff --git a/Resources/Prototypes/DeltaV/DeviceLinking/sink_ports.yml b/Resources/Prototypes/DeltaV/DeviceLinking/sink_ports.yml
new file mode 100644
index 00000000000..cd82f46c01a
--- /dev/null
+++ b/Resources/Prototypes/DeltaV/DeviceLinking/sink_ports.yml
@@ -0,0 +1,19 @@
+- type: sinkPort
+ id: Pause
+ name: tape-recorder-menu-stopped-button
+ description: signal-port-description-pause
+
+- type: sinkPort
+ id: Record
+ name: tape-recorder-menu-recording-button
+ description: signal-port-description-record
+
+- type: sinkPort
+ id: Playback
+ name: tape-recorder-menu-playing-button
+ description: signal-port-description-playback
+
+- type: sinkPort
+ id: Rewind
+ name: tape-recorder-menu-rewinding-button
+ description: signal-port-description-rewind
diff --git a/Resources/Prototypes/DeltaV/Entities/Objects/Devices/tape_recorder.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Devices/tape_recorder.yml
index bfce3188521..b9179c8bc7f 100644
--- a/Resources/Prototypes/DeltaV/Entities/Objects/Devices/tape_recorder.yml
+++ b/Resources/Prototypes/DeltaV/Entities/Objects/Devices/tape_recorder.yml
@@ -50,6 +50,17 @@
interfaces:
enum.TapeRecorderUIKey.Key:
type: TapeRecorderBoundUserInterface
+ - type: DeviceNetwork
+ deviceNetId: Wireless
+ receiveFrequencyId: BasicDevice
+ - type: WirelessNetworkConnection
+ range: 200
+ - type: DeviceLinkSink
+ ports:
+ - Pause
+ - Record
+ - Playback
+ - Rewind
- type: entity
parent: TapeRecorder
diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml
index 4e0e6aeff6d..9bec663084a 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml
@@ -111,6 +111,20 @@
- type: StaticPrice
price: 90
+
+- type: entity
+ parent: BaseComputerCircuitboard
+ id: MessageBoardCircuitboard
+ name: message board computer board
+ description: A computer printed circuit board for a message board computer.
+ components:
+ - type: Sprite
+ state: cpu_supply
+ - type: ComputerBoard
+ prototype: ComputerMessageBoard
+ - type: StaticPrice
+ price: 90
+
- type: entity
parent: BaseComputerCircuitboard
id: StationModComputerCircuitboard
diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml
index fc20f1a6863..58941b21513 100644
--- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml
+++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml
@@ -407,6 +407,8 @@
Quantity: 8
- ReagentId: Titanium
Quantity: 4
+ - ReagentId: Phoron
+ Quantity: .2 # 5 phoron glass to 1 Phoron reagent
canReact: false
- type: entity
@@ -490,6 +492,8 @@
Quantity: 4.5
- ReagentId: Carbon
Quantity: 0.5
+ - ReagentId: Phoron
+ Quantity: 0.2
canReact: false
- type: entity
diff --git a/Resources/Prototypes/Entities/Objects/Materials/materials.yml b/Resources/Prototypes/Entities/Objects/Materials/materials.yml
index 944886b6adf..14bd6d8d994 100644
--- a/Resources/Prototypes/Entities/Objects/Materials/materials.yml
+++ b/Resources/Prototypes/Entities/Objects/Materials/materials.yml
@@ -445,6 +445,8 @@
Quantity: 20
- ReagentId: Titanium
Quantity: 10
+ - ReagentId: Phoron
+ Quantity: .2 # 5 phoron alloy to 1u Phoron reagent
canReact: false
- type: Material
- type: PhysicalComposition
diff --git a/Resources/Prototypes/Entities/Objects/Materials/shards.yml b/Resources/Prototypes/Entities/Objects/Materials/shards.yml
index 985a90a36c6..8d34699f4a1 100644
--- a/Resources/Prototypes/Entities/Objects/Materials/shards.yml
+++ b/Resources/Prototypes/Entities/Objects/Materials/shards.yml
@@ -228,7 +228,7 @@
- ReagentId: Silicon
Quantity: 5
- ReagentId: Phoron
- Quantity: 5
+ Quantity: .1
- type: Construction
graph: PhoronShivConstruct
node: start
diff --git a/Resources/Prototypes/Procedural/salvage_rewards.yml b/Resources/Prototypes/Procedural/salvage_rewards.yml
index 984ccc26d29..7bd685be663 100644
--- a/Resources/Prototypes/Procedural/salvage_rewards.yml
+++ b/Resources/Prototypes/Procedural/salvage_rewards.yml
@@ -15,9 +15,9 @@
RandomArtifactSpawner: 0.25
# weighted down since it sells for a lot
NuclearBombKeg: 0.1
- # money
- SpaceCash500: 0.5
- SpaceCash1000: 0.25
+ # # money
+ # SpaceCash500: 0.5
+ # SpaceCash1000: 0.25
- type: weightedRandomEntity
id: SalvageRewardRare
@@ -46,10 +46,10 @@
ClothingOuterArmorBasicSlim: 0.25
# rare weapons
WeaponMakeshiftLaser: 0.1
- # money
- SpaceCash500: 1.0
- SpaceCash1000: 0.75
- SpaceCash2500: 0.5
+ # # money
+ # SpaceCash500: 1.0
+ # SpaceCash1000: 0.75
+ # SpaceCash2500: 0.5
- type: weightedRandomEntity
id: SalvageRewardEpic
@@ -69,7 +69,7 @@
# rare chemicals
ChemistryBottleCognizine: 1.0
ChemistryBottleOmnizine: 1.0
- # money
- SpaceCash2500: 1.0
- SpaceCash5000: 0.75
- SpaceCash10000: 0.5
+ # # money
+ # SpaceCash2500: 1.0
+ # SpaceCash5000: 0.75
+ # SpaceCash10000: 0.5
diff --git a/Resources/Prototypes/Reagents/medicine.yml b/Resources/Prototypes/Reagents/medicine.yml
index b2e3a8063ea..5650265a114 100644
--- a/Resources/Prototypes/Reagents/medicine.yml
+++ b/Resources/Prototypes/Reagents/medicine.yml
@@ -853,10 +853,10 @@
effects:
- !type:EvenHealthChange
damage:
- Burn: -2
- Toxin: -2
- Airloss: -2
- Brute: -2
+ Burn: -0.2
+ Toxin: -0.2
+ Airloss: -0.2
+ Brute: -0.2
- type: reagent
id: Ultravasculine
diff --git a/Resources/Prototypes/Recipes/Lathes/Packs/service.yml b/Resources/Prototypes/Recipes/Lathes/Packs/service.yml
index 1706c161cfb..f65b2662414 100644
--- a/Resources/Prototypes/Recipes/Lathes/Packs/service.yml
+++ b/Resources/Prototypes/Recipes/Lathes/Packs/service.yml
@@ -46,6 +46,8 @@
- SodaDispenserMachineCircuitboard
- SmartFridgeCircuitboard
- IdPrinterCircuitboard
+ - MessageBoardCircuitboard
+ - StationModComputerCircuitboard
- BlockGameArcadeComputerCircuitboard
- SpaceVillainArcadeComputerCircuitboard
- InvoicePrinterCircuitboard
diff --git a/Resources/Prototypes/Recipes/Lathes/computer_boards.yml b/Resources/Prototypes/Recipes/Lathes/computer_boards.yml
index 6984a0c0702..a899b0e5d0b 100644
--- a/Resources/Prototypes/Recipes/Lathes/computer_boards.yml
+++ b/Resources/Prototypes/Recipes/Lathes/computer_boards.yml
@@ -193,6 +193,16 @@
id: IdPrinterCircuitboard
result: IdPrinterCircuitboard
+- type: latheRecipe
+ parent: [ BaseCircuitboardRecipe, BaseGeneralComputerRecipeCategory ]
+ id: MessageBoardCircuitboard
+ result: MessageBoardCircuitboard
+
+- type: latheRecipe
+ parent: [ BaseCircuitboardRecipe, BaseGeneralComputerRecipeCategory ]
+ id: StationModComputerCircuitboard
+ result: StationModComputerCircuitboard
+
- type: latheRecipe
parent: [ BaseCircuitboardRecipe, BaseGeneralComputerRecipeCategory ]
id: IDComputerCircuitboard
diff --git a/Resources/Prototypes/Recipes/Reactions/chemicals.yml b/Resources/Prototypes/Recipes/Reactions/chemicals.yml
index 5d6559337e3..5671df47771 100644
--- a/Resources/Prototypes/Recipes/Reactions/chemicals.yml
+++ b/Resources/Prototypes/Recipes/Reactions/chemicals.yml
@@ -523,13 +523,18 @@
- type: reaction
id: ArtifactGlue
+ minTemp: 370
reactants:
SpaceGlue:
amount: 1
Artifexium:
amount: 1
+ Phoron:
+ amount: 1
products:
ArtifactGlue: 2
+ Phoron: 1
+
- type: reaction
id: Lye
diff --git a/Resources/Prototypes/Recipes/Reactions/fun.yml b/Resources/Prototypes/Recipes/Reactions/fun.yml
index fb2d184779a..31a3cce7585 100644
--- a/Resources/Prototypes/Recipes/Reactions/fun.yml
+++ b/Resources/Prototypes/Recipes/Reactions/fun.yml
@@ -49,9 +49,11 @@
minTemp: 370
reactants:
SpaceLube:
- amount: 1
+ amount: 2
Slime:
amount: 1
+ Fiber:
+ amount: 1
products:
SpaceGlue: 2
diff --git a/Resources/Prototypes/Recipes/_Persistence14/phoron-balance.yml b/Resources/Prototypes/Recipes/_Persistence14/phoron-balance.yml
new file mode 100644
index 00000000000..ddcfdeb854c
--- /dev/null
+++ b/Resources/Prototypes/Recipes/_Persistence14/phoron-balance.yml
@@ -0,0 +1,8 @@
+- type: reaction
+ id: phoron-decay
+ minTemp: 500
+ reactants:
+ Phoron:
+ amount: 1
+ products:
+ Plasma: 1
\ No newline at end of file
diff --git a/Resources/Prototypes/Sectors/sector_weather.yml b/Resources/Prototypes/Sectors/sector_weather.yml
new file mode 100644
index 00000000000..5eca29b8937
--- /dev/null
+++ b/Resources/Prototypes/Sectors/sector_weather.yml
@@ -0,0 +1,17 @@
+- type: sectorWeather
+ id: GravitationalStorm
+ name: sector-weather-gravitational-storm
+ borderColor: "#7B3FA0C0"
+ hazard: true
+ screenTintColor: "#C78AE6AA"
+ screenTintStrength: 0.12
+ screenTintNoiseStrength: 0.75
+
+- type: sectorWeather
+ id: ElectronSurge
+ name: sector-weather-electron-surge
+ borderColor: "#2B7DC890"
+ hazard: true
+ screenTintColor: "#4DC8FF80"
+ screenTintStrength: 0.08
+ screenTintNoiseStrength: 0.50
diff --git a/Resources/Prototypes/Shaders/shaders.yml b/Resources/Prototypes/Shaders/shaders.yml
index 057abf0ac23..4d453e37645 100644
--- a/Resources/Prototypes/Shaders/shaders.yml
+++ b/Resources/Prototypes/Shaders/shaders.yml
@@ -115,3 +115,8 @@
id: Hologram
kind: source
path: "/Textures/Shaders/hologram.swsl"
+
+- type: shader
+ id: SectorEventTint
+ kind: source
+ path: "/Textures/Shaders/sector_event_tint.swsl"
diff --git a/Resources/Prototypes/XenoArch/effects.yml b/Resources/Prototypes/XenoArch/effects.yml
index 0ceb48401a3..61b09a111f7 100644
--- a/Resources/Prototypes/XenoArch/effects.yml
+++ b/Resources/Prototypes/XenoArch/effects.yml
@@ -486,6 +486,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:GroupSelector
rolls: !type:RangeNumberSelector
range: 1, 4
@@ -515,6 +516,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:AllSelector
children:
- id: FoodBanana
@@ -542,6 +544,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:AllSelector
children:
- id: RandomFloraTree
@@ -641,6 +644,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:AllSelector
children:
- id: RandomInstruments
@@ -661,6 +665,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:GroupSelector
children:
- id: MobMonkey
@@ -751,6 +756,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:AllSelector
children:
- id: SilverOre1
@@ -786,6 +792,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:AllSelector
children:
- id: SilverOre1
@@ -809,6 +816,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:AllSelector
children:
- id: PlasmaOre1
@@ -832,6 +840,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:AllSelector
children:
- id: GoldOre1
@@ -855,6 +864,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:AllSelector
children:
- id: UraniumOre1
@@ -878,6 +888,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:GroupSelector
children:
- id: MobCarpMagic
@@ -901,6 +912,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:GroupSelector
children:
- id: MobMouse
@@ -948,6 +960,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:GroupSelector
children:
- id: MobAdultSlimesYellowAngry
@@ -975,17 +988,18 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:GroupSelector
rolls: !type:RangeNumberSelector
range: 2, 4
children:
- - id: SpaceCash10
+ - id: TreasureCoinIron
weight: 0.75
- - id: SpaceCash100
+ - id: TreasureCoinSilver
weight: 0.5
- - id: SpaceCash500
+ - id: TreasureCoinGold
weight: 0.25
- - id: SpaceCash1000
+ - id: TreasureCoinDiamond
weight: 0.1
- type: entity
@@ -1091,6 +1105,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:AllSelector
children:
- id: RandomAnomalySpawner
@@ -1172,6 +1187,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:GroupSelector
children:
- id: SheetGlass
@@ -1187,6 +1203,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:GroupSelector
children:
- id: SheetSteel
@@ -1202,6 +1219,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:GroupSelector
children:
- id: SheetPlastic
@@ -1229,6 +1247,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:AllSelector
children:
- id: RandomArtifactSpawner
@@ -1275,6 +1294,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:AllSelector
children:
- id: Singularity
@@ -1290,6 +1310,7 @@
components:
- type: EntityTableSpawner
deleteSpawnerAfterSpawn: false
+ spawnDetached: true
table: !type:AllSelector
children:
- id: TeslaEnergyBall
diff --git a/Resources/Textures/Logo/logo.png b/Resources/Textures/Logo/logo.png
index 41127e63f8c..23ad341115c 100644
Binary files a/Resources/Textures/Logo/logo.png and b/Resources/Textures/Logo/logo.png differ
diff --git a/Resources/Textures/Shaders/sector_event_tint.swsl b/Resources/Textures/Shaders/sector_event_tint.swsl
new file mode 100644
index 00000000000..3112c5b906d
--- /dev/null
+++ b/Resources/Textures/Shaders/sector_event_tint.swsl
@@ -0,0 +1,26 @@
+uniform sampler2D SCREEN_TEXTURE;
+uniform highp vec4 tintColor;
+uniform highp float noiseStrength;
+
+void fragment() {
+ highp vec2 uv = FRAGCOORD.xy * SCREEN_PIXEL_SIZE.xy;
+ highp vec2 coord = FRAGCOORD.xy;
+ highp vec4 src = zTextureSpec(SCREEN_TEXTURE, uv);
+
+ // Blend the scene very gently toward the configured tint color.
+ highp float strength = clamp(tintColor.a, 0.0, 1.0);
+ highp float noise = (
+ sin(coord.x * 0.012 + TIME * 0.32) +
+ sin(coord.y * 0.010 - TIME * 0.27) +
+ sin((coord.x + coord.y) * 0.007 + TIME * 0.21)
+ ) / 3.0;
+ highp float noiseFactor = 1.0 + noise * clamp(noiseStrength, 0.0, 1.0);
+ noiseFactor = clamp(noiseFactor, 0.0, 2.0);
+ strength *= noiseFactor;
+ strength = clamp(strength, 0.0, 1.0);
+
+ highp vec3 tinted = mix(src.rgb, tintColor.rgb, 0.18);
+ src.rgb = mix(src.rgb, tinted, strength);
+
+ COLOR = src;
+}