diff --git a/Content.Client/Administration/AdminNameOverlay.cs b/Content.Client/Administration/AdminNameOverlay.cs
index 47d251910c..86c9b595f6 100644
--- a/Content.Client/Administration/AdminNameOverlay.cs
+++ b/Content.Client/Administration/AdminNameOverlay.cs
@@ -85,7 +85,7 @@ internal sealed class AdminNameOverlay : Overlay
{
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 2), _antagLabelClassic, uiScale, _antagColorClassic);
}
- else if (!classic && _filter.Contains(playerInfo.RoleProto.ID))
+ else if (!classic && _filter.Contains(playerInfo.RoleProto))
{
var label = Loc.GetString(playerInfo.RoleProto.Name).ToUpper();
var color = playerInfo.RoleProto.Color;
diff --git a/Content.Client/CartridgeLoader/Cartridges/LogProbeUi.cs b/Content.Client/CartridgeLoader/Cartridges/LogProbeUi.cs
index d28d3228c9..12b678d5d4 100644
--- a/Content.Client/CartridgeLoader/Cartridges/LogProbeUi.cs
+++ b/Content.Client/CartridgeLoader/Cartridges/LogProbeUi.cs
@@ -1,4 +1,5 @@
using Content.Client.UserInterface.Fragments;
+using Content.Shared.CartridgeLoader;
using Content.Shared.CartridgeLoader.Cartridges;
using Robust.Client.UserInterface;
@@ -13,16 +14,23 @@ public sealed partial class LogProbeUi : UIFragment
return _fragment!;
}
- public override void Setup(BoundUserInterface userInterface, EntityUid? fragmentOwner)
+ public override void Setup(BoundUserInterface ui, EntityUid? fragmentOwner)
{
_fragment = new LogProbeUiFragment();
+
+ _fragment.OnPrintPressed += () =>
+ {
+ var ev = new LogProbePrintMessage();
+ var message = new CartridgeUiMessage(ev);
+ ui.SendMessage(message);
+ };
}
public override void UpdateState(BoundUserInterfaceState state)
{
- if (state is not LogProbeUiState logProbeUiState)
+ if (state is not LogProbeUiState cast)
return;
- _fragment?.UpdateState(logProbeUiState.PulledLogs);
+ _fragment?.UpdateState(cast.EntityName, cast.PulledLogs);
}
}
diff --git a/Content.Client/CartridgeLoader/Cartridges/LogProbeUiFragment.xaml b/Content.Client/CartridgeLoader/Cartridges/LogProbeUiFragment.xaml
index d12fb55cdc..cdbaf7d6ee 100644
--- a/Content.Client/CartridgeLoader/Cartridges/LogProbeUiFragment.xaml
+++ b/Content.Client/CartridgeLoader/Cartridges/LogProbeUiFragment.xaml
@@ -18,4 +18,9 @@
+
+
+
+
+
diff --git a/Content.Client/CartridgeLoader/Cartridges/LogProbeUiFragment.xaml.cs b/Content.Client/CartridgeLoader/Cartridges/LogProbeUiFragment.xaml.cs
index b22e0bc196..d9569bd11d 100644
--- a/Content.Client/CartridgeLoader/Cartridges/LogProbeUiFragment.xaml.cs
+++ b/Content.Client/CartridgeLoader/Cartridges/LogProbeUiFragment.xaml.cs
@@ -8,17 +8,24 @@ namespace Content.Client.CartridgeLoader.Cartridges;
[GenerateTypedNameReferences]
public sealed partial class LogProbeUiFragment : BoxContainer
{
+ ///
+ /// Action invoked when the print button gets pressed.
+ ///
+ public Action? OnPrintPressed;
+
public LogProbeUiFragment()
{
RobustXamlLoader.Load(this);
+
+ PrintButton.OnPressed += _ => OnPrintPressed?.Invoke();
}
- public void UpdateState(List logs)
+ public void UpdateState(string name, List logs)
{
- ProbedDeviceContainer.RemoveAllChildren();
+ EntityName.Text = name;
+ PrintButton.Disabled = string.IsNullOrEmpty(name);
- //Reverse the list so the oldest entries appear at the bottom
- logs.Reverse();
+ ProbedDeviceContainer.RemoveAllChildren();
var count = 1;
foreach (var log in logs)
diff --git a/Content.Client/Chat/UI/SpeechBubble.cs b/Content.Client/Chat/UI/SpeechBubble.cs
index aa61e73e31..94505fd892 100644
--- a/Content.Client/Chat/UI/SpeechBubble.cs
+++ b/Content.Client/Chat/UI/SpeechBubble.cs
@@ -217,7 +217,7 @@ namespace Content.Client.Chat.UI
{
StyleClasses = { "speechBox", speechStyleClass },
Children = { label },
- ModulateSelfOverride = Color.White.WithAlpha(0.75f)
+ ModulateSelfOverride = Color.White.WithAlpha(ConfigManager.GetCVar(CCVars.SpeechBubbleBackgroundOpacity))
};
return panel;
@@ -247,21 +247,23 @@ namespace Content.Client.Chat.UI
{
StyleClasses = { "speechBox", speechStyleClass },
Children = { label },
- ModulateSelfOverride = Color.White.WithAlpha(0.75f)
+ ModulateSelfOverride = Color.White.WithAlpha(ConfigManager.GetCVar(CCVars.SpeechBubbleBackgroundOpacity)),
};
return unfanciedPanel;
}
var bubbleHeader = new RichTextLabel
{
- Margin = new Thickness(1, 1, 1, 1)
+ ModulateSelfOverride = Color.White.WithAlpha(ConfigManager.GetCVar(CCVars.SpeechBubbleSpeakerOpacity)),
+ Margin = new Thickness(1, 1, 1, 1),
};
var bubbleContent = new RichTextLabel
{
+ ModulateSelfOverride = Color.White.WithAlpha(ConfigManager.GetCVar(CCVars.SpeechBubbleTextOpacity)),
MaxWidth = SpeechMaxWidth,
Margin = new Thickness(2, 6, 2, 2),
- StyleClasses = { "bubbleContent" }
+ StyleClasses = { "bubbleContent" },
};
//We'll be honest. *Yes* this is hacky. Doing this in a cleaner way would require a bottom-up refactor of how saycode handles sending chat messages. -Myr
@@ -273,7 +275,7 @@ namespace Content.Client.Chat.UI
{
StyleClasses = { "speechBox", speechStyleClass },
Children = { bubbleContent },
- ModulateSelfOverride = Color.White.WithAlpha(0.75f),
+ ModulateSelfOverride = Color.White.WithAlpha(ConfigManager.GetCVar(CCVars.SpeechBubbleBackgroundOpacity)),
HorizontalAlignment = HAlignment.Center,
VerticalAlignment = VAlignment.Bottom,
Margin = new Thickness(4, 14, 4, 2)
@@ -283,7 +285,7 @@ namespace Content.Client.Chat.UI
{
StyleClasses = { "speechBox", speechStyleClass },
Children = { bubbleHeader },
- ModulateSelfOverride = Color.White.WithAlpha(ConfigManager.GetCVar(CCVars.ChatFancyNameBackground) ? 0.75f : 0f),
+ ModulateSelfOverride = Color.White.WithAlpha(ConfigManager.GetCVar(CCVars.ChatFancyNameBackground) ? ConfigManager.GetCVar(CCVars.SpeechBubbleBackgroundOpacity) : 0f),
HorizontalAlignment = HAlignment.Center,
VerticalAlignment = VAlignment.Top
};
diff --git a/Content.Client/Chemistry/EntitySystems/ChemistryGuideDataSystem.cs b/Content.Client/Chemistry/EntitySystems/ChemistryGuideDataSystem.cs
index 4c3a583b2a..374b00aa6e 100644
--- a/Content.Client/Chemistry/EntitySystems/ChemistryGuideDataSystem.cs
+++ b/Content.Client/Chemistry/EntitySystems/ChemistryGuideDataSystem.cs
@@ -94,7 +94,7 @@ public sealed class ChemistryGuideDataSystem : SharedChemistryGuideDataSystem
if (entProto.Abstract || usedNames.Contains(entProto.Name))
continue;
- if (!entProto.TryGetComponent(out var extractableComponent))
+ if (!entProto.TryGetComponent(out var extractableComponent, EntityManager.ComponentFactory))
continue;
//these bloat the hell out of blood/fat
@@ -121,7 +121,7 @@ public sealed class ChemistryGuideDataSystem : SharedChemistryGuideDataSystem
if (extractableComponent.GrindableSolution is { } grindableSolutionId &&
- entProto.TryGetComponent(out var manager) &&
+ entProto.TryGetComponent(out var manager, EntityManager.ComponentFactory) &&
_solutionContainer.TryGetSolution(manager, grindableSolutionId, out var grindableSolution))
{
var data = new ReagentEntitySourceData(
diff --git a/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs b/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs
index 3ef7f0ae73..a669a8da4c 100644
--- a/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs
+++ b/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs
@@ -46,6 +46,8 @@ namespace Content.Client.Chemistry.UI
_window.CreateBottleButton.OnPressed += _ => SendMessage(
new ChemMasterOutputToBottleMessage(
(uint) _window.BottleDosage.Value, _window.LabelLine));
+ _window.BufferSortButton.OnPressed += _ => SendMessage(
+ new ChemMasterSortingTypeCycleMessage());
for (uint i = 0; i < _window.PillTypeButtons.Length; i++)
{
diff --git a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml
index b1f4f5917f..aca316f6b3 100644
--- a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml
+++ b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml
@@ -34,6 +34,7 @@
+
diff --git a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs
index 20c61f10cb..807ec4c1e7 100644
--- a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs
+++ b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs
@@ -140,17 +140,17 @@ namespace Content.Client.Chemistry.UI
// Ensure the Panel Info is updated, including UI elements for Buffer Volume, Output Container and so on
UpdatePanelInfo(castState);
-
+
BufferCurrentVolume.Text = $" {castState.BufferCurrentVolume?.Int() ?? 0}u";
-
+
InputEjectButton.Disabled = castState.InputContainerInfo is null;
OutputEjectButton.Disabled = castState.OutputContainerInfo is null;
CreateBottleButton.Disabled = castState.OutputContainerInfo?.Reagents == null;
CreatePillButton.Disabled = castState.OutputContainerInfo?.Entities == null;
-
+
UpdateDosageFields(castState);
}
-
+
//assign default values for pill and bottle fields.
private void UpdateDosageFields(ChemMasterBoundUserInterfaceState castState)
{
@@ -162,8 +162,9 @@ namespace Content.Client.Chemistry.UI
var bufferVolume = castState.BufferCurrentVolume?.Int() ?? 0;
PillDosage.Value = (int)Math.Min(bufferVolume, castState.PillDosageLimit);
-
+
PillTypeButtons[castState.SelectedPillType].Pressed = true;
+
PillNumber.IsValid = x => x >= 0 && x <= pillNumberMax;
PillDosage.IsValid = x => x > 0 && x <= castState.PillDosageLimit;
BottleDosage.IsValid = x => x >= 0 && x <= bottleAmountMax;
@@ -213,6 +214,17 @@ namespace Content.Client.Chemistry.UI
BufferInfo.Children.Clear();
+ // This has to happen here due to people possibly
+ // setting sorting before putting any chemicals
+ BufferSortButton.Text = state.SortingType switch
+ {
+ ChemMasterSortingType.Alphabetical => Loc.GetString("chem-master-window-sort-type-alphabetical"),
+ ChemMasterSortingType.Quantity => Loc.GetString("chem-master-window-sort-type-quantity"),
+ ChemMasterSortingType.Latest => Loc.GetString("chem-master-window-sort-type-latest"),
+ _ => Loc.GetString("chem-master-window-sort-type-none")
+ };
+
+
if (!state.BufferReagents.Any())
{
BufferInfo.Children.Add(new Label { Text = Loc.GetString("chem-master-window-buffer-empty-text") });
@@ -235,19 +247,48 @@ namespace Content.Client.Chemistry.UI
};
bufferHBox.AddChild(bufferVol);
- // initialises rowCount to allow for striped rows
-
- var rowCount = 0;
+ // This sets up the needed data for sorting later in a list
+ // Its done this way to not repeat having to use same code twice (once for sorting
+ // and once for displaying)
+ var reagentList = new List<(ReagentId reagentId, string name, Color color, FixedPoint2 quantity)>();
foreach (var (reagent, quantity) in state.BufferReagents)
{
var reagentId = reagent;
_prototypeManager.TryIndex(reagentId.Prototype, out ReagentPrototype? proto);
var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text");
var reagentColor = proto?.SubstanceColor ?? default(Color);
- BufferInfo.Children.Add(BuildReagentRow(reagentColor, rowCount++, name, reagentId, quantity, true, true));
+ reagentList.Add(new (reagentId, name, reagentColor, quantity));
+ }
+
+ // We sort here since we need sorted list to be filled first.
+ // You can easily add any new params you need to it.
+ switch (state.SortingType)
+ {
+ case ChemMasterSortingType.Alphabetical:
+ reagentList = reagentList.OrderBy(x => x.name).ToList();
+ break;
+
+ case ChemMasterSortingType.Quantity:
+ reagentList = reagentList.OrderByDescending(x => x.quantity).ToList();
+ break;
+ case ChemMasterSortingType.Latest:
+ reagentList = Enumerable.Reverse(reagentList).ToList();
+ break;
+
+ case ChemMasterSortingType.None:
+ default:
+ // This case is pointless but it is there for readability
+ break;
+ }
+
+ // initialises rowCount to allow for striped rows
+ var rowCount = 0;
+ foreach (var reagent in reagentList)
+ {
+ BufferInfo.Children.Add(BuildReagentRow(reagent.color, rowCount++, reagent.name, reagent.reagentId, reagent.quantity, true, true));
}
}
-
+
private void BuildContainerUI(Control control, ContainerInfo? info, bool addReagentButtons)
{
control.Children.Clear();
@@ -295,7 +336,7 @@ namespace Content.Client.Chemistry.UI
_prototypeManager.TryIndex(reagent.Reagent.Prototype, out ReagentPrototype? proto);
var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text");
var reagentColor = proto?.SubstanceColor ?? default(Color);
-
+
control.Children.Add(BuildReagentRow(reagentColor, rowCount++, name, reagent.Reagent, reagent.Quantity, false, addReagentButtons));
}
}
@@ -315,7 +356,7 @@ namespace Content.Client.Chemistry.UI
}
//this calls the separated button builder, and stores the return to render after labels
var reagentButtonConstructors = CreateReagentTransferButtons(reagent, isBuffer, addReagentButtons);
-
+
// Create the row layout with the color panel
var rowContainer = new BoxContainer
{
@@ -358,7 +399,7 @@ namespace Content.Client.Chemistry.UI
Children = { rowContainer }
};
}
-
+
public string LabelLine
{
get => LabelLineEdit.Text;
diff --git a/Content.Client/Construction/FlatpackSystem.cs b/Content.Client/Construction/FlatpackSystem.cs
index 911ff1279c..f39c2327ca 100644
--- a/Content.Client/Construction/FlatpackSystem.cs
+++ b/Content.Client/Construction/FlatpackSystem.cs
@@ -27,7 +27,7 @@ public sealed class FlatpackSystem : SharedFlatpackSystem
if (!PrototypeManager.TryIndex(machineBoardId, out var machineBoardPrototype))
return;
- if (!machineBoardPrototype.TryGetComponent(out var sprite))
+ if (!machineBoardPrototype.TryGetComponent(out var sprite, EntityManager.ComponentFactory))
return;
Color? color = null;
diff --git a/Content.Client/Gameplay/GameplayState.cs b/Content.Client/Gameplay/GameplayState.cs
index 427dd5c071..0ce6436f2d 100644
--- a/Content.Client/Gameplay/GameplayState.cs
+++ b/Content.Client/Gameplay/GameplayState.cs
@@ -59,13 +59,13 @@ namespace Content.Client.Gameplay
// Version number watermark.
_version = new Label();
+ _version.FontColorOverride = Color.FromHex("#FFFFFF20");
_version.Text = _changelog.GetClientVersion();
- _version.Visible = VersionVisible();
UserInterfaceManager.PopupRoot.AddChild(_version);
- _configurationManager.OnValueChanged(CCVars.HudVersionWatermark, (show) => { _version.Visible = VersionVisible(); });
- _configurationManager.OnValueChanged(CCVars.ForceClientHudVersionWatermark, (show) => { _version.Visible = VersionVisible(); });
+ _configurationManager.OnValueChanged(CCVars.HudVersionWatermark, (show) => { _version.Visible = VersionVisible(); }, true);
+ _configurationManager.OnValueChanged(CCVars.ForceClientHudVersionWatermark, (show) => { _version.Visible = VersionVisible(); }, true);
// TODO make this centered or something
- LayoutContainer.SetPosition(_version, new Vector2(800, 0));
+ LayoutContainer.SetPosition(_version, new Vector2(70, 0));
}
// This allows servers to force the watermark on clients
diff --git a/Content.Client/Ghost/GhostSystem.cs b/Content.Client/Ghost/GhostSystem.cs
index 0a6046cefa..dd2e51d15d 100644
--- a/Content.Client/Ghost/GhostSystem.cs
+++ b/Content.Client/Ghost/GhostSystem.cs
@@ -155,7 +155,7 @@ namespace Content.Client.Ghost
private void OnGhostState(EntityUid uid, GhostComponent component, ref AfterAutoHandleStateEvent args)
{
if (TryComp(uid, out var sprite))
- sprite.LayerSetColor(0, component.color);
+ sprite.LayerSetColor(0, component.Color);
if (uid != _playerManager.LocalEntity)
return;
diff --git a/Content.Client/Implants/ImplanterSystem.cs b/Content.Client/Implants/ImplanterSystem.cs
index 13a90f3e39..cca09f5dad 100644
--- a/Content.Client/Implants/ImplanterSystem.cs
+++ b/Content.Client/Implants/ImplanterSystem.cs
@@ -2,11 +2,15 @@
using Content.Client.Items;
using Content.Shared.Implants;
using Content.Shared.Implants.Components;
+using Robust.Shared.Prototypes;
namespace Content.Client.Implants;
public sealed class ImplanterSystem : SharedImplanterSystem
{
+ [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
+ [Dependency] private readonly IPrototypeManager _proto = default!;
+
public override void Initialize()
{
base.Initialize();
@@ -17,6 +21,18 @@ public sealed class ImplanterSystem : SharedImplanterSystem
private void OnHandleImplanterState(EntityUid uid, ImplanterComponent component, ref AfterAutoHandleStateEvent args)
{
+ if (_uiSystem.TryGetOpenUi(uid, DeimplantUiKey.Key, out var bui))
+ {
+ Dictionary implants = new();
+ foreach (var implant in component.DeimplantWhitelist)
+ {
+ if (_proto.TryIndex(implant, out var proto))
+ implants.Add(proto.ID, proto.Name);
+ }
+
+ bui.UpdateState(implants, component.DeimplantChosen);
+ }
+
component.UiUpdateNeeded = true;
}
}
diff --git a/Content.Client/Implants/UI/DeimplantBoundUserInterface.cs b/Content.Client/Implants/UI/DeimplantBoundUserInterface.cs
new file mode 100644
index 0000000000..0857cdf86f
--- /dev/null
+++ b/Content.Client/Implants/UI/DeimplantBoundUserInterface.cs
@@ -0,0 +1,35 @@
+using Content.Shared.Implants;
+using Robust.Client.UserInterface;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Implants.UI;
+
+public sealed class DeimplantBoundUserInterface : BoundUserInterface
+{
+ [Dependency] private readonly IPrototypeManager _protomanager = default!;
+
+ [ViewVariables]
+ private DeimplantChoiceWindow? _window;
+
+ public DeimplantBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ {
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+
+ _window = this.CreateWindow();
+
+ _window.OnImplantChange += implant => SendMessage(new DeimplantChangeVerbMessage(implant));
+ }
+
+ public void UpdateState(Dictionary implantList, string? implant)
+ {
+ if (_window != null)
+ {
+ _window.UpdateImplantList(implantList);
+ _window.UpdateState(implant);
+ }
+ }
+}
diff --git a/Content.Client/Implants/UI/DeimplantChoiceWindow.xaml b/Content.Client/Implants/UI/DeimplantChoiceWindow.xaml
new file mode 100644
index 0000000000..f0de9f32e0
--- /dev/null
+++ b/Content.Client/Implants/UI/DeimplantChoiceWindow.xaml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Implants/UI/DeimplantChoiceWindow.xaml.cs b/Content.Client/Implants/UI/DeimplantChoiceWindow.xaml.cs
new file mode 100644
index 0000000000..a7ce50d855
--- /dev/null
+++ b/Content.Client/Implants/UI/DeimplantChoiceWindow.xaml.cs
@@ -0,0 +1,53 @@
+using Content.Client.UserInterface.Controls;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.XAML;
+using System.Linq;
+
+namespace Content.Client.Implants.UI;
+
+[GenerateTypedNameReferences]
+public sealed partial class DeimplantChoiceWindow : FancyWindow
+{
+ public Action? OnImplantChange;
+
+ private Dictionary _implants = new();
+
+ private string? _chosenImplant;
+
+ public DeimplantChoiceWindow()
+ {
+ RobustXamlLoader.Load(this);
+
+ ImplantSelector.OnItemSelected += args =>
+ {
+ OnImplantChange?.Invoke(_implants.ElementAt(args.Id).Key);
+ ImplantSelector.SelectId(args.Id);
+ };
+ }
+
+ public void UpdateImplantList(Dictionary implants)
+ {
+ _implants = implants;
+ int i = 0;
+ ImplantSelector.Clear();
+ foreach (var implantDict in _implants)
+ {
+ ImplantSelector.AddItem(implantDict.Value, i);
+ i++;
+ }
+ }
+
+ public void UpdateState(string? implant)
+ {
+ _chosenImplant = implant;
+
+ for (int id = 0; id < ImplantSelector.ItemCount; id++)
+ {
+ if (_implants.ElementAt(id).Key.Equals(_chosenImplant))
+ {
+ ImplantSelector.SelectId(id);
+ break;
+ }
+ }
+ }
+}
diff --git a/Content.Client/Implants/UI/ImplanterStatusControl.cs b/Content.Client/Implants/UI/ImplanterStatusControl.cs
index e2ffabd17d..569dd785d7 100644
--- a/Content.Client/Implants/UI/ImplanterStatusControl.cs
+++ b/Content.Client/Implants/UI/ImplanterStatusControl.cs
@@ -4,17 +4,20 @@ using Content.Client.UserInterface.Controls;
using Content.Shared.Implants.Components;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
+using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client.Implants.UI;
public sealed class ImplanterStatusControl : Control
{
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
private readonly ImplanterComponent _parent;
private readonly RichTextLabel _label;
public ImplanterStatusControl(ImplanterComponent parent)
{
+ IoCManager.InjectDependencies(this);
_parent = parent;
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
_label.MaxWidth = 350;
@@ -43,12 +46,25 @@ public sealed class ImplanterStatusControl : Control
_ => Loc.GetString("injector-invalid-injector-toggle-mode")
};
- var implantName = _parent.ImplanterSlot.HasItem
- ? _parent.ImplantData.Item1
- : Loc.GetString("implanter-empty-text");
+ if (_parent.CurrentMode == ImplanterToggleMode.Draw)
+ {
+ string implantName = _parent.DeimplantChosen != null
+ ? (_prototype.TryIndex(_parent.DeimplantChosen.Value, out EntityPrototype? implantProto) ? implantProto.Name : Loc.GetString("implanter-empty-text"))
+ : Loc.GetString("implanter-empty-text");
- _label.SetMarkup(Loc.GetString("implanter-label",
- ("implantName", implantName),
- ("modeString", modeStringLocalized)));
+ _label.SetMarkup(Loc.GetString("implanter-label-draw",
+ ("implantName", implantName),
+ ("modeString", modeStringLocalized)));
+ }
+ else
+ {
+ var implantName = _parent.ImplanterSlot.HasItem
+ ? _parent.ImplantData.Item1
+ : Loc.GetString("implanter-empty-text");
+
+ _label.SetMarkup(Loc.GetString("implanter-label-inject",
+ ("implantName", implantName),
+ ("modeString", modeStringLocalized)));
+ }
}
}
diff --git a/Content.Client/Lathe/UI/LatheMenu.xaml.cs b/Content.Client/Lathe/UI/LatheMenu.xaml.cs
index fa99a9dd8e..2b18ce28de 100644
--- a/Content.Client/Lathe/UI/LatheMenu.xaml.cs
+++ b/Content.Client/Lathe/UI/LatheMenu.xaml.cs
@@ -94,8 +94,17 @@ public sealed partial class LatheMenu : DefaultWindow
if (!_prototypeManager.TryIndex(recipe, out var proto))
continue;
- if (CurrentCategory != null && proto.Category != CurrentCategory)
- continue;
+ // Category filtering
+ if (CurrentCategory != null)
+ {
+ if (proto.Categories.Count <= 0)
+ continue;
+
+ var validRecipe = proto.Categories.Any(category => category == CurrentCategory);
+
+ if (!validRecipe)
+ continue;
+ }
if (SearchBar.Text.Trim().Length != 0)
{
@@ -179,18 +188,22 @@ public sealed partial class LatheMenu : DefaultWindow
public void UpdateCategories()
{
+ // Get categories from recipes
var currentCategories = new List>();
foreach (var recipeId in Recipes)
{
var recipe = _prototypeManager.Index(recipeId);
- if (recipe.Category == null)
+ if (recipe.Categories.Count <= 0)
continue;
- if (currentCategories.Contains(recipe.Category.Value))
- continue;
+ foreach (var category in recipe.Categories)
+ {
+ if (currentCategories.Contains(category))
+ continue;
- currentCategories.Add(recipe.Category.Value);
+ currentCategories.Add(category);
+ }
}
if (Categories != null && (Categories.Count == currentCategories.Count || !Categories.All(currentCategories.Contains)))
diff --git a/Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs b/Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs
index f3bff9ef0c..9e8ca9a9c9 100644
--- a/Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs
+++ b/Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs
@@ -10,7 +10,7 @@ using Robust.Client.Player;
namespace Content.Client.Movement.Systems;
-public partial class EyeCursorOffsetSystem : EntitySystem
+public sealed partial class EyeCursorOffsetSystem : EntitySystem
{
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
diff --git a/Content.Client/Options/UI/OptionsMenu.xaml b/Content.Client/Options/UI/OptionsMenu.xaml
index 3c398d8590..22be5aeeab 100644
--- a/Content.Client/Options/UI/OptionsMenu.xaml
+++ b/Content.Client/Options/UI/OptionsMenu.xaml
@@ -9,6 +9,7 @@
+
diff --git a/Content.Client/Options/UI/OptionsMenu.xaml.cs b/Content.Client/Options/UI/OptionsMenu.xaml.cs
index df9b1b2ca5..430b86a436 100644
--- a/Content.Client/Options/UI/OptionsMenu.xaml.cs
+++ b/Content.Client/Options/UI/OptionsMenu.xaml.cs
@@ -1,4 +1,4 @@
-using Content.Client.Options.UI.Tabs;
+using Content.Client.Administration.Managers;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
@@ -8,6 +8,8 @@ namespace Content.Client.Options.UI
[GenerateTypedNameReferences]
public sealed partial class OptionsMenu : DefaultWindow
{
+ [Dependency] private readonly IClientAdminManager _adminManager = default!;
+
public OptionsMenu()
{
RobustXamlLoader.Load(this);
@@ -18,6 +20,7 @@ namespace Content.Client.Options.UI
Tabs.SetTabTitle(2, Loc.GetString("ui-options-tab-controls"));
Tabs.SetTabTitle(3, Loc.GetString("ui-options-tab-audio"));
Tabs.SetTabTitle(4, Loc.GetString("ui-options-tab-accessibility"));
+ Tabs.SetTabTitle(5, Loc.GetString("ui-options-tab-admin"));
// CP14-options-menu-start
Tabs.SetTabTitle(5, Loc.GetString("cp14-ui-options-tab-main"));
@@ -28,10 +31,14 @@ namespace Content.Client.Options.UI
public void UpdateTabs()
{
+ var isAdmin = _adminManager.IsAdmin(true);
+ Tabs.SetTabVisible(5, isAdmin);
+
GraphicsTab.Control.ReloadValues();
MiscTab.Control.ReloadValues();
AccessibilityTab.Control.ReloadValues();
AudioTab.Control.ReloadValues();
+ AdminOptionsTab.Control.ReloadValues();
}
}
}
diff --git a/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml b/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml
index 54d92b2b11..763fd995ca 100644
--- a/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml
+++ b/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml
@@ -7,8 +7,11 @@
-
+
+
+
+
diff --git a/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml.cs b/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml.cs
index 15182fbf12..e1dead0b0e 100644
--- a/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml.cs
+++ b/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml.cs
@@ -15,8 +15,11 @@ public sealed partial class AccessibilityTab : Control
Control.AddOptionCheckBox(CCVars.ChatEnableColorName, EnableColorNameCheckBox);
Control.AddOptionCheckBox(CCVars.AccessibilityColorblindFriendly, ColorblindFriendlyCheckBox);
Control.AddOptionCheckBox(CCVars.ReducedMotion, ReducedMotionCheckBox);
- Control.AddOptionPercentSlider(CCVars.ChatWindowOpacity, ChatWindowOpacitySlider);
Control.AddOptionPercentSlider(CCVars.ScreenShakeIntensity, ScreenShakeIntensitySlider);
+ Control.AddOptionPercentSlider(CCVars.ChatWindowOpacity, ChatWindowOpacitySlider);
+ Control.AddOptionPercentSlider(CCVars.SpeechBubbleTextOpacity, SpeechBubbleTextOpacitySlider);
+ Control.AddOptionPercentSlider(CCVars.SpeechBubbleSpeakerOpacity, SpeechBubbleSpeakerOpacitySlider);
+ Control.AddOptionPercentSlider(CCVars.SpeechBubbleBackgroundOpacity, SpeechBubbleBackgroundOpacitySlider);
Control.Initialize();
}
diff --git a/Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml b/Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml
new file mode 100644
index 0000000000..38666b7ec1
--- /dev/null
+++ b/Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml.cs b/Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml.cs
new file mode 100644
index 0000000000..9661adfa20
--- /dev/null
+++ b/Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml.cs
@@ -0,0 +1,20 @@
+using Content.Shared.CCVar;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.Options.UI.Tabs;
+
+[GenerateTypedNameReferences]
+public sealed partial class AdminOptionsTab : Control
+{
+ public AdminOptionsTab()
+ {
+ RobustXamlLoader.Load(this);
+
+ Control.AddOptionCheckBox(CCVars.AdminOverlayClassic, EnableClassicOverlayCheckBox);
+
+ Control.Initialize();
+ }
+}
+
diff --git a/Content.Client/SubFloor/SubFloorHideSystem.cs b/Content.Client/SubFloor/SubFloorHideSystem.cs
index 64d3afc640..5e5f2993b4 100644
--- a/Content.Client/SubFloor/SubFloorHideSystem.cs
+++ b/Content.Client/SubFloor/SubFloorHideSystem.cs
@@ -67,8 +67,11 @@ public sealed class SubFloorHideSystem : SharedSubFloorHideSystem
// allows a t-ray to show wires/pipes above carpets/puddles
if (scannerRevealed)
{
- component.OriginalDrawDepth ??= args.Sprite.DrawDepth;
- args.Sprite.DrawDepth = (int) Shared.DrawDepth.DrawDepth.FloorObjects + 1;
+ if (component.OriginalDrawDepth is not null)
+ return;
+ component.OriginalDrawDepth = args.Sprite.DrawDepth;
+ var drawDepthDifference = Shared.DrawDepth.DrawDepth.ThickPipe - Shared.DrawDepth.DrawDepth.Puddles;
+ args.Sprite.DrawDepth -= drawDepthDifference - 1;
}
else if (component.OriginalDrawDepth.HasValue)
{
diff --git a/Content.Client/SubFloor/TrayScanRevealSystem.cs b/Content.Client/SubFloor/TrayScanRevealSystem.cs
new file mode 100644
index 0000000000..ca3e91a7dc
--- /dev/null
+++ b/Content.Client/SubFloor/TrayScanRevealSystem.cs
@@ -0,0 +1,29 @@
+using System.Linq;
+using Content.Shared.SubFloor;
+using Robust.Shared.Map.Components;
+
+namespace Content.Client.SubFloor;
+
+public sealed class TrayScanRevealSystem : EntitySystem
+{
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly SharedMapSystem _map = default!;
+
+ public bool IsUnderRevealingEntity(EntityUid uid)
+ {
+ var gridUid = _transform.GetGrid(uid);
+ if (gridUid is null)
+ return false;
+
+ var gridComp = Comp(gridUid.Value);
+ var position = _transform.GetGridOrMapTilePosition(uid);
+
+ return HasTrayScanReveal(((EntityUid)gridUid, gridComp), position);
+ }
+
+ private bool HasTrayScanReveal(Entity ent, Vector2i position)
+ {
+ var anchoredEnum = _map.GetAnchoredEntities(ent, position);
+ return anchoredEnum.Any(HasComp);
+ }
+}
diff --git a/Content.Client/SubFloor/TrayScannerSystem.cs b/Content.Client/SubFloor/TrayScannerSystem.cs
index 95bce03c0f..f65c784025 100644
--- a/Content.Client/SubFloor/TrayScannerSystem.cs
+++ b/Content.Client/SubFloor/TrayScannerSystem.cs
@@ -19,6 +19,7 @@ public sealed class TrayScannerSystem : SharedTrayScannerSystem
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly TrayScanRevealSystem _trayScanReveal = default!;
private const string TRayAnimationKey = "trays";
private const double AnimationLength = 0.3;
@@ -82,7 +83,7 @@ public sealed class TrayScannerSystem : SharedTrayScannerSystem
foreach (var (uid, comp) in inRange)
{
- if (comp.IsUnderCover)
+ if (comp.IsUnderCover || _trayScanReveal.IsUnderRevealingEntity(uid))
EnsureComp(uid);
}
}
diff --git a/Content.Client/Toggleable/ToggleableLightVisualsSystem.cs b/Content.Client/Toggleable/ToggleableLightVisualsSystem.cs
index 3507c786b9..a3d35ffeed 100644
--- a/Content.Client/Toggleable/ToggleableLightVisualsSystem.cs
+++ b/Content.Client/Toggleable/ToggleableLightVisualsSystem.cs
@@ -2,6 +2,7 @@ using Content.Client.Clothing;
using Content.Client.Items.Systems;
using Content.Shared.Clothing;
using Content.Shared.Hands;
+using Content.Shared.Inventory;
using Content.Shared.Item;
using Content.Shared.Toggleable;
using Robust.Client.GameObjects;
@@ -62,7 +63,16 @@ public sealed class ToggleableLightVisualsSystem : VisualizerSystem? layers = null;
+
+ // attempt to get species specific data
+ if (inventory.SpeciesId != null)
+ component.ClothingVisuals.TryGetValue($"{args.Slot}-{inventory.SpeciesId}", out layers);
+
+ // No species specific data. Try to default to generic data.
+ if (layers == null && !component.ClothingVisuals.TryGetValue(args.Slot, out layers))
return;
var modulate = AppearanceSystem.TryGetData(uid, ToggleableLightVisuals.Color, out var color, appearance);
diff --git a/Content.Client/UserInterface/Systems/CloseWindow/CloseRecentWindowUIController.cs b/Content.Client/UserInterface/Systems/CloseWindow/CloseRecentWindowUIController.cs
index 3517a001c2..0e648c25a0 100644
--- a/Content.Client/UserInterface/Systems/CloseWindow/CloseRecentWindowUIController.cs
+++ b/Content.Client/UserInterface/Systems/CloseWindow/CloseRecentWindowUIController.cs
@@ -74,7 +74,7 @@ public sealed class CloseRecentWindowUIController : UIController
/// internal recentlyInteractedWindows tracking.
///
///
- private void SetMostRecentlyInteractedWindow(BaseWindow window)
+ public void SetMostRecentlyInteractedWindow(BaseWindow window)
{
// Search through the list and see if already added.
// (This search is backwards since it's fairly common that the user is clicking the same
@@ -134,7 +134,6 @@ public sealed class CloseRecentWindowUIController : UIController
if (window.IsOpen)
return true;
- recentlyInteractedWindows.RemoveAt(i);
// continue going down the list, hoping to find a still-open window
}
diff --git a/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs b/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs
index 768a434d83..f0c8cfeb21 100644
--- a/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs
+++ b/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs
@@ -5,6 +5,7 @@ using Content.Client.Interaction;
using Content.Client.Storage;
using Content.Client.Storage.Systems;
using Content.Client.UserInterface.Systems.Hotbar.Widgets;
+using Content.Client.UserInterface.Systems.Info;
using Content.Client.UserInterface.Systems.Storage.Controls;
using Content.Client.Verbs.UI;
using Content.Shared.CCVar;
@@ -37,6 +38,7 @@ public sealed class StorageUIController : UIController, IOnSystemChanged()?.StorageContainer.AddChild(window);
+ _closeRecentWindowUIController.SetMostRecentlyInteractedWindow(window);
}
else
{
diff --git a/Content.IntegrationTests/Tests/CargoTest.cs b/Content.IntegrationTests/Tests/CargoTest.cs
index 8cb51e36c2..63015eb429 100644
--- a/Content.IntegrationTests/Tests/CargoTest.cs
+++ b/Content.IntegrationTests/Tests/CargoTest.cs
@@ -6,10 +6,8 @@ using Content.Server.Cargo.Systems;
using Content.Server.Nutrition.Components;
using Content.Server.Nutrition.EntitySystems;
using Content.Shared.Cargo.Prototypes;
-using Content.Shared.IdentityManagement;
using Content.Shared.Prototypes;
using Content.Shared.Stacks;
-using Content.Shared.Tag;
using Content.Shared.Whitelist;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
@@ -67,7 +65,7 @@ public sealed class CargoTest
var testMap = await pair.CreateTestMap();
var entManager = server.ResolveDependency();
- var mapManager = server.ResolveDependency();
+ var mapSystem = server.System();
var protoManager = server.ResolveDependency();
var cargo = entManager.System();
@@ -93,7 +91,7 @@ public sealed class CargoTest
}
});
- mapManager.DeleteMap(mapId);
+ mapSystem.DeleteMap(mapId);
});
await pair.CleanReturnAsync();
@@ -151,6 +149,7 @@ public sealed class CargoTest
var testMap = await pair.CreateTestMap();
var entManager = server.ResolveDependency();
+ var mapSystem = server.System();
var mapManager = server.ResolveDependency();
var protoManager = server.ResolveDependency();
var componentFactory = server.ResolveDependency();
@@ -207,7 +206,7 @@ public sealed class CargoTest
entManager.DeleteEntity(ent);
}
- mapManager.DeleteMap(mapId);
+ mapSystem.DeleteMap(mapId);
});
await pair.CleanReturnAsync();
diff --git a/Content.IntegrationTests/Tests/Hands/HandTests.cs b/Content.IntegrationTests/Tests/Hands/HandTests.cs
index 5e96015feb..b88f2dc9eb 100644
--- a/Content.IntegrationTests/Tests/Hands/HandTests.cs
+++ b/Content.IntegrationTests/Tests/Hands/HandTests.cs
@@ -6,7 +6,6 @@ using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
-using Robust.Shared.Map;
namespace Content.IntegrationTests.Tests.Hands;
@@ -38,7 +37,7 @@ public sealed class HandTests
var entMan = server.ResolveDependency();
var playerMan = server.ResolveDependency();
- var mapMan = server.ResolveDependency();
+ var mapSystem = server.System();
var sys = entMan.System();
var tSys = entMan.System();
@@ -69,7 +68,7 @@ public sealed class HandTests
await pair.RunTicksSync(5);
Assert.That(hands.ActiveHandEntity, Is.Null);
- await server.WaitPost(() => mapMan.DeleteMap(data.MapId));
+ await server.WaitPost(() => mapSystem.DeleteMap(data.MapId));
await pair.CleanReturnAsync();
}
@@ -87,7 +86,7 @@ public sealed class HandTests
var entMan = server.ResolveDependency();
var playerMan = server.ResolveDependency();
- var mapMan = server.ResolveDependency();
+ var mapSystem = server.System();
var sys = entMan.System();
var tSys = entMan.System();
var containerSystem = server.System();
@@ -134,7 +133,7 @@ public sealed class HandTests
Assert.That(hands.ActiveHandEntity, Is.Not.EqualTo(item));
Assert.That(containerSystem.IsInSameOrNoContainer((player, xform), (item, itemXform)));
- await server.WaitPost(() => mapMan.DeleteMap(map.MapId));
+ await server.WaitPost(() => mapSystem.DeleteMap(map.MapId));
await pair.CleanReturnAsync();
}
}
diff --git a/Content.IntegrationTests/Tests/HumanInventoryUniformSlotsTest.cs b/Content.IntegrationTests/Tests/HumanInventoryUniformSlotsTest.cs
index 4bba605f0a..929a231159 100644
--- a/Content.IntegrationTests/Tests/HumanInventoryUniformSlotsTest.cs
+++ b/Content.IntegrationTests/Tests/HumanInventoryUniformSlotsTest.cs
@@ -1,6 +1,5 @@
using Content.Shared.Inventory;
using Robust.Shared.GameObjects;
-using Robust.Shared.Map;
namespace Content.IntegrationTests.Tests
{
@@ -67,7 +66,7 @@ namespace Content.IntegrationTests.Tests
EntityUid pocketItem = default;
InventorySystem invSystem = default!;
- var mapMan = server.ResolveDependency();
+ var mapSystem = server.System();
var entityMan = server.ResolveDependency();
await server.WaitAssertion(() =>
@@ -129,7 +128,7 @@ namespace Content.IntegrationTests.Tests
Assert.That(!invSystem.TryGetSlotEntity(human, "pocket1", out _));
});
- mapMan.DeleteMap(testMap.MapId);
+ mapSystem.DeleteMap(testMap.MapId);
});
await pair.CleanReturnAsync();
diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs
index b3d684e01a..47a1f748eb 100644
--- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs
+++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs
@@ -260,7 +260,7 @@ public abstract partial class InteractionTest
[TearDown]
public async Task TearDownInternal()
{
- await Server.WaitPost(() => MapMan.DeleteMap(MapId));
+ await Server.WaitPost(() => MapSystem.DeleteMap(MapId));
await Pair.CleanReturnAsync();
await TearDown();
}
diff --git a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs
index ae09405220..e6422f0ec4 100644
--- a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs
+++ b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs
@@ -346,7 +346,7 @@ public sealed class MaterialArbitrageTest
}
});
- await server.WaitPost(() => mapManager.DeleteMap(testMap.MapId));
+ await server.WaitPost(() => mapSystem.DeleteMap(testMap.MapId));
await pair.CleanReturnAsync();
async Task GetSpawnedPrice(Dictionary ents)
diff --git a/Content.IntegrationTests/Tests/Materials/MaterialTests.cs b/Content.IntegrationTests/Tests/Materials/MaterialTests.cs
index 1caaf8a3ff..30800f358e 100644
--- a/Content.IntegrationTests/Tests/Materials/MaterialTests.cs
+++ b/Content.IntegrationTests/Tests/Materials/MaterialTests.cs
@@ -3,7 +3,6 @@ using Content.Server.Stack;
using Content.Shared.Stacks;
using Content.Shared.Materials;
using Robust.Shared.GameObjects;
-using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests.Materials
@@ -24,7 +23,7 @@ namespace Content.IntegrationTests.Tests.Materials
var server = pair.Server;
await server.WaitIdleAsync();
- var mapManager = server.ResolveDependency();
+ var mapSystem = server.System();
var prototypeManager = server.ResolveDependency();
var entityManager = server.ResolveDependency();
@@ -59,7 +58,7 @@ namespace Content.IntegrationTests.Tests.Materials
}
});
- mapManager.DeleteMap(testMap.MapId);
+ mapSystem.DeleteMap(testMap.MapId);
});
await pair.CleanReturnAsync();
diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs b/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs
index de7739b2ad..6f33188813 100644
--- a/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs
+++ b/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs
@@ -81,7 +81,7 @@ public sealed partial class MindTests
var testMap2 = await pair.CreateTestMap();
var entMan = server.ResolveDependency();
- var mapManager = server.ResolveDependency();
+ var mapSystem = server.System();
var playerMan = server.ResolveDependency();
var player = playerMan.Sessions.Single();
@@ -101,7 +101,7 @@ public sealed partial class MindTests
});
await pair.RunTicksSync(5);
- await server.WaitAssertion(() => mapManager.DeleteMap(testMap.MapId));
+ await server.WaitAssertion(() => mapSystem.DeleteMap(testMap.MapId));
await pair.RunTicksSync(5);
await server.WaitAssertion(() =>
diff --git a/Content.IntegrationTests/Tests/PostMapInitTest.cs b/Content.IntegrationTests/Tests/PostMapInitTest.cs
index 3cb064c8f0..1e7c173263 100644
--- a/Content.IntegrationTests/Tests/PostMapInitTest.cs
+++ b/Content.IntegrationTests/Tests/PostMapInitTest.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using Content.Server.Administration.Systems;
using Content.Server.GameTicking;
using Content.Server.Maps;
using Content.Server.Shuttles.Components;
@@ -42,6 +43,7 @@ namespace Content.IntegrationTests.Tests
//CrystallEdge Map replacement
//CrystallEdge Map replacement end
+ AdminTestArenaSystem.ArenaMapPath
};
private static readonly string[] DoNotMapWhitelist =
diff --git a/Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs b/Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs
index 3b2935258a..de89f16be7 100644
--- a/Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs
+++ b/Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs
@@ -2,7 +2,6 @@ using System.Linq;
using Content.Shared.Roles;
using Content.Server.Storage.EntitySystems;
using Robust.Shared.GameObjects;
-using Robust.Shared.Map;
using Robust.Shared.Collections;
namespace Content.IntegrationTests.Tests.Roles;
@@ -19,7 +18,7 @@ public sealed class StartingGearPrototypeStorageTest
var settings = new PoolSettings { Connected = true, Dirty = true };
await using var pair = await PoolManager.GetServerClient(settings);
var server = pair.Server;
- var mapManager = server.ResolveDependency();
+ var mapSystem = server.System();
var storageSystem = server.System();
var protos = server.ProtoMan
@@ -64,7 +63,7 @@ public sealed class StartingGearPrototypeStorageTest
}
}
- mapManager.DeleteMap(testMap.MapId);
+ mapSystem.DeleteMap(testMap.MapId);
});
await pair.CleanReturnAsync();
diff --git a/Content.Server/Abilities/Mime/MimePowersComponent.cs b/Content.Server/Abilities/Mime/MimePowersComponent.cs
index d56644ed19..24ffe5d023 100644
--- a/Content.Server/Abilities/Mime/MimePowersComponent.cs
+++ b/Content.Server/Abilities/Mime/MimePowersComponent.cs
@@ -55,5 +55,16 @@ namespace Content.Server.Abilities.Mime
[DataField]
public ProtoId VowBrokenAlert = "VowBroken";
+ ///
+ /// Does this component prevent the mime from writing on paper while their vow is active?
+ ///
+ [DataField]
+ public bool PreventWriting = false;
+
+ ///
+ /// What message is displayed when the mime fails to write?
+ ///
+ [DataField]
+ public LocId FailWriteMessage = "paper-component-illiterate-mime";
}
}
diff --git a/Content.Server/Abilities/Mime/MimePowersSystem.cs b/Content.Server/Abilities/Mime/MimePowersSystem.cs
index bd8cf7c176..3de356f608 100644
--- a/Content.Server/Abilities/Mime/MimePowersSystem.cs
+++ b/Content.Server/Abilities/Mime/MimePowersSystem.cs
@@ -5,6 +5,7 @@ using Content.Shared.Actions.Events;
using Content.Shared.Alert;
using Content.Shared.Coordinates.Helpers;
using Content.Shared.Maps;
+using Content.Shared.Paper;
using Content.Shared.Physics;
using Robust.Shared.Containers;
using Robust.Shared.Map;
@@ -55,6 +56,13 @@ namespace Content.Server.Abilities.Mime
private void OnComponentInit(EntityUid uid, MimePowersComponent component, ComponentInit args)
{
EnsureComp(uid);
+ if (component.PreventWriting)
+ {
+ EnsureComp(uid, out var illiterateComponent);
+ illiterateComponent.FailWriteMessage = component.FailWriteMessage;
+ Dirty(uid, illiterateComponent);
+ }
+
_alertsSystem.ShowAlert(uid, component.VowAlert);
_actionsSystem.AddAction(uid, ref component.InvisibleWallActionEntity, component.InvisibleWallAction, uid);
}
@@ -123,6 +131,8 @@ namespace Content.Server.Abilities.Mime
mimePowers.VowBroken = true;
mimePowers.VowRepentTime = _timing.CurTime + mimePowers.VowCooldown;
RemComp(uid);
+ if (mimePowers.PreventWriting)
+ RemComp(uid);
_alertsSystem.ClearAlert(uid, mimePowers.VowAlert);
_alertsSystem.ShowAlert(uid, mimePowers.VowBrokenAlert);
_actionsSystem.RemoveAction(uid, mimePowers.InvisibleWallActionEntity);
@@ -146,6 +156,13 @@ namespace Content.Server.Abilities.Mime
mimePowers.ReadyToRepent = false;
mimePowers.VowBroken = false;
AddComp(uid);
+ if (mimePowers.PreventWriting)
+ {
+ EnsureComp(uid, out var illiterateComponent);
+ illiterateComponent.FailWriteMessage = mimePowers.FailWriteMessage;
+ Dirty(uid, illiterateComponent);
+ }
+
_alertsSystem.ClearAlert(uid, mimePowers.VowBrokenAlert);
_alertsSystem.ShowAlert(uid, mimePowers.VowAlert);
_actionsSystem.AddAction(uid, ref mimePowers.InvisibleWallActionEntity, mimePowers.InvisibleWallAction, uid);
diff --git a/Content.Server/Administration/Commands/SetMindCommand.cs b/Content.Server/Administration/Commands/SetMindCommand.cs
index a7b6849423..423c30ae37 100644
--- a/Content.Server/Administration/Commands/SetMindCommand.cs
+++ b/Content.Server/Administration/Commands/SetMindCommand.cs
@@ -74,5 +74,15 @@ namespace Content.Server.Administration.Commands
mindSystem.TransferTo(mind, eUid, ghostOverride);
}
+
+ public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
+ {
+ if (args.Length == 2)
+ {
+ return CompletionResult.FromHintOptions(CompletionHelper.SessionNames(), Loc.GetString("cmd-mind-command-hint"));
+ }
+
+ return CompletionResult.Empty;
+ }
}
}
diff --git a/Content.Server/Administration/Systems/AdminTestArenaSystem.cs b/Content.Server/Administration/Systems/AdminTestArenaSystem.cs
index 12bf0eba64..f45af60f6d 100644
--- a/Content.Server/Administration/Systems/AdminTestArenaSystem.cs
+++ b/Content.Server/Administration/Systems/AdminTestArenaSystem.cs
@@ -12,6 +12,7 @@ public sealed class AdminTestArenaSystem : EntitySystem
{
[Dependency] private readonly MapLoaderSystem _loader = default!;
[Dependency] private readonly MetaDataSystem _metaDataSystem = default!;
+ [Dependency] private readonly SharedMapSystem _maps = default!;
public const string ArenaMapPath = "/Maps/Test/admin_test_arena.yml";
@@ -33,17 +34,20 @@ public sealed class AdminTestArenaSystem : EntitySystem
}
var path = new ResPath(ArenaMapPath);
- if (!_loader.TryLoadMap(path, out var map, out var grids))
+ var mapUid = _maps.CreateMap(out var mapId);
+
+ if (!_loader.TryLoadGrid(mapId, path, out var grid))
+ {
+ QueueDel(mapUid);
throw new Exception($"Failed to load admin arena");
+ }
- ArenaMap[admin.UserId] = map.Value.Owner;
- _metaDataSystem.SetEntityName(map.Value.Owner, $"ATAM-{admin.Name}");
+ ArenaMap[admin.UserId] = mapUid;
+ _metaDataSystem.SetEntityName(mapUid, $"ATAM-{admin.Name}");
- var grid = grids.FirstOrNull();
- ArenaGrid[admin.UserId] = grid?.Owner;
- if (grid != null)
- _metaDataSystem.SetEntityName(grid.Value.Owner, $"ATAG-{admin.Name}");
+ ArenaGrid[admin.UserId] = grid.Value.Owner;
+ _metaDataSystem.SetEntityName(grid.Value.Owner, $"ATAG-{admin.Name}");
- return (map.Value.Owner, grid?.Owner);
+ return (mapUid, grid.Value.Owner);
}
}
diff --git a/Content.Server/Administration/Systems/BwoinkSystem.cs b/Content.Server/Administration/Systems/BwoinkSystem.cs
index b3039e92ff..2df9aa9fcc 100644
--- a/Content.Server/Administration/Systems/BwoinkSystem.cs
+++ b/Content.Server/Administration/Systems/BwoinkSystem.cs
@@ -759,6 +759,7 @@ namespace Content.Server.Administration.Systems
_gameTicker.RoundDuration().ToString("hh\\:mm\\:ss"),
_gameTicker.RunLevel,
playedSound: playSound,
+ adminOnly: message.AdminOnly,
noReceivers: nonAfkAdmins.Count == 0
);
_messageQueues[msg.UserId].Enqueue(GenerateAHelpMessage(messageParams));
@@ -790,7 +791,7 @@ namespace Content.Server.Administration.Systems
.ToList();
}
- private static DiscordRelayedData GenerateAHelpMessage(AHelpMessageParams parameters)
+ private DiscordRelayedData GenerateAHelpMessage(AHelpMessageParams parameters)
{
var stringbuilder = new StringBuilder();
@@ -806,7 +807,7 @@ namespace Content.Server.Administration.Systems
if (parameters.RoundTime != string.Empty && parameters.RoundState == GameRunLevel.InRound)
stringbuilder.Append($" **{parameters.RoundTime}**");
if (!parameters.PlayedSound)
- stringbuilder.Append(" **(S)**");
+ stringbuilder.Append($" **{(parameters.AdminOnly ? Loc.GetString("bwoink-message-admin-only") : Loc.GetString("bwoink-message-silent"))}**");
if (parameters.Icon == null)
stringbuilder.Append($" **{parameters.Username}:** ");
else
@@ -869,6 +870,7 @@ namespace Content.Server.Administration.Systems
public string RoundTime { get; set; }
public GameRunLevel RoundState { get; set; }
public bool PlayedSound { get; set; }
+ public readonly bool AdminOnly;
public bool NoReceivers { get; set; }
public string? Icon { get; set; }
@@ -879,6 +881,7 @@ namespace Content.Server.Administration.Systems
string roundTime,
GameRunLevel roundState,
bool playedSound,
+ bool adminOnly = false,
bool noReceivers = false,
string? icon = null)
{
@@ -888,6 +891,7 @@ namespace Content.Server.Administration.Systems
RoundTime = roundTime;
RoundState = roundState;
PlayedSound = playedSound;
+ AdminOnly = adminOnly;
NoReceivers = noReceivers;
Icon = icon;
}
diff --git a/Content.Server/AlertLevel/AlertLevelPrototype.cs b/Content.Server/AlertLevel/AlertLevelPrototype.cs
index c6740c16cc..ccf6ff912c 100644
--- a/Content.Server/AlertLevel/AlertLevelPrototype.cs
+++ b/Content.Server/AlertLevel/AlertLevelPrototype.cs
@@ -6,7 +6,7 @@ namespace Content.Server.AlertLevel;
[Prototype("alertLevels")]
public sealed partial class AlertLevelPrototype : IPrototype
{
- [IdDataField] public string ID { get; } = default!;
+ [IdDataField] public string ID { get; private set; } = default!;
///
/// Dictionary of alert levels. Keyed by string - the string key is the most important
diff --git a/Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs b/Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs
index 9a551acc49..4b5e0679f5 100644
--- a/Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs
+++ b/Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs
@@ -28,7 +28,7 @@ public sealed partial class AntagRandomObjectivesComponent : Component
/// Difficulty is checked over all sets, but each set has its own probability and pick count.
///
[DataRecord]
-public record struct AntagObjectiveSet()
+public partial record struct AntagObjectiveSet()
{
///
/// The grouping used by the objective system to pick random objectives.
diff --git a/Content.Server/Bible/BibleSystem.cs b/Content.Server/Bible/BibleSystem.cs
index 76efe3290b..d9033a0f94 100644
--- a/Content.Server/Bible/BibleSystem.cs
+++ b/Content.Server/Bible/BibleSystem.cs
@@ -84,7 +84,7 @@ namespace Content.Server.Bible
}
summonableComp.AlreadySummoned = false;
_popupSystem.PopupEntity(Loc.GetString("bible-summon-respawn-ready", ("book", uid)), uid, PopupType.Medium);
- _audio.PlayPvs("/Audio/Effects/radpulse9.ogg", uid, AudioParams.Default.WithVolume(-4f));
+ _audio.PlayPvs(summonableComp.SummonSound, uid);
// Clean up the accumulator and respawn tracking component
summonableComp.Accumulator = 0;
_remQueue.Enqueue(uid);
@@ -126,7 +126,7 @@ namespace Content.Server.Bible
var selfFailMessage = Loc.GetString(component.LocPrefix + "-heal-fail-self", ("target", Identity.Entity(args.Target.Value, EntityManager)), ("bible", uid));
_popupSystem.PopupEntity(selfFailMessage, args.User, args.User, PopupType.MediumCaution);
- _audio.PlayPvs("/Audio/Effects/hit_kick.ogg", args.User);
+ _audio.PlayPvs(component.BibleHitSound, args.User);
_damageableSystem.TryChangeDamage(args.Target.Value, component.DamageOnFail, true, origin: uid);
_delay.TryResetDelay((uid, useDelay));
return;
diff --git a/Content.Server/Bible/Components/BibleComponent.cs b/Content.Server/Bible/Components/BibleComponent.cs
index b7dc3db8e3..7f4af569e5 100644
--- a/Content.Server/Bible/Components/BibleComponent.cs
+++ b/Content.Server/Bible/Components/BibleComponent.cs
@@ -1,11 +1,23 @@
using Content.Shared.Damage;
using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
namespace Content.Server.Bible.Components
{
[RegisterComponent]
public sealed partial class BibleComponent : Component
{
+ ///
+ /// Default sound when bible hits somebody.
+ ///
+ private static readonly ProtoId DefaultBibleHit = new("BibleHit");
+
+ ///
+ /// Sound to play when bible hits somebody.
+ ///
+ [DataField]
+ public SoundSpecifier BibleHitSound = new SoundCollectionSpecifier(DefaultBibleHit, AudioParams.Default.WithVolume(-4f));
+
///
/// Damage that will be healed on a success
///
diff --git a/Content.Server/Bible/Components/SummonableComponent.cs b/Content.Server/Bible/Components/SummonableComponent.cs
index 62f536d5d6..fa180c932e 100644
--- a/Content.Server/Bible/Components/SummonableComponent.cs
+++ b/Content.Server/Bible/Components/SummonableComponent.cs
@@ -1,3 +1,4 @@
+using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
@@ -9,6 +10,17 @@ namespace Content.Server.Bible.Components
[RegisterComponent]
public sealed partial class SummonableComponent : Component
{
+ ///
+ /// Default sound to play when entity is summoned.
+ ///
+ private static readonly ProtoId DefaultSummonSound = new("Summon");
+
+ ///
+ /// Sound to play when entity is summoned.
+ ///
+ [DataField]
+ public SoundSpecifier SummonSound = new SoundCollectionSpecifier(DefaultSummonSound, AudioParams.Default.WithVolume(-4f));
+
///
/// Used for a special item only the Chaplain can summon. Usually a mob, but supports regular items too.
///
diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs
index 5608338f22..1eb22241cd 100644
--- a/Content.Server/Botany/SeedPrototype.cs
+++ b/Content.Server/Botany/SeedPrototype.cs
@@ -14,7 +14,7 @@ namespace Content.Server.Botany;
[Prototype("seed")]
public sealed partial class SeedPrototype : SeedData, IPrototype
{
- [IdDataField] public string ID { get; private init; } = default!;
+ [IdDataField] public string ID { get; private set; } = default!;
}
public enum HarvestType : byte
diff --git a/Content.Server/CartridgeLoader/CartridgeLoaderSystem.cs b/Content.Server/CartridgeLoader/CartridgeLoaderSystem.cs
index 98df7e2c50..8637e44e62 100644
--- a/Content.Server/CartridgeLoader/CartridgeLoaderSystem.cs
+++ b/Content.Server/CartridgeLoader/CartridgeLoaderSystem.cs
@@ -427,6 +427,7 @@ public sealed class CartridgeLoaderSystem : SharedCartridgeLoaderSystem
private void OnUiMessage(EntityUid uid, CartridgeLoaderComponent component, CartridgeUiMessage args)
{
var cartridgeEvent = args.MessageEvent;
+ cartridgeEvent.User = args.Actor;
cartridgeEvent.LoaderUid = GetNetEntity(uid);
cartridgeEvent.Actor = args.Actor;
diff --git a/Content.Server/CartridgeLoader/Cartridges/LogProbeCartridgeComponent.cs b/Content.Server/CartridgeLoader/Cartridges/LogProbeCartridgeComponent.cs
index cfa92dd67f..659433de36 100644
--- a/Content.Server/CartridgeLoader/Cartridges/LogProbeCartridgeComponent.cs
+++ b/Content.Server/CartridgeLoader/Cartridges/LogProbeCartridgeComponent.cs
@@ -1,12 +1,21 @@
using Content.Shared.CartridgeLoader.Cartridges;
+using Content.Shared.Paper;
using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server.CartridgeLoader.Cartridges;
-[RegisterComponent]
-[Access(typeof(LogProbeCartridgeSystem))]
+[RegisterComponent, Access(typeof(LogProbeCartridgeSystem))]
+[AutoGenerateComponentPause]
public sealed partial class LogProbeCartridgeComponent : Component
{
+ ///
+ /// The name of the scanned entity, sent to clients when they open the UI.
+ ///
+ [DataField]
+ public string EntityName = string.Empty;
+
///
/// The list of pulled access logs
///
@@ -18,4 +27,25 @@ public sealed partial class LogProbeCartridgeComponent : Component
///
[DataField, ViewVariables(VVAccess.ReadWrite)]
public SoundSpecifier SoundScan = new SoundPathSpecifier("/Audio/Machines/scan_finish.ogg");
+
+ ///
+ /// Paper to spawn when printing logs.
+ ///
+ [DataField]
+ public EntProtoId PaperPrototype = "PaperAccessLogs";
+
+ [DataField]
+ public SoundSpecifier PrintSound = new SoundPathSpecifier("/Audio/Machines/diagnoser_printing.ogg");
+
+ ///
+ /// How long you have to wait before printing logs again.
+ ///
+ [DataField]
+ public TimeSpan PrintCooldown = TimeSpan.FromSeconds(5);
+
+ ///
+ /// When anyone is allowed to spawn another printout.
+ ///
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
+ public TimeSpan NextPrintAllowed = TimeSpan.Zero;
}
diff --git a/Content.Server/CartridgeLoader/Cartridges/LogProbeCartridgeSystem.cs b/Content.Server/CartridgeLoader/Cartridges/LogProbeCartridgeSystem.cs
index f5ccea9590..ac5c0baa54 100644
--- a/Content.Server/CartridgeLoader/Cartridges/LogProbeCartridgeSystem.cs
+++ b/Content.Server/CartridgeLoader/Cartridges/LogProbeCartridgeSystem.cs
@@ -1,25 +1,40 @@
using Content.Shared.Access.Components;
+using Content.Shared.Administration.Logs;
using Content.Shared.Audio;
using Content.Shared.CartridgeLoader;
using Content.Shared.CartridgeLoader.Cartridges;
+using Content.Shared.Database;
+using Content.Shared.Hands.EntitySystems;
+using Content.Shared.Labels.EntitySystems;
+using Content.Shared.Paper;
using Content.Shared.Popups;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Random;
+using Robust.Shared.Timing;
+using System.Text;
namespace Content.Server.CartridgeLoader.Cartridges;
public sealed class LogProbeCartridgeSystem : EntitySystem
{
+ [Dependency] private readonly CartridgeLoaderSystem _cartridge = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IRobustRandom _random = default!;
- [Dependency] private readonly CartridgeLoaderSystem? _cartridgeLoaderSystem = default!;
- [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
- [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
+ [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly SharedHandsSystem _hands = default!;
+ [Dependency] private readonly SharedLabelSystem _label = default!;
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly PaperSystem _paper = default!;
public override void Initialize()
{
base.Initialize();
+
SubscribeLocalEvent(OnUiReady);
SubscribeLocalEvent(AfterInteract);
+ SubscribeLocalEvent(OnMessage);
}
///
@@ -37,9 +52,10 @@ public sealed class LogProbeCartridgeSystem : EntitySystem
return;
//Play scanning sound with slightly randomized pitch
- _audioSystem.PlayEntity(ent.Comp.SoundScan, args.InteractEvent.User, target, AudioHelpers.WithVariation(0.25f, _random));
- _popupSystem.PopupCursor(Loc.GetString("log-probe-scan", ("device", target)), args.InteractEvent.User);
+ _audio.PlayEntity(ent.Comp.SoundScan, args.InteractEvent.User, target, AudioHelpers.WithVariation(0.25f, _random));
+ _popup.PopupCursor(Loc.GetString("log-probe-scan", ("device", target)), args.InteractEvent.User);
+ ent.Comp.EntityName = Name(target);
ent.Comp.PulledAccessLogs.Clear();
foreach (var accessRecord in accessReaderComponent.AccessLog)
@@ -52,6 +68,9 @@ public sealed class LogProbeCartridgeSystem : EntitySystem
ent.Comp.PulledAccessLogs.Add(log);
}
+ // Reverse the list so the oldest is at the bottom
+ ent.Comp.PulledAccessLogs.Reverse();
+
UpdateUiState(ent, args.Loader);
}
@@ -63,9 +82,49 @@ public sealed class LogProbeCartridgeSystem : EntitySystem
UpdateUiState(ent, args.Loader);
}
+ private void OnMessage(Entity ent, ref CartridgeMessageEvent args)
+ {
+ if (args is LogProbePrintMessage cast)
+ PrintLogs(ent, cast.User);
+ }
+
+ private void PrintLogs(Entity ent, EntityUid user)
+ {
+ if (string.IsNullOrEmpty(ent.Comp.EntityName))
+ return;
+
+ if (_timing.CurTime < ent.Comp.NextPrintAllowed)
+ return;
+
+ ent.Comp.NextPrintAllowed = _timing.CurTime + ent.Comp.PrintCooldown;
+
+ var paper = Spawn(ent.Comp.PaperPrototype, _transform.GetMapCoordinates(user));
+ _label.Label(paper, ent.Comp.EntityName); // label it for easy identification
+
+ _audio.PlayEntity(ent.Comp.PrintSound, user, paper);
+ _hands.PickupOrDrop(user, paper, checkActionBlocker: false);
+
+ // generate the actual printout text
+ var builder = new StringBuilder();
+ builder.AppendLine(Loc.GetString("log-probe-printout-device", ("name", ent.Comp.EntityName)));
+ builder.AppendLine(Loc.GetString("log-probe-printout-header"));
+ var number = 1;
+ foreach (var log in ent.Comp.PulledAccessLogs)
+ {
+ var time = TimeSpan.FromSeconds(Math.Truncate(log.Time.TotalSeconds)).ToString();
+ builder.AppendLine(Loc.GetString("log-probe-printout-entry", ("number", number), ("time", time), ("accessor", log.Accessor)));
+ number++;
+ }
+
+ var paperComp = Comp(paper);
+ _paper.SetContent((paper, paperComp), builder.ToString());
+
+ _adminLogger.Add(LogType.EntitySpawn, LogImpact.Low, $"{ToPrettyString(user):user} printed out LogProbe logs ({paper}) of {ent.Comp.EntityName}");
+ }
+
private void UpdateUiState(Entity ent, EntityUid loaderUid)
{
- var state = new LogProbeUiState(ent.Comp.PulledAccessLogs);
- _cartridgeLoaderSystem?.UpdateCartridgeUiState(loaderUid, state);
+ var state = new LogProbeUiState(ent.Comp.EntityName, ent.Comp.PulledAccessLogs);
+ _cartridge.UpdateCartridgeUiState(loaderUid, state);
}
}
diff --git a/Content.Server/Chemistry/Components/ChemMasterComponent.cs b/Content.Server/Chemistry/Components/ChemMasterComponent.cs
index 9578755ba7..0309d07ed9 100644
--- a/Content.Server/Chemistry/Components/ChemMasterComponent.cs
+++ b/Content.Server/Chemistry/Components/ChemMasterComponent.cs
@@ -18,6 +18,9 @@ namespace Content.Server.Chemistry.Components
[DataField("mode"), ViewVariables(VVAccess.ReadWrite)]
public ChemMasterMode Mode = ChemMasterMode.Transfer;
+ [DataField]
+ public ChemMasterSortingType SortingType = ChemMasterSortingType.None;
+
[DataField("pillDosageLimit", required: true), ViewVariables(VVAccess.ReadWrite)]
public uint PillDosageLimit;
diff --git a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs
index c733c7215e..dd97bfa8f6 100644
--- a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs
+++ b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs
@@ -53,6 +53,7 @@ namespace Content.Server.Chemistry.EntitySystems
SubscribeLocalEvent(SubscribeUpdateUiState);
SubscribeLocalEvent(OnSetModeMessage);
+ SubscribeLocalEvent(OnCycleSortingTypeMessage);
SubscribeLocalEvent(OnSetPillTypeMessage);
SubscribeLocalEvent(OnReagentButtonMessage);
SubscribeLocalEvent(OnCreatePillsMessage);
@@ -76,7 +77,7 @@ namespace Content.Server.Chemistry.EntitySystems
var bufferCurrentVolume = bufferSolution.Volume;
var state = new ChemMasterBoundUserInterfaceState(
- chemMaster.Mode, BuildInputContainerInfo(inputContainer), BuildOutputContainerInfo(outputContainer),
+ chemMaster.Mode, chemMaster.SortingType, BuildInputContainerInfo(inputContainer), BuildOutputContainerInfo(outputContainer),
bufferReagents, bufferCurrentVolume, chemMaster.PillType, chemMaster.PillDosageLimit, updateLabel);
_userInterfaceSystem.SetUiState(owner, ChemMasterUiKey.Key, state);
@@ -93,6 +94,15 @@ namespace Content.Server.Chemistry.EntitySystems
ClickSound(chemMaster);
}
+ private void OnCycleSortingTypeMessage(Entity chemMaster, ref ChemMasterSortingTypeCycleMessage message)
+ {
+ chemMaster.Comp.SortingType++;
+ if (chemMaster.Comp.SortingType > ChemMasterSortingType.Latest)
+ chemMaster.Comp.SortingType = ChemMasterSortingType.None;
+ UpdateUiState(chemMaster);
+ ClickSound(chemMaster);
+ }
+
private void OnSetPillTypeMessage(Entity chemMaster, ref ChemMasterSetPillTypeMessage message)
{
// Ensure valid pill type. There are 20 pills selectable, 0-19.
diff --git a/Content.Server/Connection/Whitelist/WhitelistPrototype.cs b/Content.Server/Connection/Whitelist/WhitelistPrototype.cs
index 2b8b9babbc..eccf364a28 100644
--- a/Content.Server/Connection/Whitelist/WhitelistPrototype.cs
+++ b/Content.Server/Connection/Whitelist/WhitelistPrototype.cs
@@ -18,25 +18,25 @@ namespace Content.Server.Connection.Whitelist;
/// If the condition doesn't match, the next condition is checked.
///
[Prototype("playerConnectionWhitelist")]
-public sealed class PlayerConnectionWhitelistPrototype : IPrototype
+public sealed partial class PlayerConnectionWhitelistPrototype : IPrototype
{
[IdDataField]
- public string ID { get; } = default!;
+ public string ID { get; private set; } = default!;
///
/// Minimum number of players required for this whitelist to be active.
/// If there are less players than this, the whitelist will be ignored and the next one in the list will be used.
///
[DataField]
- public int MinimumPlayers { get; } = 0;
+ public int MinimumPlayers = 0;
///
/// Maximum number of players allowed for this whitelist to be active.
/// If there are more players than this, the whitelist will be ignored and the next one in the list will be used.
///
[DataField]
- public int MaximumPlayers { get; } = int.MaxValue;
+ public int MaximumPlayers = int.MaxValue;
[DataField]
- public WhitelistCondition[] Conditions { get; } = default!;
+ public WhitelistCondition[] Conditions = default!;
}
diff --git a/Content.Server/Containers/ThrowInsertContainerSystem.cs b/Content.Server/Containers/ThrowInsertContainerSystem.cs
index cbaec7f5c6..09176c423c 100644
--- a/Content.Server/Containers/ThrowInsertContainerSystem.cs
+++ b/Content.Server/Containers/ThrowInsertContainerSystem.cs
@@ -30,6 +30,12 @@ public sealed class ThrowInsertContainerSystem : EntitySystem
if (!_containerSystem.CanInsert(args.Thrown, container))
return;
+ var beforeThrowArgs = new BeforeThrowInsertEvent(args.Thrown);
+ RaiseLocalEvent(ent, ref beforeThrowArgs);
+
+ if (beforeThrowArgs.Cancelled)
+ return;
+
if (_random.Prob(ent.Comp.Probability))
{
_audio.PlayPvs(ent.Comp.MissSound, ent);
@@ -46,3 +52,10 @@ public sealed class ThrowInsertContainerSystem : EntitySystem
_adminLogger.Add(LogType.Landed, LogImpact.Low, $"{ToPrettyString(args.Thrown)} thrown by {ToPrettyString(args.Component.Thrower.Value):player} landed in {ToPrettyString(ent)}");
}
}
+
+///
+/// Sent before the insertion is made.
+/// Allows preventing the insertion if any system on the entity should need to.
+///
+[ByRefEvent]
+public record struct BeforeThrowInsertEvent(EntityUid ThrownEntity, bool Cancelled = false);
diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs
index 2cf88efc6e..136ac2c440 100644
--- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs
+++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs
@@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Administration.Logs;
using Content.Server.Atmos.EntitySystems;
+using Content.Server.Containers;
using Content.Server.Disposal.Tube;
using Content.Server.Disposal.Tube.Components;
using Content.Server.Disposal.Unit.Components;
@@ -85,6 +86,8 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
SubscribeLocalEvent(OnDoAfter);
+ SubscribeLocalEvent(OnThrowInsert);
+
SubscribeLocalEvent(OnUiButtonPressed);
}
@@ -195,6 +198,12 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
args.Handled = true;
}
+ private void OnThrowInsert(Entity ent, ref BeforeThrowInsertEvent args)
+ {
+ if (!CanInsert(ent, ent, args.ThrownEntity))
+ args.Cancelled = true;
+ }
+
public override void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, SharedDisposalUnitComponent? disposal = null)
{
if (!ResolveDisposals(uid, ref disposal))
diff --git a/Content.Server/Forensics/Components/FingerprintComponent.cs b/Content.Server/Forensics/Components/FingerprintComponent.cs
deleted file mode 100644
index 97d138119e..0000000000
--- a/Content.Server/Forensics/Components/FingerprintComponent.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace Content.Server.Forensics
-{
- ///
- /// This component is for mobs that leave fingerprints.
- ///
- [RegisterComponent]
- public sealed partial class FingerprintComponent : Component
- {
- [DataField("fingerprint"), ViewVariables(VVAccess.ReadWrite)]
- public string? Fingerprint;
- }
-}
diff --git a/Content.Server/Forensics/Components/FingerprintMaskComponent.cs b/Content.Server/Forensics/Components/FingerprintMaskComponent.cs
deleted file mode 100644
index 7371097761..0000000000
--- a/Content.Server/Forensics/Components/FingerprintMaskComponent.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Content.Server.Forensics
-{
- ///
- /// This component stops the entity from leaving finger prints,
- /// usually so fibres can be left instead.
- ///
- [RegisterComponent]
- public sealed partial class FingerprintMaskComponent : Component
- {}
-}
diff --git a/Content.Server/Forensics/Systems/ForensicPadSystem.cs b/Content.Server/Forensics/Systems/ForensicPadSystem.cs
index 71bbcfb44a..846d72a9fa 100644
--- a/Content.Server/Forensics/Systems/ForensicPadSystem.cs
+++ b/Content.Server/Forensics/Systems/ForensicPadSystem.cs
@@ -3,6 +3,7 @@ using Content.Server.Popups;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Shared.Forensics;
+using Content.Shared.Forensics.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Inventory;
diff --git a/Content.Server/Forensics/Systems/ForensicsSystem.cs b/Content.Server/Forensics/Systems/ForensicsSystem.cs
index c1595dd0c1..f811bede7b 100644
--- a/Content.Server/Forensics/Systems/ForensicsSystem.cs
+++ b/Content.Server/Forensics/Systems/ForensicsSystem.cs
@@ -63,9 +63,10 @@ namespace Content.Server.Forensics
ApplyEvidence(uid, args.Other);
}
- private void OnFingerprintInit(EntityUid uid, FingerprintComponent component, MapInitEvent args)
+ private void OnFingerprintInit(Entity ent, ref MapInitEvent args)
{
- component.Fingerprint = GenerateFingerprint();
+ ent.Comp.Fingerprint = GenerateFingerprint();
+ Dirty(ent);
}
private void OnDNAInit(EntityUid uid, DnaComponent component, MapInitEvent args)
diff --git a/Content.Server/GameTicking/Rules/SurvivorRuleSystem.cs b/Content.Server/GameTicking/Rules/SurvivorRuleSystem.cs
index 00f652b6c6..81ad2b1be7 100644
--- a/Content.Server/GameTicking/Rules/SurvivorRuleSystem.cs
+++ b/Content.Server/GameTicking/Rules/SurvivorRuleSystem.cs
@@ -1,4 +1,4 @@
-using Content.Server.Antag;
+using Content.Server.Antag;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.Mind;
using Content.Server.Roles;
diff --git a/Content.Server/Ghost/Roles/Raffles/GhostRoleRaffleDeciderPrototype.cs b/Content.Server/Ghost/Roles/Raffles/GhostRoleRaffleDeciderPrototype.cs
index b2ebf6ca5f..e7b0c8326a 100644
--- a/Content.Server/Ghost/Roles/Raffles/GhostRoleRaffleDeciderPrototype.cs
+++ b/Content.Server/Ghost/Roles/Raffles/GhostRoleRaffleDeciderPrototype.cs
@@ -6,7 +6,7 @@ namespace Content.Server.Ghost.Roles.Raffles;
/// Allows getting a as prototype.
///
[Prototype("ghostRoleRaffleDecider")]
-public sealed class GhostRoleRaffleDeciderPrototype : IPrototype
+public sealed partial class GhostRoleRaffleDeciderPrototype : IPrototype
{
///
[IdDataField]
diff --git a/Content.Server/Guardian/GuardianSystem.cs b/Content.Server/Guardian/GuardianSystem.cs
index 7a1b875756..4f80e2cd6c 100644
--- a/Content.Server/Guardian/GuardianSystem.cs
+++ b/Content.Server/Guardian/GuardianSystem.cs
@@ -269,6 +269,7 @@ namespace Content.Server.Guardian
component.Host,
args.DamageDelta * component.DamageShare,
origin: args.Origin,
+ ignoreResistances: true,
interruptsDoAfters: false);
_popupSystem.PopupEntity(Loc.GetString("guardian-entity-taking-damage"), component.Host.Value, component.Host.Value);
diff --git a/Content.Server/Implants/ImplanterSystem.cs b/Content.Server/Implants/ImplanterSystem.cs
index 5efd5dc6fb..5023b1b3e4 100644
--- a/Content.Server/Implants/ImplanterSystem.cs
+++ b/Content.Server/Implants/ImplanterSystem.cs
@@ -68,8 +68,6 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem
args.Handled = true;
}
-
-
///
/// Attempt to implant someone else.
///
diff --git a/Content.Server/Kitchen/Components/ActivelyMicrowavedComponent.cs b/Content.Server/Kitchen/Components/ActivelyMicrowavedComponent.cs
index 54a7edd745..c7fca5a47e 100644
--- a/Content.Server/Kitchen/Components/ActivelyMicrowavedComponent.cs
+++ b/Content.Server/Kitchen/Components/ActivelyMicrowavedComponent.cs
@@ -1,5 +1,3 @@
-using Content.Shared.Kitchen;
-
namespace Content.Server.Kitchen.Components;
///
@@ -8,4 +6,9 @@ namespace Content.Server.Kitchen.Components;
[RegisterComponent]
public sealed partial class ActivelyMicrowavedComponent : Component
{
+ ///
+ /// The microwave this entity is actively being microwaved by.
+ ///
+ [DataField]
+ public EntityUid? Microwave;
}
diff --git a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs
index 1ac02ac882..7f0778cbdd 100644
--- a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs
+++ b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs
@@ -14,6 +14,7 @@ using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.Chemistry.Reaction;
using Content.Shared.Construction.EntitySystems;
using Content.Shared.Database;
using Content.Shared.Destructible;
@@ -101,6 +102,7 @@ namespace Content.Server.Kitchen.EntitySystems
SubscribeLocalEvent(OnActiveMicrowaveRemove);
SubscribeLocalEvent(OnConstructionTemp);
+ SubscribeLocalEvent>(OnReactionAttempt);
SubscribeLocalEvent(OnGetSecretRecipes);
}
@@ -126,7 +128,8 @@ namespace Content.Server.Kitchen.EntitySystems
private void OnActiveMicrowaveInsert(Entity ent, ref EntInsertedIntoContainerMessage args)
{
- AddComp(args.Entity);
+ var microwavedComp = AddComp(args.Entity);
+ microwavedComp.Microwave = ent.Owner;
}
private void OnActiveMicrowaveRemove(Entity ent, ref EntRemovedFromContainerMessage args)
@@ -134,10 +137,33 @@ namespace Content.Server.Kitchen.EntitySystems
EntityManager.RemoveComponentDeferred(args.Entity);
}
+ // Stop items from transforming through constructiongraphs while being microwaved.
+ // They might be reserved for a microwave recipe.
private void OnConstructionTemp(Entity ent, ref OnConstructionTemperatureEvent args)
{
args.Result = HandleResult.False;
- return;
+ }
+
+ // Stop reagents from reacting if they are currently reserved for a microwave recipe.
+ // For example Egg would cook into EggCooked, causing it to not being removed once we are done microwaving.
+ private void OnReactionAttempt(Entity ent, ref SolutionRelayEvent args)
+ {
+ if (!TryComp(ent.Comp.Microwave, out var activeMicrowaveComp))
+ return;
+
+ if (activeMicrowaveComp.PortionedRecipe.Item1 == null) // no recipe selected
+ return;
+
+ var recipeReagents = activeMicrowaveComp.PortionedRecipe.Item1.IngredientsReagents.Keys;
+
+ foreach (var reagent in recipeReagents)
+ {
+ if (args.Event.Reaction.Reactants.ContainsKey(reagent))
+ {
+ args.Event.Cancelled = true;
+ return;
+ }
+ }
}
///
@@ -176,33 +202,29 @@ namespace Content.Server.Kitchen.EntitySystems
// this is spaghetti ngl
foreach (var item in component.Storage.ContainedEntities)
{
- if (!TryComp(item, out var solMan))
+ // use the same reagents as when we selected the recipe
+ if (!_solutionContainer.TryGetDrainableSolution(item, out var solutionEntity, out var solution))
continue;
- // go over every solution
- foreach (var (_, soln) in _solutionContainer.EnumerateSolutions((item, solMan)))
+ foreach (var (reagent, _) in recipe.IngredientsReagents)
{
- var solution = soln.Comp.Solution;
- foreach (var (reagent, _) in recipe.IngredientsReagents)
+ // removed everything
+ if (!totalReagentsToRemove.ContainsKey(reagent))
+ continue;
+
+ var quant = solution.GetTotalPrototypeQuantity(reagent);
+
+ if (quant >= totalReagentsToRemove[reagent])
{
- // removed everything
- if (!totalReagentsToRemove.ContainsKey(reagent))
- continue;
-
- var quant = solution.GetTotalPrototypeQuantity(reagent);
-
- if (quant >= totalReagentsToRemove[reagent])
- {
- quant = totalReagentsToRemove[reagent];
- totalReagentsToRemove.Remove(reagent);
- }
- else
- {
- totalReagentsToRemove[reagent] -= quant;
- }
-
- _solutionContainer.RemoveReagent(soln, reagent, quant);
+ quant = totalReagentsToRemove[reagent];
+ totalReagentsToRemove.Remove(reagent);
}
+ else
+ {
+ totalReagentsToRemove[reagent] -= quant;
+ }
+
+ _solutionContainer.RemoveReagent(solutionEntity.Value, reagent, quant);
}
}
@@ -541,7 +563,8 @@ namespace Content.Server.Kitchen.EntitySystems
continue;
}
- AddComp(item);
+ var microwavedComp = AddComp(item);
+ microwavedComp.Microwave = uid;
string? solidID = null;
int amountToAdd = 1;
@@ -560,33 +583,20 @@ namespace Content.Server.Kitchen.EntitySystems
}
if (solidID is null)
- {
continue;
- }
-
- if (solidsDict.ContainsKey(solidID))
- {
+ if (!solidsDict.TryAdd(solidID, amountToAdd))
solidsDict[solidID] += amountToAdd;
- }
- else
- {
- solidsDict.Add(solidID, amountToAdd);
- }
- if (!TryComp(item, out var solMan))
+ // only use reagents we have access to
+ // you have to break the eggs before we can use them!
+ if (!_solutionContainer.TryGetDrainableSolution(item, out var _, out var solution))
continue;
- foreach (var (_, soln) in _solutionContainer.EnumerateSolutions((item, solMan)))
+ foreach (var (reagent, quantity) in solution.Contents)
{
- var solution = soln.Comp.Solution;
- foreach (var (reagent, quantity) in solution.Contents)
- {
- if (reagentDict.ContainsKey(reagent.Prototype))
- reagentDict[reagent.Prototype] += quantity;
- else
- reagentDict.Add(reagent.Prototype, quantity);
- }
+ if (!reagentDict.TryAdd(reagent.Prototype, quantity))
+ reagentDict[reagent.Prototype] += quantity;
}
}
diff --git a/Content.Server/Materials/MaterialStorageSystem.cs b/Content.Server/Materials/MaterialStorageSystem.cs
index 9f43a22049..8c50d5fe9a 100644
--- a/Content.Server/Materials/MaterialStorageSystem.cs
+++ b/Content.Server/Materials/MaterialStorageSystem.cs
@@ -69,7 +69,7 @@ public sealed class MaterialStorageSystem : SharedMaterialStorageSystem
if (material.StackEntity != null)
{
- if (!_prototypeManager.Index(material.StackEntity).TryGetComponent(out var composition))
+ if (!_prototypeManager.Index(material.StackEntity).TryGetComponent(out var composition, EntityManager.ComponentFactory))
return;
var volumePerSheet = composition.MaterialComposition.FirstOrDefault(kvp => kvp.Key == msg.Material).Value;
@@ -169,7 +169,7 @@ public sealed class MaterialStorageSystem : SharedMaterialStorageSystem
return new List();
var entProto = _prototypeManager.Index(materialProto.StackEntity);
- if (!entProto.TryGetComponent(out var composition))
+ if (!entProto.TryGetComponent(out var composition, EntityManager.ComponentFactory))
return new List();
var materialPerStack = composition.MaterialComposition[materialProto.ID];
diff --git a/Content.Server/Medical/VomitSystem.cs b/Content.Server/Medical/VomitSystem.cs
index 5cff161e0e..a6982cea4f 100644
--- a/Content.Server/Medical/VomitSystem.cs
+++ b/Content.Server/Medical/VomitSystem.cs
@@ -31,6 +31,12 @@ namespace Content.Server.Medical
[Dependency] private readonly ForensicsSystem _forensics = default!;
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
+ [ValidatePrototypeId]
+ private const string VomitCollection = "Vomit";
+
+ private readonly SoundSpecifier _vomitSound = new SoundCollectionSpecifier(VomitCollection,
+ AudioParams.Default.WithVariation(0.2f).WithVolume(-4f));
+
///
/// Make an entity vomit, if they have a stomach.
///
@@ -94,7 +100,7 @@ namespace Content.Server.Medical
}
// Force sound to play as spill doesn't work if solution is empty.
- _audio.PlayPvs("/Audio/Effects/Fluids/splat.ogg", uid, AudioParams.Default.WithVariation(0.2f).WithVolume(-4f));
+ _audio.PlayPvs(_vomitSound, uid);
_popup.PopupEntity(Loc.GetString("disease-vomit", ("person", Identity.Entity(uid, EntityManager))), uid);
}
}
diff --git a/Content.Server/NPC/HTN/HTNCompoundPrototype.cs b/Content.Server/NPC/HTN/HTNCompoundPrototype.cs
index 69f8441973..b99ea09826 100644
--- a/Content.Server/NPC/HTN/HTNCompoundPrototype.cs
+++ b/Content.Server/NPC/HTN/HTNCompoundPrototype.cs
@@ -8,7 +8,7 @@ namespace Content.Server.NPC.HTN;
[Prototype("htnCompound")]
public sealed partial class HTNCompoundPrototype : IPrototype
{
- [IdDataField] public string ID { get; } = string.Empty;
+ [IdDataField] public string ID { get; private set; } = string.Empty;
[DataField("branches", required: true)]
public List Branches = new();
diff --git a/Content.Server/NPC/Queries/Curves/UtilityCurvePresetPrototype.cs b/Content.Server/NPC/Queries/Curves/UtilityCurvePresetPrototype.cs
index 7a6e9152c6..3116dede93 100644
--- a/Content.Server/NPC/Queries/Curves/UtilityCurvePresetPrototype.cs
+++ b/Content.Server/NPC/Queries/Curves/UtilityCurvePresetPrototype.cs
@@ -5,7 +5,7 @@ namespace Content.Server.NPC.Queries.Curves;
[Prototype("utilityCurvePreset")]
public sealed partial class UtilityCurvePresetPrototype : IPrototype
{
- [IdDataField] public string ID { get; } = string.Empty;
+ [IdDataField] public string ID { get; private set; } = string.Empty;
[DataField("curve", required: true)] public IUtilityCurve Curve = default!;
}
diff --git a/Content.Server/Objectives/Components/FreeObjectiveComponent.cs b/Content.Server/Objectives/Components/FreeObjectiveComponent.cs
new file mode 100644
index 0000000000..ae5070ddf7
--- /dev/null
+++ b/Content.Server/Objectives/Components/FreeObjectiveComponent.cs
@@ -0,0 +1,7 @@
+namespace Content.Server.Objectives.Components;
+
+///
+/// A free greentext, that's it.
+///
+[RegisterComponent]
+public sealed partial class FreeObjectiveComponent : Component;
diff --git a/Content.Server/Objectives/Systems/FreeObjectiveSystem.cs b/Content.Server/Objectives/Systems/FreeObjectiveSystem.cs
new file mode 100644
index 0000000000..ae1753b5c4
--- /dev/null
+++ b/Content.Server/Objectives/Systems/FreeObjectiveSystem.cs
@@ -0,0 +1,20 @@
+using Content.Server.Objectives.Components;
+using Content.Shared.Objectives.Components;
+
+namespace Content.Server.Objectives.Systems;
+
+public sealed class FreeObjectiveSystem : EntitySystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnGetProgress);
+ }
+
+ // You automatically greentext, there's not much else to it
+ private void OnGetProgress(Entity ent, ref ObjectiveGetProgressEvent args)
+ {
+ args.Progress = 1f;
+ }
+}
diff --git a/Content.Server/Roles/WizardRoleComponent.cs b/Content.Server/Roles/WizardRoleComponent.cs
new file mode 100644
index 0000000000..72a89ee2ca
--- /dev/null
+++ b/Content.Server/Roles/WizardRoleComponent.cs
@@ -0,0 +1,7 @@
+namespace Content.Server.Roles;
+
+///
+/// Mind role to tag entities that they're a Wizard
+///
+[RegisterComponent]
+public sealed partial class WizardRoleComponent : Component;
diff --git a/Content.Server/Salvage/SalvageSystem.Magnet.cs b/Content.Server/Salvage/SalvageSystem.Magnet.cs
index 523ac06e0b..988c5da797 100644
--- a/Content.Server/Salvage/SalvageSystem.Magnet.cs
+++ b/Content.Server/Salvage/SalvageSystem.Magnet.cs
@@ -354,7 +354,7 @@ public sealed partial class SalvageSystem
if (!TryGetSalvagePlacementLocation(magnet, mapId, attachedBounds, bounds!.Value, worldAngle, out var spawnLocation, out var spawnAngle))
{
Report(magnet.Owner, MagnetChannel, "salvage-system-announcement-spawn-no-debris-available");
- _mapManager.DeleteMap(salvMapXform.MapID);
+ _mapSystem.DeleteMap(salvMapXform.MapID);
return;
}
@@ -391,7 +391,7 @@ public sealed partial class SalvageSystem
}
Report(magnet.Owner, MagnetChannel, "salvage-system-announcement-arrived", ("timeLeft", data.Comp.ActiveTime.TotalSeconds));
- _mapManager.DeleteMap(salvMapXform.MapID);
+ _mapSystem.DeleteMap(salvMapXform.MapID);
data.Comp.Announced = false;
diff --git a/Content.Server/Shuttles/Components/GridSpawnComponent.cs b/Content.Server/Shuttles/Components/GridSpawnComponent.cs
index 18959dd7f3..c74613f5eb 100644
--- a/Content.Server/Shuttles/Components/GridSpawnComponent.cs
+++ b/Content.Server/Shuttles/Components/GridSpawnComponent.cs
@@ -62,7 +62,7 @@ public interface IGridSpawnGroup
}
[DataRecord]
-public sealed class DungeonSpawnGroup : IGridSpawnGroup
+public sealed partial class DungeonSpawnGroup : IGridSpawnGroup
{
///
/// Prototypes we can choose from to spawn.
@@ -97,7 +97,7 @@ public sealed class DungeonSpawnGroup : IGridSpawnGroup
}
[DataRecord]
-public sealed class GridSpawnGroup : IGridSpawnGroup
+public sealed partial class GridSpawnGroup : IGridSpawnGroup
{
public List Paths = new();
diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs
index 708f5a7a1c..896570fb34 100644
--- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs
+++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs
@@ -30,8 +30,10 @@ using Robust.Server.GameObjects;
using Robust.Shared.Collections;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
+using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
@@ -46,23 +48,23 @@ namespace Content.Server.Shuttles.Systems;
///
public sealed class ArrivalsSystem : EntitySystem
{
+ [Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
[Dependency] private readonly IConsoleHost _console = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
- [Dependency] private readonly IChatManager _chat = default!;
- [Dependency] private readonly SharedMapSystem _mapSystem = default!;
+ [Dependency] private readonly ActorSystem _actor = default!;
[Dependency] private readonly BiomeSystem _biomes = default!;
+ [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
[Dependency] private readonly GameTicker _ticker = default!;
[Dependency] private readonly MapLoaderSystem _loader = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
- [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
+ [Dependency] private readonly SharedMapSystem _mapSystem = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly ShuttleSystem _shuttles = default!;
[Dependency] private readonly StationSpawningSystem _stationSpawning = default!;
[Dependency] private readonly StationSystem _station = default!;
- [Dependency] private readonly ActorSystem _actor = default!;
private EntityQuery _pendingQuery;
private EntityQuery _blacklistQuery;
@@ -515,31 +517,31 @@ public sealed class ArrivalsSystem : EntitySystem
private void SetupArrivalsStation()
{
var path = new ResPath(_cfgManager.GetCVar(CCVars.ArrivalsMap));
- if (!_loader.TryLoadMap(path, out var map, out var grids))
+ _mapSystem.CreateMap(out var mapId, runMapInit: false);
+ var mapUid = _mapSystem.GetMap(mapId);
+
+ if (!_loader.TryLoadGrid(mapId, path, out var grid))
return;
- _metaData.SetEntityName(map.Value, Loc.GetString("map-name-terminal"));
+ _metaData.SetEntityName(mapUid, Loc.GetString("map-name-terminal"));
- foreach (var id in grids)
- {
- EnsureComp(id);
- EnsureComp(id);
- EnsureComp(id);
- }
+ EnsureComp(grid.Value);
+ EnsureComp(grid.Value);
+ EnsureComp(grid.Value);
// Setup planet arrivals if relevant
if (_cfgManager.GetCVar(CCVars.ArrivalsPlanet))
{
var template = _random.Pick(_arrivalsBiomeOptions);
- _biomes.EnsurePlanet(map.Value, _protoManager.Index(template));
+ _biomes.EnsurePlanet(mapUid, _protoManager.Index(template));
var restricted = new RestrictedRangeComponent
{
Range = 32f
};
- AddComp(map.Value, restricted);
+ AddComp(mapUid, restricted);
}
- _mapSystem.InitializeMap(map.Value.Comp.MapId);
+ _mapSystem.InitializeMap(mapId);
// Handle roundstart stations.
var query = AllEntityQuery();
diff --git a/Content.Server/Silicons/Laws/IonStormSystem.cs b/Content.Server/Silicons/Laws/IonStormSystem.cs
index 6017a36fc0..c47d7e99ea 100644
--- a/Content.Server/Silicons/Laws/IonStormSystem.cs
+++ b/Content.Server/Silicons/Laws/IonStormSystem.cs
@@ -200,11 +200,11 @@ public sealed class IonStormSystem : EntitySystem
// i dont think theres a way to do this in fluent
var (who, plural) = _robustRandom.Next(0, 5) switch
{
- 0 => (Loc.GetString("ion-storm-you"), false),
- 1 => (Loc.GetString("ion-storm-the-station"), true),
- 2 => (Loc.GetString("ion-storm-the-crew"), true),
- 3 => (Loc.GetString("ion-storm-the-job", ("job", crew2)), false),
- _ => (area, true) // THE SINGULARITY REQUIRES THE HAPPY CLOWNS
+ 0 => (Loc.GetString("ion-storm-you"), true),
+ 1 => (Loc.GetString("ion-storm-the-station"), false),
+ 2 => (Loc.GetString("ion-storm-the-crew"), false),
+ 3 => (Loc.GetString("ion-storm-the-job", ("job", crew2)), true),
+ _ => (area, false) // THE SINGULARITY REQUIRES THE HAPPY CLOWNS
};
var jobChange = _robustRandom.Next(0, 3) switch
{
diff --git a/Content.Server/StationEvents/Components/PowerGridCheckRuleComponent.cs b/Content.Server/StationEvents/Components/PowerGridCheckRuleComponent.cs
index 98c60346c7..757a4f18b5 100644
--- a/Content.Server/StationEvents/Components/PowerGridCheckRuleComponent.cs
+++ b/Content.Server/StationEvents/Components/PowerGridCheckRuleComponent.cs
@@ -1,11 +1,24 @@
using System.Threading;
using Content.Server.StationEvents.Events;
+using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
namespace Content.Server.StationEvents.Components;
[RegisterComponent, Access(typeof(PowerGridCheckRule))]
public sealed partial class PowerGridCheckRuleComponent : Component
{
+ ///
+ /// Default sound of the announcement when power is back on.
+ ///
+ private static readonly ProtoId DefaultPowerOn = new("PowerOn");
+
+ ///
+ /// Sound of the announcement to play when power is back on.
+ ///
+ [DataField]
+ public SoundSpecifier PowerOnSound = new SoundCollectionSpecifier(DefaultPowerOn, AudioParams.Default.WithVolume(-4f));
+
public CancellationTokenSource? AnnounceCancelToken;
public EntityUid AffectedStation;
diff --git a/Content.Server/StationEvents/EventManagerSystem.cs b/Content.Server/StationEvents/EventManagerSystem.cs
index 86c98a8e30..a66499d0aa 100644
--- a/Content.Server/StationEvents/EventManagerSystem.cs
+++ b/Content.Server/StationEvents/EventManagerSystem.cs
@@ -210,7 +210,7 @@ public sealed class EventManagerSystem : EntitySystem
if (prototype.Abstract)
continue;
- if (!prototype.TryGetComponent(out var stationEvent))
+ if (!prototype.TryGetComponent(out var stationEvent, EntityManager.ComponentFactory))
continue;
allEvents.Add(prototype, stationEvent);
diff --git a/Content.Server/StationEvents/Events/ImmovableRodRule.cs b/Content.Server/StationEvents/Events/ImmovableRodRule.cs
index 17a92e1b3d..5e324a529b 100644
--- a/Content.Server/StationEvents/Events/ImmovableRodRule.cs
+++ b/Content.Server/StationEvents/Events/ImmovableRodRule.cs
@@ -26,7 +26,8 @@ public sealed class ImmovableRodRule : StationEventSystem(protoName);
- if (proto.TryGetComponent(out var rod) && proto.TryGetComponent(out var despawn))
+ if (proto.TryGetComponent(out var rod, EntityManager.ComponentFactory) &&
+ proto.TryGetComponent(out var despawn, EntityManager.ComponentFactory))
{
if (!TryFindRandomTile(out _, out _, out _, out var targetCoords))
return;
diff --git a/Content.Server/StationEvents/Events/PowerGridCheckRule.cs b/Content.Server/StationEvents/Events/PowerGridCheckRule.cs
index a4594e1387..3a86f694ab 100644
--- a/Content.Server/StationEvents/Events/PowerGridCheckRule.cs
+++ b/Content.Server/StationEvents/Events/PowerGridCheckRule.cs
@@ -59,7 +59,7 @@ namespace Content.Server.StationEvents.Events
component.AnnounceCancelToken = new CancellationTokenSource();
Timer.Spawn(3000, () =>
{
- Audio.PlayGlobal("/Audio/Announcements/power_on.ogg", Filter.Broadcast(), true, AudioParams.Default.WithVolume(-4f));
+ Audio.PlayGlobal(component.PowerOnSound, Filter.Broadcast(), true);
}, component.AnnounceCancelToken.Token);
component.Unpowered.Clear();
}
diff --git a/Content.Server/StationRecords/Systems/StationRecordsSystem.cs b/Content.Server/StationRecords/Systems/StationRecordsSystem.cs
index 87947ddf79..6a7584e65c 100644
--- a/Content.Server/StationRecords/Systems/StationRecordsSystem.cs
+++ b/Content.Server/StationRecords/Systems/StationRecordsSystem.cs
@@ -11,6 +11,7 @@ using Content.Shared.Roles;
using Content.Shared.StationRecords;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
namespace Content.Server.StationRecords.Systems;
@@ -39,6 +40,7 @@ public sealed class StationRecordsSystem : SharedStationRecordsSystem
[Dependency] private readonly StationRecordKeyStorageSystem _keyStorage = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IdCardSystem _idCard = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
public override void Initialize()
{
@@ -232,6 +234,28 @@ public sealed class StationRecordsSystem : SharedStationRecordsSystem
return records.Records.TryGetRecordEntry(key.Id, out entry);
}
+ ///
+ /// Gets a random record from the station's record entries.
+ ///
+ /// The EntityId of the station from which you want to get the record.
+ /// The resulting entry.
+ /// Type to get from the record set.
+ /// True if a record was obtained. False otherwise.
+ public bool TryGetRandomRecord(Entity ent, [NotNullWhen(true)] out T? entry)
+ {
+ entry = default;
+
+ if (!Resolve(ent.Owner, ref ent.Comp))
+ return false;
+
+ if (ent.Comp.Records.Keys.Count == 0)
+ return false;
+
+ var key = _random.Pick(ent.Comp.Records.Keys);
+
+ return ent.Comp.Records.TryGetRecordEntry(key, out entry);
+ }
+
///
/// Returns an id if a record with the same name exists.
///
diff --git a/Content.Server/Store/Systems/StoreSystem.Listings.cs b/Content.Server/Store/Systems/StoreSystem.Listings.cs
index 4699ec7b66..412114ce03 100644
--- a/Content.Server/Store/Systems/StoreSystem.Listings.cs
+++ b/Content.Server/Store/Systems/StoreSystem.Listings.cs
@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
+using Content.Shared.Mind;
using Content.Shared.Store;
using Content.Shared.Store.Components;
using Robust.Shared.Prototypes;
@@ -117,7 +118,7 @@ public sealed partial class StoreSystem
if (listing.Conditions != null)
{
- var args = new ListingConditionArgs(buyer, storeEntity, listing, EntityManager);
+ var args = new ListingConditionArgs(GetBuyerMind(buyer), storeEntity, listing, EntityManager);
var conditionsMet = true;
foreach (var condition in listing.Conditions)
@@ -137,6 +138,19 @@ public sealed partial class StoreSystem
}
}
+ ///
+ /// Returns the entity's mind entity, if it has one, to be used for listing conditions.
+ /// If it doesn't have one, or is a mind entity already, it returns itself.
+ ///
+ /// The buying entity.
+ public EntityUid GetBuyerMind(EntityUid buyer)
+ {
+ if (!HasComp(buyer) && _mind.TryGetMind(buyer, out var buyerMind, out var _))
+ return buyerMind;
+
+ return buyer;
+ }
+
///
/// Checks if a listing appears in a list of given categories
///
diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs
index 3f4ccf696d..93bbd3bd96 100644
--- a/Content.Server/Store/Systems/StoreSystem.Ui.cs
+++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs
@@ -146,7 +146,7 @@ public sealed partial class StoreSystem
//condition checking because why not
if (listing.Conditions != null)
{
- var args = new ListingConditionArgs(component.AccountOwner ?? buyer, uid, listing, EntityManager);
+ var args = new ListingConditionArgs(component.AccountOwner ?? GetBuyerMind(buyer), uid, listing, EntityManager);
var conditionsMet = listing.Conditions.All(condition => condition.Condition(args));
if (!conditionsMet)
diff --git a/Content.Server/Wires/WireLayout.cs b/Content.Server/Wires/WireLayout.cs
index 621992c915..7ca4faaeca 100644
--- a/Content.Server/Wires/WireLayout.cs
+++ b/Content.Server/Wires/WireLayout.cs
@@ -20,7 +20,7 @@ public sealed partial class WireLayoutPrototype : IPrototype, IInheritingPrototy
public string[]? Parents { get; private set; }
[AbstractDataField]
- public bool Abstract { get; }
+ public bool Abstract { get; private set; }
///
/// How many wires in this layout will do
diff --git a/Content.Server/Worldgen/Prototypes/BiomePrototype.cs b/Content.Server/Worldgen/Prototypes/BiomePrototype.cs
index 1cecf87a5e..1c3633fb5f 100644
--- a/Content.Server/Worldgen/Prototypes/BiomePrototype.cs
+++ b/Content.Server/Worldgen/Prototypes/BiomePrototype.cs
@@ -14,12 +14,12 @@ public sealed partial class BiomePrototype : IPrototype, IInheritingPrototype
{
///
[ParentDataField(typeof(AbstractPrototypeIdArraySerializer))]
- public string[]? Parents { get; }
+ public string[]? Parents { get; private set; }
///
[NeverPushInheritance]
[AbstractDataField]
- public bool Abstract { get; }
+ public bool Abstract { get; private set; }
///
[IdDataField]
@@ -42,7 +42,7 @@ public sealed partial class BiomePrototype : IPrototype, IInheritingPrototype
///
[DataField("chunkComponents")]
[AlwaysPushInheritance]
- public ComponentRegistry ChunkComponents { get; } = new();
+ public ComponentRegistry ChunkComponents = new();
//TODO: Get someone to make this a method on componentregistry that does it Correctly.
///
diff --git a/Content.Server/Worldgen/Prototypes/NoiseChannelPrototype.cs b/Content.Server/Worldgen/Prototypes/NoiseChannelPrototype.cs
index 02ca383d30..deb39de99e 100644
--- a/Content.Server/Worldgen/Prototypes/NoiseChannelPrototype.cs
+++ b/Content.Server/Worldgen/Prototypes/NoiseChannelPrototype.cs
@@ -84,12 +84,12 @@ public sealed partial class NoiseChannelPrototype : NoiseChannelConfig, IPrototy
{
///
[ParentDataField(typeof(AbstractPrototypeIdArraySerializer))]
- public string[]? Parents { get; }
+ public string[]? Parents { get; private set; }
///
[NeverPushInheritance]
[AbstractDataField]
- public bool Abstract { get; }
+ public bool Abstract { get; private set; }
///
[IdDataField]
diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs
index 6152699ccb..08eac657c0 100644
--- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs
+++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs
@@ -109,10 +109,16 @@ namespace Content.Shared.ActionBlocker
///
public bool CanUseHeldEntity(EntityUid user, EntityUid used)
{
- var ev = new UseAttemptEvent(user, used);
- RaiseLocalEvent(user, ev);
+ var useEv = new UseAttemptEvent(user, used);
+ RaiseLocalEvent(user, useEv);
- return !ev.Cancelled;
+ if (useEv.Cancelled)
+ return false;
+
+ var usedEv = new GettingUsedAttemptEvent(user);
+ RaiseLocalEvent(used, usedEv);
+
+ return !usedEv.Cancelled;
}
diff --git a/Content.Shared/Alert/AlertCategoryPrototype.cs b/Content.Shared/Alert/AlertCategoryPrototype.cs
index 7c7d047521..6685151142 100644
--- a/Content.Shared/Alert/AlertCategoryPrototype.cs
+++ b/Content.Shared/Alert/AlertCategoryPrototype.cs
@@ -10,5 +10,5 @@ public sealed partial class AlertCategoryPrototype : IPrototype
{
///
[IdDataField]
- public string ID { get; } = default!;
+ public string ID { get; private set; } = default!;
}
diff --git a/Content.Shared/Audio/AmbientMusicPrototype.cs b/Content.Shared/Audio/AmbientMusicPrototype.cs
index 219c41527d..e98f324a06 100644
--- a/Content.Shared/Audio/AmbientMusicPrototype.cs
+++ b/Content.Shared/Audio/AmbientMusicPrototype.cs
@@ -12,7 +12,7 @@ namespace Content.Shared.Audio;
[Prototype("ambientMusic")]
public sealed partial class AmbientMusicPrototype : IPrototype
{
- [IdDataField] public string ID { get; } = string.Empty;
+ [IdDataField] public string ID { get; private set; } = string.Empty;
///
/// Traditionally you'd prioritise most rules to least as priority but in our case we'll just be explicit.
diff --git a/Content.Shared/Audio/Jukebox/JukeboxPrototype.cs b/Content.Shared/Audio/Jukebox/JukeboxPrototype.cs
index ad690ef497..df90d42428 100644
--- a/Content.Shared/Audio/Jukebox/JukeboxPrototype.cs
+++ b/Content.Shared/Audio/Jukebox/JukeboxPrototype.cs
@@ -10,7 +10,7 @@ namespace Content.Shared.Audio.Jukebox;
public sealed partial class JukeboxPrototype : IPrototype
{
[IdDataField]
- public string ID { get; } = string.Empty;
+ public string ID { get; private set; } = string.Empty;
///
/// User friendly name to use in UI.
diff --git a/Content.Shared/Body/Prototypes/BodyPrototype.cs b/Content.Shared/Body/Prototypes/BodyPrototype.cs
index 123638f099..646acaaf6b 100644
--- a/Content.Shared/Body/Prototypes/BodyPrototype.cs
+++ b/Content.Shared/Body/Prototypes/BodyPrototype.cs
@@ -5,7 +5,7 @@ namespace Content.Shared.Body.Prototypes;
[Prototype("body")]
public sealed partial class BodyPrototype : IPrototype
{
- [IdDataField] public string ID { get; } = default!;
+ [IdDataField] public string ID { get; private set; } = default!;
[DataField("name")]
public string Name { get; private set; } = "";
@@ -26,4 +26,4 @@ public sealed partial class BodyPrototype : IPrototype
}
[DataRecord]
-public sealed record BodyPrototypeSlot(EntProtoId? Part, HashSet Connections, Dictionary Organs);
+public sealed partial record BodyPrototypeSlot(EntProtoId? Part, HashSet Connections, Dictionary Organs);
diff --git a/Content.Shared/CCVar/CCVars.Accessibility.cs b/Content.Shared/CCVar/CCVars.Accessibility.cs
index 8eb61f0806..14312363a1 100644
--- a/Content.Shared/CCVar/CCVars.Accessibility.cs
+++ b/Content.Shared/CCVar/CCVars.Accessibility.cs
@@ -38,4 +38,27 @@ public sealed partial class CCVars
///
public static readonly CVarDef AccessibilityColorblindFriendly =
CVarDef.Create("accessibility.colorblind_friendly", false, CVar.CLIENTONLY | CVar.ARCHIVE);
+
+ ///
+ /// Speech bubble text opacity slider, controlling the alpha of speech bubble's text.
+ /// Goes from to 0 (completely transparent) to 1 (completely opaque)
+ ///
+ public static readonly CVarDef SpeechBubbleTextOpacity =
+ CVarDef.Create("accessibility.speech_bubble_text_opacity", 1f, CVar.CLIENTONLY | CVar.ARCHIVE);
+
+ ///
+ /// Speech bubble speaker opacity slider, controlling the alpha of the speaker's name in a speech bubble.
+ /// Goes from to 0 (completely transparent) to 1 (completely opaque)
+ ///
+ public static readonly CVarDef SpeechBubbleSpeakerOpacity =
+ CVarDef.Create("accessibility.speech_bubble_speaker_opacity", 1f, CVar.CLIENTONLY | CVar.ARCHIVE);
+
+ ///
+ /// Speech bubble background opacity slider, controlling the alpha of the speech bubble's background.
+ /// Goes from to 0 (completely transparent) to 1 (completely opaque)
+ ///
+ public static readonly CVarDef SpeechBubbleBackgroundOpacity =
+ CVarDef.Create("accessibility.speech_bubble_background_opacity", 0.75f, CVar.CLIENTONLY | CVar.ARCHIVE);
+
+
}
diff --git a/Content.Shared/CCVar/CCVars.Game.cs b/Content.Shared/CCVar/CCVars.Game.cs
index 4a6f1118a1..13e72b9e3c 100644
--- a/Content.Shared/CCVar/CCVars.Game.cs
+++ b/Content.Shared/CCVar/CCVars.Game.cs
@@ -5,391 +5,390 @@ namespace Content.Shared.CCVar;
public sealed partial class CCVars
{
- ///
- /// Disables most functionality in the GameTicker.
- ///
- public static readonly CVarDef
- GameDummyTicker = CVarDef.Create("game.dummyticker", false, CVar.ARCHIVE | CVar.SERVERONLY);
+ ///
+ /// Disables most functionality in the GameTicker.
+ ///
+ public static readonly CVarDef
+ GameDummyTicker = CVarDef.Create("game.dummyticker", false, CVar.ARCHIVE | CVar.SERVERONLY);
- ///
- /// Controls if the lobby is enabled. If it is not, and there are no available jobs, you may get stuck on a black screen.
- ///
- public static readonly CVarDef
- GameLobbyEnabled = CVarDef.Create("game.lobbyenabled", true, CVar.ARCHIVE);
+ ///
+ /// Controls if the lobby is enabled. If it is not, and there are no available jobs, you may get stuck on a black screen.
+ ///
+ public static readonly CVarDef
+ GameLobbyEnabled = CVarDef.Create("game.lobbyenabled", true, CVar.ARCHIVE);
- ///
- /// Controls the duration of the lobby timer in seconds. Defaults to 2 minutes and 30 seconds.
- ///
- public static readonly CVarDef
- GameLobbyDuration = CVarDef.Create("game.lobbyduration", 150, CVar.ARCHIVE);
+ ///
+ /// Controls the duration of the lobby timer in seconds. Defaults to 2 minutes and 30 seconds.
+ ///
+ public static readonly CVarDef
+ GameLobbyDuration = CVarDef.Create("game.lobbyduration", 150, CVar.ARCHIVE);
- ///
- /// Controls if players can latejoin at all.
- ///
- public static readonly CVarDef
- GameDisallowLateJoins = CVarDef.Create("game.disallowlatejoins", false, CVar.ARCHIVE | CVar.SERVERONLY);
+ ///
+ /// Controls if players can latejoin at all.
+ ///
+ public static readonly CVarDef
+ GameDisallowLateJoins = CVarDef.Create("game.disallowlatejoins", false, CVar.ARCHIVE | CVar.SERVERONLY);
- ///
- /// Controls the default game preset.
- ///
- public static readonly CVarDef
- GameLobbyDefaultPreset = CVarDef.Create("game.defaultpreset", "secret", CVar.ARCHIVE);
+ ///
+ /// Controls the default game preset.
+ ///
+ public static readonly CVarDef
+ GameLobbyDefaultPreset = CVarDef.Create("game.defaultpreset", "secret", CVar.ARCHIVE);
- ///
- /// Controls if the game can force a different preset if the current preset's criteria are not met.
- ///
- public static readonly CVarDef
- GameLobbyFallbackEnabled = CVarDef.Create("game.fallbackenabled", true, CVar.ARCHIVE);
+ ///
+ /// Controls if the game can force a different preset if the current preset's criteria are not met.
+ ///
+ public static readonly CVarDef
+ GameLobbyFallbackEnabled = CVarDef.Create("game.fallbackenabled", true, CVar.ARCHIVE);
- ///
- /// The preset for the game to fall back to if the selected preset could not be used, and fallback is enabled.
- ///
- public static readonly CVarDef
- GameLobbyFallbackPreset = CVarDef.Create("game.fallbackpreset", "Traitor,Extended", CVar.ARCHIVE);
+ ///
+ /// The preset for the game to fall back to if the selected preset could not be used, and fallback is enabled.
+ ///
+ public static readonly CVarDef
+ GameLobbyFallbackPreset = CVarDef.Create("game.fallbackpreset", "Traitor,Extended", CVar.ARCHIVE);
- ///
- /// Controls if people can win the game in Suspicion or Deathmatch.
- ///
- public static readonly CVarDef
- GameLobbyEnableWin = CVarDef.Create("game.enablewin", true, CVar.ARCHIVE);
+ ///
+ /// Controls if people can win the game in Suspicion or Deathmatch.
+ ///
+ public static readonly CVarDef
+ GameLobbyEnableWin = CVarDef.Create("game.enablewin", true, CVar.ARCHIVE);
- ///
- /// Controls the maximum number of character slots a player is allowed to have.
- ///
- public static readonly CVarDef
- GameMaxCharacterSlots = CVarDef.Create("game.maxcharacterslots", 30, CVar.ARCHIVE | CVar.SERVERONLY);
+ ///
+ /// Controls the maximum number of character slots a player is allowed to have.
+ ///
+ public static readonly CVarDef
+ GameMaxCharacterSlots = CVarDef.Create("game.maxcharacterslots", 30, CVar.ARCHIVE | CVar.SERVERONLY);
- ///
- /// Controls the game map prototype to load. SS14 stores these prototypes in Prototypes/Maps.
- ///
- public static readonly CVarDef
- GameMap = CVarDef.Create("game.map", string.Empty, CVar.SERVERONLY);
+ ///
+ /// Controls the game map prototype to load. SS14 stores these prototypes in Prototypes/Maps.
+ ///
+ public static readonly CVarDef
+ GameMap = CVarDef.Create("game.map", string.Empty, CVar.SERVERONLY);
- ///
- /// Controls whether to use world persistence or not.
- ///
- public static readonly CVarDef
- UsePersistence = CVarDef.Create("game.usepersistence", false, CVar.ARCHIVE);
+ ///
+ /// Controls whether to use world persistence or not.
+ ///
+ public static readonly CVarDef
+ UsePersistence = CVarDef.Create("game.usepersistence", false, CVar.ARCHIVE);
- ///
- /// If world persistence is used, what map prototype should be initially loaded.
- /// If the save file exists, it replaces MapPath but everything else stays the same (station name and such).
- ///
- public static readonly CVarDef
- PersistenceMap = CVarDef.Create("game.persistencemap", "Empty", CVar.ARCHIVE);
+ ///
+ /// If world persistence is used, what map prototype should be initially loaded.
+ /// If the save file exists, it replaces MapPath but everything else stays the same (station name and such).
+ ///
+ public static readonly CVarDef
+ PersistenceMap = CVarDef.Create("game.persistencemap", "Empty", CVar.ARCHIVE);
- ///
- /// Prototype to use for map pool.
- ///
- public static readonly CVarDef
- GameMapPool = CVarDef.Create("game.map_pool", "DefaultMapPool", CVar.SERVERONLY);
+ ///
+ /// Prototype to use for map pool.
+ ///
+ public static readonly CVarDef
+ GameMapPool = CVarDef.Create("game.map_pool", "DefaultMapPool", CVar.SERVERONLY);
- ///
- /// The depth of the queue used to calculate which map is next in rotation.
- /// This is how long the game "remembers" that some map was put in play. Default is 16 rounds.
- ///
- public static readonly CVarDef
- GameMapMemoryDepth = CVarDef.Create("game.map_memory_depth", 16, CVar.SERVERONLY);
+ ///
+ /// The depth of the queue used to calculate which map is next in rotation.
+ /// This is how long the game "remembers" that some map was put in play. Default is 16 rounds.
+ ///
+ public static readonly CVarDef
+ GameMapMemoryDepth = CVarDef.Create("game.map_memory_depth", 16, CVar.SERVERONLY);
- ///
- /// Is map rotation enabled?
- ///
- public static readonly CVarDef
- GameMapRotation = CVarDef.Create("game.map_rotation", true, CVar.SERVERONLY);
+ ///
+ /// Is map rotation enabled?
+ ///
+ public static readonly CVarDef
+ GameMapRotation = CVarDef.Create("game.map_rotation", true, CVar.SERVERONLY);
- ///
- /// If roles should be restricted based on time.
- ///
- public static readonly CVarDef
- GameRoleTimers = CVarDef.Create("game.role_timers", true, CVar.SERVER | CVar.REPLICATED);
+ ///
+ /// If roles should be restricted based on time.
+ ///
+ public static readonly CVarDef
+ GameRoleTimers = CVarDef.Create("game.role_timers", true, CVar.SERVER | CVar.REPLICATED);
- ///
- /// Override default role requirements using a
- ///
- public static readonly CVarDef
- GameRoleTimerOverride = CVarDef.Create("game.role_timer_override", "", CVar.SERVER | CVar.REPLICATED);
+ ///
+ /// Override default role requirements using a
+ ///
+ public static readonly CVarDef
+ GameRoleTimerOverride = CVarDef.Create("game.role_timer_override", "", CVar.SERVER | CVar.REPLICATED);
- ///
- /// If roles should be restricted based on whether or not they are whitelisted.
- ///
- public static readonly CVarDef
- GameRoleWhitelist = CVarDef.Create("game.role_whitelist", true, CVar.SERVER | CVar.REPLICATED);
+ ///
+ /// If roles should be restricted based on whether or not they are whitelisted.
+ ///
+ public static readonly CVarDef
+ GameRoleWhitelist = CVarDef.Create("game.role_whitelist", true, CVar.SERVER | CVar.REPLICATED);
- ///
- /// Whether or not disconnecting inside of a cryopod should remove the character or just store them until they reconnect.
- ///
- public static readonly CVarDef
- GameCryoSleepRejoining = CVarDef.Create("game.cryo_sleep_rejoining", false, CVar.SERVER | CVar.REPLICATED);
+ ///
+ /// Whether or not disconnecting inside of a cryopod should remove the character or just store them until they reconnect.
+ ///
+ public static readonly CVarDef
+ GameCryoSleepRejoining = CVarDef.Create("game.cryo_sleep_rejoining", false, CVar.SERVER | CVar.REPLICATED);
- ///
- /// When enabled, guests will be assigned permanent UIDs and will have their preferences stored.
- ///
- public static readonly CVarDef GamePersistGuests =
- CVarDef.Create("game.persistguests", true, CVar.ARCHIVE | CVar.SERVERONLY);
+ ///
+ /// When enabled, guests will be assigned permanent UIDs and will have their preferences stored.
+ ///
+ public static readonly CVarDef GamePersistGuests =
+ CVarDef.Create("game.persistguests", true, CVar.ARCHIVE | CVar.SERVERONLY);
- public static readonly CVarDef GameDiagonalMovement =
- CVarDef.Create("game.diagonalmovement", true, CVar.ARCHIVE);
+ public static readonly CVarDef GameDiagonalMovement =
+ CVarDef.Create("game.diagonalmovement", true, CVar.ARCHIVE);
- public static readonly CVarDef SoftMaxPlayers =
- CVarDef.Create("game.soft_max_players", 30, CVar.SERVERONLY | CVar.ARCHIVE);
+ public static readonly CVarDef SoftMaxPlayers =
+ CVarDef.Create("game.soft_max_players", 30, CVar.SERVERONLY | CVar.ARCHIVE);
- ///
- /// If a player gets denied connection to the server,
- /// how long they are forced to wait before attempting to reconnect.
- ///
- public static readonly CVarDef GameServerFullReconnectDelay =
- CVarDef.Create("game.server_full_reconnect_delay", 30, CVar.SERVERONLY);
+ ///
+ /// If a player gets denied connection to the server,
+ /// how long they are forced to wait before attempting to reconnect.
+ ///
+ public static readonly CVarDef GameServerFullReconnectDelay =
+ CVarDef.Create("game.server_full_reconnect_delay", 30, CVar.SERVERONLY);
- ///
- /// Whether or not panic bunker is currently enabled.
- ///
- public static readonly CVarDef PanicBunkerEnabled =
- CVarDef.Create("game.panic_bunker.enabled", false, CVar.NOTIFY | CVar.REPLICATED | CVar.SERVER);
+ ///
+ /// Whether or not panic bunker is currently enabled.
+ ///
+ public static readonly CVarDef PanicBunkerEnabled =
+ CVarDef.Create("game.panic_bunker.enabled", false, CVar.NOTIFY | CVar.REPLICATED | CVar.SERVER);
- ///
- /// Whether or not the panic bunker will disable when an admin comes online.
- ///
- public static readonly CVarDef PanicBunkerDisableWithAdmins =
- CVarDef.Create("game.panic_bunker.disable_with_admins", false, CVar.SERVERONLY);
+ ///
+ /// Whether or not the panic bunker will disable when an admin comes online.
+ ///
+ public static readonly CVarDef PanicBunkerDisableWithAdmins =
+ CVarDef.Create("game.panic_bunker.disable_with_admins", false, CVar.SERVERONLY);
- ///
- /// Whether or not the panic bunker will enable when no admins are online.
- /// This counts everyone with the 'Admin' AdminFlag.
- ///
- public static readonly CVarDef PanicBunkerEnableWithoutAdmins =
- CVarDef.Create("game.panic_bunker.enable_without_admins", false, CVar.SERVERONLY);
+ ///
+ /// Whether or not the panic bunker will enable when no admins are online.
+ /// This counts everyone with the 'Admin' AdminFlag.
+ ///
+ public static readonly CVarDef PanicBunkerEnableWithoutAdmins =
+ CVarDef.Create("game.panic_bunker.enable_without_admins", false, CVar.SERVERONLY);
- ///
- /// Whether or not the panic bunker will count deadminned admins for
- /// and
- ///
- ///
- public static readonly CVarDef PanicBunkerCountDeadminnedAdmins =
- CVarDef.Create("game.panic_bunker.count_deadminned_admins", false, CVar.SERVERONLY);
+ ///
+ /// Whether or not the panic bunker will count deadminned admins for
+ /// and
+ ///
+ ///
+ public static readonly CVarDef PanicBunkerCountDeadminnedAdmins =
+ CVarDef.Create("game.panic_bunker.count_deadminned_admins", false, CVar.SERVERONLY);
- ///
- /// Show reason of disconnect for user or not.
- ///
- public static readonly CVarDef PanicBunkerShowReason =
- CVarDef.Create("game.panic_bunker.show_reason", false, CVar.SERVERONLY);
+ ///
+ /// Show reason of disconnect for user or not.
+ ///
+ public static readonly CVarDef PanicBunkerShowReason =
+ CVarDef.Create("game.panic_bunker.show_reason", false, CVar.SERVERONLY);
- ///
- /// Minimum age of the account (from server's PoV, so from first-seen date) in minutes.
- ///
- public static readonly CVarDef PanicBunkerMinAccountAge =
- CVarDef.Create("game.panic_bunker.min_account_age", 1440, CVar.SERVERONLY);
+ ///
+ /// Minimum age of the account (from server's PoV, so from first-seen date) in minutes.
+ ///
+ public static readonly CVarDef PanicBunkerMinAccountAge =
+ CVarDef.Create("game.panic_bunker.min_account_age", 1440, CVar.SERVERONLY);
- ///
- /// Minimal overall played time.
- ///
- public static readonly CVarDef PanicBunkerMinOverallMinutes =
- CVarDef.Create("game.panic_bunker.min_overall_minutes", 600, CVar.SERVERONLY);
+ ///
+ /// Minimal overall played time.
+ ///
+ public static readonly CVarDef PanicBunkerMinOverallMinutes =
+ CVarDef.Create("game.panic_bunker.min_overall_minutes", 600, CVar.SERVERONLY);
- ///
- /// A custom message that will be used for connections denied to the panic bunker
- /// If not empty, then will overwrite
- ///
- public static readonly CVarDef PanicBunkerCustomReason =
- CVarDef.Create("game.panic_bunker.custom_reason", string.Empty, CVar.SERVERONLY);
+ ///
+ /// A custom message that will be used for connections denied to the panic bunker
+ /// If not empty, then will overwrite
+ ///
+ public static readonly CVarDef PanicBunkerCustomReason =
+ CVarDef.Create("game.panic_bunker.custom_reason", string.Empty, CVar.SERVERONLY);
- ///
- /// Allow bypassing the panic bunker if the user is whitelisted.
- ///
- public static readonly CVarDef BypassBunkerWhitelist =
- CVarDef.Create("game.panic_bunker.whitelisted_can_bypass", true, CVar.SERVERONLY);
+ ///
+ /// Allow bypassing the panic bunker if the user is whitelisted.
+ ///
+ public static readonly CVarDef BypassBunkerWhitelist =
+ CVarDef.Create("game.panic_bunker.whitelisted_can_bypass", true, CVar.SERVERONLY);
- ///
- /// Enable IPIntel for blocking VPN connections from new players.
- ///
- public static readonly CVarDef GameIPIntelEnabled =
- CVarDef.Create("game.ipintel_enabled", false, CVar.SERVERONLY);
+ ///
+ /// Enable IPIntel for blocking VPN connections from new players.
+ ///
+ public static readonly CVarDef GameIPIntelEnabled =
+ CVarDef.Create("game.ipintel_enabled", false, CVar.SERVERONLY);
- ///
- /// Whether clients which are flagged as a VPN will be denied
- ///
- public static readonly CVarDef GameIPIntelRejectBad =
- CVarDef.Create("game.ipintel_reject_bad", true, CVar.SERVERONLY);
+ ///
+ /// Whether clients which are flagged as a VPN will be denied
+ ///
+ public static readonly CVarDef GameIPIntelRejectBad =
+ CVarDef.Create("game.ipintel_reject_bad", true, CVar.SERVERONLY);
- ///
- /// Whether clients which cannot be checked due to a rate limit will be denied
- ///
- public static readonly CVarDef GameIPIntelRejectRateLimited =
- CVarDef.Create("game.ipintel_reject_ratelimited", false, CVar.SERVERONLY);
+ ///
+ /// Whether clients which cannot be checked due to a rate limit will be denied
+ ///
+ public static readonly CVarDef GameIPIntelRejectRateLimited =
+ CVarDef.Create("game.ipintel_reject_ratelimited", false, CVar.SERVERONLY);
- ///
- /// Whether clients which cannot be checked due to an error of some form will be denied
- ///
- public static readonly CVarDef GameIPIntelRejectUnknown =
- CVarDef.Create("game.ipintel_reject_unknown", false, CVar.SERVERONLY);
+ ///
+ /// Whether clients which cannot be checked due to an error of some form will be denied
+ ///
+ public static readonly CVarDef GameIPIntelRejectUnknown =
+ CVarDef.Create("game.ipintel_reject_unknown", false, CVar.SERVERONLY);
- ///
- /// Should an admin message be made if the connection got rejected cause of ipintel?
- ///
- public static readonly CVarDef GameIPIntelAlertAdminReject =
- CVarDef.Create("game.ipintel_alert_admin_rejected", false, CVar.SERVERONLY);
+ ///
+ /// Should an admin message be made if the connection got rejected cause of ipintel?
+ ///
+ public static readonly CVarDef GameIPIntelAlertAdminReject =
+ CVarDef.Create("game.ipintel_alert_admin_rejected", false, CVar.SERVERONLY);
- ///
- /// A contact email to be sent along with the request. Required by IPIntel
- ///
- public static readonly CVarDef GameIPIntelEmail =
- CVarDef.Create("game.ipintel_contact_email", string.Empty, CVar.SERVERONLY | CVar.CONFIDENTIAL);
+ ///
+ /// A contact email to be sent along with the request. Required by IPIntel
+ ///
+ public static readonly CVarDef GameIPIntelEmail =
+ CVarDef.Create("game.ipintel_contact_email", string.Empty, CVar.SERVERONLY | CVar.CONFIDENTIAL);
- ///
- /// The URL to IPIntel to make requests to. If you pay for more queries this is what you want to change.
- ///
- public static readonly CVarDef GameIPIntelBase =
- CVarDef.Create("game.ipintel_baseurl", "https://check.getipintel.net", CVar.SERVERONLY);
+ ///
+ /// The URL to IPIntel to make requests to. If you pay for more queries this is what you want to change.
+ ///
+ public static readonly CVarDef GameIPIntelBase =
+ CVarDef.Create("game.ipintel_baseurl", "https://check.getipintel.net", CVar.SERVERONLY);
- ///
- /// The flags to use in the request to IPIntel, please look here for more info. https://getipintel.net/free-proxy-vpn-tor-detection-api/#optional_settings
- /// Note: Some flags may increase the chances of false positives and request time. The default should be fine for most servers.
- ///
- public static readonly CVarDef GameIPIntelFlags =
- CVarDef.Create("game.ipintel_flags", "b", CVar.SERVERONLY);
+ ///
+ /// The flags to use in the request to IPIntel, please look here for more info. https://getipintel.net/free-proxy-vpn-tor-detection-api/#optional_settings
+ /// Note: Some flags may increase the chances of false positives and request time. The default should be fine for most servers.
+ ///
+ public static readonly CVarDef GameIPIntelFlags =
+ CVarDef.Create("game.ipintel_flags", "b", CVar.SERVERONLY);
- ///
- /// Maximum amount of requests per Minute. For free you get 15.
- ///
- public static readonly CVarDef GameIPIntelMaxMinute =
- CVarDef.Create("game.ipintel_request_limit_minute", 15, CVar.SERVERONLY);
+ ///
+ /// Maximum amount of requests per Minute. For free you get 15.
+ ///
+ public static readonly CVarDef GameIPIntelMaxMinute =
+ CVarDef.Create("game.ipintel_request_limit_minute", 15, CVar.SERVERONLY);
- ///
- /// Maximum amount of requests per Day. For free you get 500.
- ///
- public static readonly CVarDef GameIPIntelMaxDay =
- CVarDef.Create("game.ipintel_request_limit_daily", 500, CVar.SERVERONLY);
+ ///
+ /// Maximum amount of requests per Day. For free you get 500.
+ ///
+ public static readonly CVarDef GameIPIntelMaxDay =
+ CVarDef.Create("game.ipintel_request_limit_daily", 500, CVar.SERVERONLY);
- ///
- /// Amount of seconds to add to the exponential backoff with every failed request.
- ///
- public static readonly CVarDef GameIPIntelBackOffSeconds =
- CVarDef.Create("game.ipintel_request_backoff_seconds", 30, CVar.SERVERONLY);
+ ///
+ /// Amount of seconds to add to the exponential backoff with every failed request.
+ ///
+ public static readonly CVarDef GameIPIntelBackOffSeconds =
+ CVarDef.Create("game.ipintel_request_backoff_seconds", 30, CVar.SERVERONLY);
- ///
- /// How much time should pass before we attempt to cleanup the IPIntel table for old ip addresses?
- ///
- public static readonly CVarDef GameIPIntelCleanupMins =
- CVarDef.Create("game.ipintel_database_cleanup_mins", 15, CVar.SERVERONLY);
+ ///
+ /// How much time should pass before we attempt to cleanup the IPIntel table for old ip addresses?
+ ///
+ public static readonly CVarDef GameIPIntelCleanupMins =
+ CVarDef.Create("game.ipintel_database_cleanup_mins", 15, CVar.SERVERONLY);
- ///
- /// How long to store results in the cache before they must be retrieved again in days.
- ///
- public static readonly CVarDef GameIPIntelCacheLength =
- CVarDef.Create("game.ipintel_cache_length", TimeSpan.FromDays(7), CVar.SERVERONLY);
+ ///
+ /// How long to store results in the cache before they must be retrieved again in days.
+ ///
+ public static readonly CVarDef GameIPIntelCacheLength =
+ CVarDef.Create("game.ipintel_cache_length", TimeSpan.FromDays(7), CVar.SERVERONLY);
- ///
- /// Amount of playtime in minutes to be exempt from an IP check. 0 to search everyone. 5 hours by default.
- ///
- /// Trust me you want one.
- /// >
- ///
- public static readonly CVarDef GameIPIntelExemptPlaytime =
- CVarDef.Create("game.ipintel_exempt_playtime", TimeSpan.FromMinutes(300), CVar.SERVERONLY);
+ ///
+ /// Amount of playtime in minutes to be exempt from an IP check. 0 to search everyone. 5 hours by default.
+ ///
+ /// Trust me you want one.
+ /// >
+ ///
+ public static readonly CVarDef GameIPIntelExemptPlaytime =
+ CVarDef.Create("game.ipintel_exempt_playtime", TimeSpan.FromMinutes(300), CVar.SERVERONLY);
- ///
- /// Rating to reject at. Anything equal to or higher than this will reject the connection.
- ///
- public static readonly CVarDef GameIPIntelBadRating =
- CVarDef.Create("game.ipintel_bad_rating", 0.95f, CVar.SERVERONLY);
+ ///
+ /// Rating to reject at. Anything equal to or higher than this will reject the connection.
+ ///
+ public static readonly CVarDef GameIPIntelBadRating =
+ CVarDef.Create("game.ipintel_bad_rating", 0.95f, CVar.SERVERONLY);
- ///
- /// Rating to send an admin warning over, but not reject the connection. Set to 0 to disable
- ///
- public static readonly CVarDef GameIPIntelAlertAdminWarnRating =
- CVarDef.Create("game.ipintel_alert_admin_warn_rating", 0f, CVar.SERVERONLY);
+ ///
+ /// Rating to send an admin warning over, but not reject the connection. Set to 0 to disable
+ ///
+ public static readonly CVarDef GameIPIntelAlertAdminWarnRating =
+ CVarDef.Create("game.ipintel_alert_admin_warn_rating", 0f, CVar.SERVERONLY);
- ///
- /// Make people bonk when trying to climb certain objects like tables.
- ///
- public static readonly CVarDef GameTableBonk =
- CVarDef.Create("game.table_bonk", false, CVar.REPLICATED);
+ ///
+ /// Make people bonk when trying to climb certain objects like tables.
+ ///
+ public static readonly CVarDef GameTableBonk =
+ CVarDef.Create("game.table_bonk", false, CVar.REPLICATED);
- ///
- /// Whether or not status icons are rendered for everyone.
- ///
- public static readonly CVarDef GlobalStatusIconsEnabled =
- CVarDef.Create("game.global_status_icons_enabled", true, CVar.SERVER | CVar.REPLICATED);
+ ///
+ /// Whether or not status icons are rendered for everyone.
+ ///
+ public static readonly CVarDef GlobalStatusIconsEnabled =
+ CVarDef.Create("game.global_status_icons_enabled", true, CVar.SERVER | CVar.REPLICATED);
- ///
- /// Whether or not status icons are rendered on this specific client.
- ///
- public static readonly CVarDef LocalStatusIconsEnabled =
- CVarDef.Create("game.local_status_icons_enabled", true, CVar.CLIENTONLY);
+ ///
+ /// Whether or not status icons are rendered on this specific client.
+ ///
+ public static readonly CVarDef LocalStatusIconsEnabled =
+ CVarDef.Create("game.local_status_icons_enabled", true, CVar.CLIENTONLY);
- ///
- /// Whether or not coordinates on the Debug overlay should only be available to admins.
- ///
- public static readonly CVarDef DebugCoordinatesAdminOnly =
- CVarDef.Create("game.debug_coordinates_admin_only", true, CVar.SERVER | CVar.REPLICATED);
+ ///
+ /// Whether or not coordinates on the Debug overlay should only be available to admins.
+ ///
+ public static readonly CVarDef DebugCoordinatesAdminOnly =
+ CVarDef.Create("game.debug_coordinates_admin_only", true, CVar.SERVER | CVar.REPLICATED);
#if EXCEPTION_TOLERANCE
- ///
- /// Amount of times round start must fail before the server is shut down.
- /// Set to 0 or a negative number to disable.
- ///
- public static readonly CVarDef RoundStartFailShutdownCount =
- CVarDef.Create("game.round_start_fail_shutdown_count", 5, CVar.SERVERONLY | CVar.SERVER);
+ ///
+ /// Amount of times round start must fail before the server is shut down.
+ /// Set to 0 or a negative number to disable.
+ ///
+ public static readonly CVarDef RoundStartFailShutdownCount =
+ CVarDef.Create("game.round_start_fail_shutdown_count", 5, CVar.SERVERONLY | CVar.SERVER);
#endif
- ///
- /// Delay between station alert level changes.
- ///
- public static readonly CVarDef GameAlertLevelChangeDelay =
- CVarDef.Create("game.alert_level_change_delay", 30, CVar.SERVERONLY);
+ ///
+ /// Delay between station alert level changes.
+ ///
+ public static readonly CVarDef GameAlertLevelChangeDelay =
+ CVarDef.Create("game.alert_level_change_delay", 30, CVar.SERVERONLY);
- ///
- /// The time in seconds that the server should wait before restarting the round.
- /// Defaults to 2 minutes.
- ///
- public static readonly CVarDef RoundRestartTime =
- CVarDef.Create("game.round_restart_time", 120f, CVar.SERVERONLY);
+ ///
+ /// The time in seconds that the server should wait before restarting the round.
+ /// Defaults to 2 minutes.
+ ///
+ public static readonly CVarDef RoundRestartTime =
+ CVarDef.Create("game.round_restart_time", 120f, CVar.SERVERONLY);
- ///
- /// The prototype to use for secret weights.
- ///
- public static readonly CVarDef SecretWeightPrototype =
- CVarDef.Create("game.secret_weight_prototype", "Secret", CVar.SERVERONLY);
+ ///
+ /// The prototype to use for secret weights.
+ ///
+ public static readonly CVarDef SecretWeightPrototype =
+ CVarDef.Create("game.secret_weight_prototype", "Secret", CVar.SERVERONLY);
- ///
- /// The id of the sound collection to randomly choose a sound from and play when the round ends.
- ///
- public static readonly CVarDef RoundEndSoundCollection =
- CVarDef.Create("game.round_end_sound_collection", "RoundEnd", CVar.SERVERONLY);
+ ///
+ /// The id of the sound collection to randomly choose a sound from and play when the round ends.
+ ///
+ public static readonly CVarDef RoundEndSoundCollection =
+ CVarDef.Create("game.round_end_sound_collection", "RoundEnd", CVar.SERVERONLY);
- ///
- /// Whether or not to add every player as a global override to PVS at round end.
- /// This will allow all players to see their clothing in the round screen player list screen,
- /// but may cause lag during round end with very high player counts.
- ///
- public static readonly CVarDef RoundEndPVSOverrides =
- CVarDef.Create("game.round_end_pvs_overrides", true, CVar.SERVERONLY);
+ ///