diff --git a/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs b/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs index 8e527d7343..a783dd368f 100644 --- a/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs +++ b/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs @@ -25,7 +25,7 @@ namespace Content.Client.Access.UI public void SetAccessLevels(IPrototypeManager protoManager, List> accessLevels) { _accessButtons.Clear(); - AccessLevelGrid.DisposeAllChildren(); + AccessLevelGrid.RemoveAllChildren(); foreach (var access in accessLevels) { diff --git a/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs index 320bb88a67..209c58c950 100644 --- a/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs +++ b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs @@ -41,7 +41,7 @@ namespace Content.Client.Access.UI public void SetAllowedIcons(string currentJobIconId) { - IconGrid.DisposeAllChildren(); + IconGrid.RemoveAllChildren(); var jobIconButtonGroup = new ButtonGroup(); var i = 0; diff --git a/Content.Client/Access/UI/GroupedAccessLevelChecklist.xaml.cs b/Content.Client/Access/UI/GroupedAccessLevelChecklist.xaml.cs index 41d5a84654..7af78d9e5f 100644 --- a/Content.Client/Access/UI/GroupedAccessLevelChecklist.xaml.cs +++ b/Content.Client/Access/UI/GroupedAccessLevelChecklist.xaml.cs @@ -99,8 +99,8 @@ public sealed partial class GroupedAccessLevelChecklist : BoxContainer private bool TryRebuildAccessGroupControls() { - AccessGroupList.DisposeAllChildren(); - AccessLevelChecklist.DisposeAllChildren(); + AccessGroupList.RemoveAllChildren(); + AccessLevelChecklist.RemoveAllChildren(); // No access level prototypes were assigned to any of the access level groups. // Either the turret controller has no assigned access levels or their names were invalid. @@ -165,7 +165,7 @@ public sealed partial class GroupedAccessLevelChecklist : BoxContainer /// public void RebuildAccessLevelsControls() { - AccessLevelChecklist.DisposeAllChildren(); + AccessLevelChecklist.RemoveAllChildren(); _accessLevelEntries.Clear(); // No access level prototypes were assigned to any of the access level groups diff --git a/Content.Client/Actions/ActionsSystem.cs b/Content.Client/Actions/ActionsSystem.cs index 8efe0b2367..49d90dedaf 100644 --- a/Content.Client/Actions/ActionsSystem.cs +++ b/Content.Client/Actions/ActionsSystem.cs @@ -33,6 +33,7 @@ namespace Content.Client.Actions [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly IResourceManager _resources = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly ISerializationManager _serialization = default!; public event Action? OnActionAdded; public event Action? OnActionRemoved; @@ -286,8 +287,27 @@ namespace Content.Client.Actions continue; } + if (assignmentNode is SequenceDataNode sequenceAssignments) + { + try + { + var nodeAssignments = _serialization.Read>(sequenceAssignments, notNullableOverride: true); + + foreach (var index in nodeAssignments) + { + assignments.Add(new SlotAssignment(index.Hotbar, index.Slot, actionId)); + } + } + catch (Exception ex) + { + Log.Error($"Failed to parse action assignments: {ex}"); + } + } + AddActionDirect((user, actions), actionId); } + + AssignSlot?.Invoke(assignments); } private void OnWorldTargetAttempt(Entity ent, ref ActionTargetAttemptEvent args) @@ -309,10 +329,10 @@ namespace Content.Client.Actions // this is the actual entity-world targeting magic EntityUid? targetEnt = null; if (TryComp(ent, out var entity) && - args.Input.EntityUid != null && - ValidateEntityTarget(user, args.Input.EntityUid, (uid, entity))) + args.Input.EntityUid is { Valid: true } entityUid && + ValidateEntityTarget(user, entityUid, (uid, entity))) { - targetEnt = args.Input.EntityUid; + targetEnt = entityUid; } if (action.ClientExclusive) diff --git a/Content.Client/Administration/UI/AdminCamera/AdminCameraWindow.xaml b/Content.Client/Administration/UI/AdminCamera/AdminCameraWindow.xaml index 87583cef97..a6ac34bb29 100644 --- a/Content.Client/Administration/UI/AdminCamera/AdminCameraWindow.xaml +++ b/Content.Client/Administration/UI/AdminCamera/AdminCameraWindow.xaml @@ -1,6 +1,5 @@ + MinSize="200 225"> diff --git a/Content.Client/Administration/UI/ManageSolutions/EditSolutionsWindow.xaml.cs b/Content.Client/Administration/UI/ManageSolutions/EditSolutionsWindow.xaml.cs index a6b61a4393..89016fdf41 100644 --- a/Content.Client/Administration/UI/ManageSolutions/EditSolutionsWindow.xaml.cs +++ b/Content.Client/Administration/UI/ManageSolutions/EditSolutionsWindow.xaml.cs @@ -67,7 +67,7 @@ namespace Content.Client.Administration.UI.ManageSolutions /// public void UpdateReagents() { - ReagentList.DisposeAllChildren(); + ReagentList.RemoveAllChildren(); if (_selectedSolution == null || _solutions == null) return; @@ -92,7 +92,7 @@ namespace Content.Client.Administration.UI.ManageSolutions /// The selected solution. private void UpdateVolumeBox(Solution solution) { - VolumeBox.DisposeAllChildren(); + VolumeBox.RemoveAllChildren(); var volumeLabel = new Label(); volumeLabel.HorizontalExpand = true; @@ -131,7 +131,7 @@ namespace Content.Client.Administration.UI.ManageSolutions /// The selected solution. private void UpdateThermalBox(Solution solution) { - ThermalBox.DisposeAllChildren(); + ThermalBox.RemoveAllChildren(); var heatCap = solution.GetHeatCapacity(null); var specificHeatLabel = new Label(); specificHeatLabel.HorizontalExpand = true; diff --git a/Content.Client/Cargo/UI/CargoConsoleMenu.xaml.cs b/Content.Client/Cargo/UI/CargoConsoleMenu.xaml.cs index 03246cfdfe..624ab36125 100644 --- a/Content.Client/Cargo/UI/CargoConsoleMenu.xaml.cs +++ b/Content.Client/Cargo/UI/CargoConsoleMenu.xaml.cs @@ -206,7 +206,7 @@ namespace Content.Client.Cargo.UI if (!_orderConsoleQuery.TryComp(_owner, out var orderConsole)) return; - Requests.DisposeAllChildren(); + Requests.RemoveAllChildren(); foreach (var order in orders) { diff --git a/Content.Client/Cargo/UI/CargoShuttleMenu.xaml.cs b/Content.Client/Cargo/UI/CargoShuttleMenu.xaml.cs index c1ffed0783..970051432b 100644 --- a/Content.Client/Cargo/UI/CargoShuttleMenu.xaml.cs +++ b/Content.Client/Cargo/UI/CargoShuttleMenu.xaml.cs @@ -30,7 +30,7 @@ namespace Content.Client.Cargo.UI public void SetOrders(SpriteSystem sprites, IPrototypeManager protoManager, List orders) { - Orders.DisposeAllChildren(); + Orders.RemoveAllChildren(); foreach (var order in orders) { diff --git a/Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml.cs b/Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml.cs index 27ddd51815..4daab3d27c 100644 --- a/Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml.cs +++ b/Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml.cs @@ -1,4 +1,4 @@ -using Content.Client.CrewManifest.UI; +using Content.Client.CrewManifest.UI; using Content.Shared.CrewManifest; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.Controls; @@ -21,7 +21,6 @@ public sealed partial class CrewManifestUiFragment : BoxContainer public void UpdateState(string stationName, CrewManifestEntries? entries) { - CrewManifestListing.DisposeAllChildren(); CrewManifestListing.RemoveAllChildren(); StationNameContainer.Visible = entries != null; diff --git a/Content.Client/Changelog/ChangelogWindow.xaml.cs b/Content.Client/Changelog/ChangelogWindow.xaml.cs index d82c34254c..d8f560f151 100644 --- a/Content.Client/Changelog/ChangelogWindow.xaml.cs +++ b/Content.Client/Changelog/ChangelogWindow.xaml.cs @@ -55,7 +55,7 @@ namespace Content.Client.Changelog // Changelog is not kept in memory so load it again. var changelogs = await _changelog.LoadChangelog(); - Tabs.DisposeAllChildren(); + Tabs.RemoveAllChildren(); var i = 0; foreach (var changelog in changelogs) diff --git a/Content.Client/ContextMenu/UI/ContextMenuUIController.cs b/Content.Client/ContextMenu/UI/ContextMenuUIController.cs index 1b83f5ed03..ca173ff1e1 100644 --- a/Content.Client/ContextMenu/UI/ContextMenuUIController.cs +++ b/Content.Client/ContextMenu/UI/ContextMenuUIController.cs @@ -95,7 +95,7 @@ namespace Content.Client.ContextMenu.UI /// public void Close() { - RootMenu.MenuBody.DisposeAllChildren(); + RootMenu.MenuBody.RemoveAllChildren(); CancelOpen?.Cancel(); CancelClose?.Cancel(); OnContextClosed?.Invoke(); diff --git a/Content.Client/ContextMenu/UI/EntityMenuUIController.cs b/Content.Client/ContextMenu/UI/EntityMenuUIController.cs index e0a88300db..1855911ca4 100644 --- a/Content.Client/ContextMenu/UI/EntityMenuUIController.cs +++ b/Content.Client/ContextMenu/UI/EntityMenuUIController.cs @@ -293,7 +293,7 @@ namespace Content.Client.ContextMenu.UI var element = new EntityMenuElement(entity); element.SubMenu = new ContextMenuPopup(_context, element); element.SubMenu.OnPopupOpen += () => _verb.OpenVerbMenu(entity, popup: element.SubMenu); - element.SubMenu.OnPopupHide += element.SubMenu.MenuBody.DisposeAllChildren; + element.SubMenu.OnPopupHide += element.SubMenu.MenuBody.RemoveAllChildren; _context.AddElement(menu, element); Elements.TryAdd(entity, element); } diff --git a/Content.Client/Crayon/UI/CrayonWindow.xaml.cs b/Content.Client/Crayon/UI/CrayonWindow.xaml.cs index 88475562c6..f1ac5a79cb 100644 --- a/Content.Client/Crayon/UI/CrayonWindow.xaml.cs +++ b/Content.Client/Crayon/UI/CrayonWindow.xaml.cs @@ -53,7 +53,7 @@ namespace Content.Client.Crayon.UI private void RefreshList() { // Clear - Grids.DisposeAllChildren(); + Grids.RemoveAllChildren(); if (_decals == null || _allDecals == null) return; diff --git a/Content.Client/CrewManifest/CrewManifestUi.xaml.cs b/Content.Client/CrewManifest/CrewManifestUi.xaml.cs index f07e54eb65..3c13681b97 100644 --- a/Content.Client/CrewManifest/CrewManifestUi.xaml.cs +++ b/Content.Client/CrewManifest/CrewManifestUi.xaml.cs @@ -18,7 +18,6 @@ public sealed partial class CrewManifestUi : DefaultWindow public void Populate(string name, CrewManifestEntries? entries) { - CrewManifestListing.DisposeAllChildren(); CrewManifestListing.RemoveAllChildren(); StationNameContainer.Visible = entries != null; diff --git a/Content.Client/Gateway/UI/GatewayWindow.xaml.cs b/Content.Client/Gateway/UI/GatewayWindow.xaml.cs index 1c779b2b35..9fb7c339d3 100644 --- a/Content.Client/Gateway/UI/GatewayWindow.xaml.cs +++ b/Content.Client/Gateway/UI/GatewayWindow.xaml.cs @@ -72,7 +72,7 @@ public sealed partial class GatewayWindow : FancyWindow, _isUnlockPending = _nextUnlock >= _timing.CurTime; _isCooldownPending = _nextReady >= _timing.CurTime; - Container.DisposeAllChildren(); + Container.RemoveAllChildren(); if (_destinations.Count == 0) { diff --git a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs index fd3615d59f..225619b031 100644 --- a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs +++ b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs @@ -116,7 +116,7 @@ namespace Content.Client.HealthAnalyzer.UI AlertsContainer.Visible = showAlerts; if (showAlerts) - AlertsContainer.DisposeAllChildren(); + AlertsContainer.RemoveAllChildren(); if (msg.Unrevivable == true) AlertsContainer.AddChild(new RichTextLabel diff --git a/Content.Client/Humanoid/MarkingPicker.xaml.cs b/Content.Client/Humanoid/MarkingPicker.xaml.cs index ad6671511a..7a591a46aa 100644 --- a/Content.Client/Humanoid/MarkingPicker.xaml.cs +++ b/Content.Client/Humanoid/MarkingPicker.xaml.cs @@ -416,7 +416,7 @@ public sealed partial class MarkingPicker : Control var stateNames = GetMarkingStateNames(prototype); _currentMarkingColors.Clear(); - CMarkingColors.DisposeAllChildren(); + CMarkingColors.RemoveAllChildren(); List colorSliders = new(); for (int i = 0; i < prototype.Sprites.Count; i++) { diff --git a/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs b/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs index f70468fcf4..95613faf40 100644 --- a/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs +++ b/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs @@ -226,7 +226,6 @@ public sealed partial class SingleMarkingPicker : BoxContainer var marking = _markings[Slot]; - ColorSelectorContainer.DisposeAllChildren(); ColorSelectorContainer.RemoveAllChildren(); if (marking.MarkingColors.Count != proto.Sprites.Count) diff --git a/Content.Client/IdentityManagement/IdentitySystem.cs b/Content.Client/IdentityManagement/IdentitySystem.cs deleted file mode 100644 index 15d4ee20e9..0000000000 --- a/Content.Client/IdentityManagement/IdentitySystem.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Content.Shared.IdentityManagement; - -namespace Content.Client.IdentityManagement; - -public sealed class IdentitySystem : SharedIdentitySystem -{ -} diff --git a/Content.Client/Lobby/UI/CharacterSetupGui.xaml.cs b/Content.Client/Lobby/UI/CharacterSetupGui.xaml.cs index 7709a517eb..f821652146 100644 --- a/Content.Client/Lobby/UI/CharacterSetupGui.xaml.cs +++ b/Content.Client/Lobby/UI/CharacterSetupGui.xaml.cs @@ -80,7 +80,7 @@ namespace Content.Client.Lobby.UI public void ReloadCharacterPickers() { _createNewCharacterButton.Orphan(); - Characters.DisposeAllChildren(); + Characters.RemoveAllChildren(); var numberOfFullSlots = 0; var characterButtonsGroup = new ButtonGroup(); diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs index 54dbc9eda6..af402198a2 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs @@ -505,7 +505,7 @@ namespace Content.Client.Lobby.UI /// public void RefreshTraits() { - TraitsList.DisposeAllChildren(); + TraitsList.RemoveAllChildren(); var traits = _prototypeManager.EnumeratePrototypes().OrderBy(t => Loc.GetString(t.Name)).ToList(); TabContainer.SetTabTitle(3, Loc.GetString("humanoid-profile-editor-traits-tab")); @@ -650,7 +650,7 @@ namespace Content.Client.Lobby.UI public void RefreshAntags() { - AntagList.DisposeAllChildren(); + AntagList.RemoveAllChildren(); var items = new[] { ("humanoid-profile-editor-antag-preference-yes-button", 0), @@ -845,7 +845,7 @@ namespace Content.Client.Lobby.UI /// public void RefreshJobs() { - JobList.DisposeAllChildren(); + JobList.RemoveAllChildren(); _jobCategories.Clear(); _jobPriorities.Clear(); var firstCategory = true; diff --git a/Content.Client/Lobby/UI/Loadouts/LoadoutGroupContainer.xaml.cs b/Content.Client/Lobby/UI/Loadouts/LoadoutGroupContainer.xaml.cs index 2c71124663..f000dd6fb1 100644 --- a/Content.Client/Lobby/UI/Loadouts/LoadoutGroupContainer.xaml.cs +++ b/Content.Client/Lobby/UI/Loadouts/LoadoutGroupContainer.xaml.cs @@ -43,7 +43,7 @@ public sealed partial class LoadoutGroupContainer : BoxContainer { var protoMan = collection.Resolve(); var loadoutSystem = collection.Resolve().System(); - RestrictionsContainer.DisposeAllChildren(); + RestrictionsContainer.RemoveAllChildren(); if (_groupProto.MinLimit > 0) { @@ -72,7 +72,7 @@ public sealed partial class LoadoutGroupContainer : BoxContainer }); } - LoadoutsContainer.DisposeAllChildren(); + LoadoutsContainer.RemoveAllChildren(); // Corvax-Loadouts-Start var groupLoadouts = _groupProto.Loadouts; diff --git a/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.xaml.cs b/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.xaml.cs index 619cac6839..9b1e7d50f8 100644 --- a/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.xaml.cs +++ b/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.xaml.cs @@ -43,7 +43,7 @@ public sealed partial class LobbyCharacterPreviewPanel : Control _previewDummy = uid; - ViewBox.DisposeAllChildren(); + ViewBox.RemoveAllChildren(); var spriteView = new SpriteView { OverrideDirection = Direction.South, diff --git a/Content.Client/Mapping/MappingPrototypeList.xaml.cs b/Content.Client/Mapping/MappingPrototypeList.xaml.cs index 8b59e6eb6f..13c92c4516 100644 --- a/Content.Client/Mapping/MappingPrototypeList.xaml.cs +++ b/Content.Client/Mapping/MappingPrototypeList.xaml.cs @@ -1,4 +1,4 @@ -using System.Numerics; +using System.Numerics; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; using Robust.Client.UserInterface; @@ -37,7 +37,7 @@ public sealed partial class MappingPrototypeList : Control { _prototypes.Clear(); - PrototypeList.DisposeAllChildren(); + PrototypeList.RemoveAllChildren(); _prototypes.AddRange(prototypes); @@ -99,7 +99,7 @@ public sealed partial class MappingPrototypeList : Control public void Search(List prototypes) { _search.Clear(); - SearchList.DisposeAllChildren(); + SearchList.RemoveAllChildren(); _lastIndices = (0, -1); _search.AddRange(prototypes); diff --git a/Content.Client/Mapping/MappingState.cs b/Content.Client/Mapping/MappingState.cs index 97fbee70bc..27440607cb 100644 --- a/Content.Client/Mapping/MappingState.cs +++ b/Content.Client/Mapping/MappingState.cs @@ -861,7 +861,7 @@ public sealed class MappingState : GameplayStateBase } else { - button.ChildrenPrototypes.DisposeAllChildren(); + button.ChildrenPrototypes.RemoveAllChildren(); button.CollapseButton.Label.Text = "▶"; } } diff --git a/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs b/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs index e2b27c1b62..209c1cd32b 100644 --- a/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs +++ b/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs @@ -53,8 +53,8 @@ public sealed partial class PowerMonitoringWindow // Selection action windowEntry.Button.OnButtonUp += args => { - windowEntry.SourcesContainer.DisposeAllChildren(); - windowEntry.LoadsContainer.DisposeAllChildren(); + windowEntry.SourcesContainer.RemoveAllChildren(); + windowEntry.LoadsContainer.RemoveAllChildren(); ButtonAction(windowEntry, masterContainer); }; } diff --git a/Content.Client/Salvage/UI/OfferingWindow.xaml.cs b/Content.Client/Salvage/UI/OfferingWindow.xaml.cs index 2b607b8213..3b12a31c77 100644 --- a/Content.Client/Salvage/UI/OfferingWindow.xaml.cs +++ b/Content.Client/Salvage/UI/OfferingWindow.xaml.cs @@ -70,7 +70,7 @@ public sealed partial class OfferingWindow : FancyWindow, public void ClearOptions() { - Container.DisposeAllChildren(); + Container.RemoveAllChildren(); } protected override void FrameUpdate(FrameEventArgs args) diff --git a/Content.Client/Shuttles/UI/DockingScreen.xaml.cs b/Content.Client/Shuttles/UI/DockingScreen.xaml.cs index 97943c6973..97125d3159 100644 --- a/Content.Client/Shuttles/UI/DockingScreen.xaml.cs +++ b/Content.Client/Shuttles/UI/DockingScreen.xaml.cs @@ -67,8 +67,8 @@ public sealed partial class DockingScreen : BoxContainer { DockingControl.BuildDocks(shuttle); var currentDock = DockingControl.ViewedDock; - // DockedWith.DisposeAllChildren(); - DockPorts.DisposeAllChildren(); + // DockedWith.RemoveAllChildren(); + DockPorts.RemoveAllChildren(); _ourDockButtons.Clear(); if (shuttle == null) diff --git a/Content.Client/Shuttles/UI/EmergencyConsoleWindow.xaml.cs b/Content.Client/Shuttles/UI/EmergencyConsoleWindow.xaml.cs index 87103084b4..f9a7f02d7e 100644 --- a/Content.Client/Shuttles/UI/EmergencyConsoleWindow.xaml.cs +++ b/Content.Client/Shuttles/UI/EmergencyConsoleWindow.xaml.cs @@ -59,7 +59,7 @@ public sealed partial class EmergencyConsoleWindow : FancyWindow, // TODO: Loc and cvar for this. _earlyLaunchTime = scc.EarlyLaunchTime; - AuthorizationsContainer.DisposeAllChildren(); + AuthorizationsContainer.RemoveAllChildren(); var remainingAuths = scc.AuthorizationsRequired - scc.Authorizations.Count; AuthorizationCount.Text = Loc.GetString("emergency-shuttle-ui-remaining", ("remaining", remainingAuths)); diff --git a/Content.Client/Shuttles/UI/MapScreen.xaml.cs b/Content.Client/Shuttles/UI/MapScreen.xaml.cs index 0d7df38b91..72ad3c28b1 100644 --- a/Content.Client/Shuttles/UI/MapScreen.xaml.cs +++ b/Content.Client/Shuttles/UI/MapScreen.xaml.cs @@ -237,7 +237,7 @@ public sealed partial class MapScreen : BoxContainer private void ClearMapObjects() { _mapObjectControls.Clear(); - HyperspaceDestinations.DisposeAllChildren(); + HyperspaceDestinations.RemoveAllChildren(); _pendingMapObjects.Clear(); _mapObjects.Clear(); _mapHeadings.Clear(); diff --git a/Content.Client/Strip/StrippingMenu.cs b/Content.Client/Strip/StrippingMenu.cs index 531e5fb44d..124356e011 100644 --- a/Content.Client/Strip/StrippingMenu.cs +++ b/Content.Client/Strip/StrippingMenu.cs @@ -25,9 +25,9 @@ namespace Content.Client.Strip public void ClearButtons() { - InventoryContainer.DisposeAllChildren(); - HandsContainer.DisposeAllChildren(); - SnareContainer.DisposeAllChildren(); + InventoryContainer.RemoveAllChildren(); + HandsContainer.RemoveAllChildren(); + SnareContainer.RemoveAllChildren(); } protected override void FrameUpdate(FrameEventArgs args) diff --git a/Content.Client/Thief/ThiefBackpackMenu.xaml.cs b/Content.Client/Thief/ThiefBackpackMenu.xaml.cs index aaee357617..328ca45b4b 100644 --- a/Content.Client/Thief/ThiefBackpackMenu.xaml.cs +++ b/Content.Client/Thief/ThiefBackpackMenu.xaml.cs @@ -29,7 +29,7 @@ public sealed partial class ThiefBackpackMenu : FancyWindow public void UpdateState(ThiefBackpackBoundUserInterfaceState state) { - SetsGrid.DisposeAllChildren(); + SetsGrid.RemoveAllChildren(); var selectedNumber = 0; foreach (var (set, info) in state.Sets) { diff --git a/Content.Client/UserInterface/Controls/MainViewport.cs b/Content.Client/UserInterface/Controls/MainViewport.cs index 0e947da7cf..5fed4379cf 100644 --- a/Content.Client/UserInterface/Controls/MainViewport.cs +++ b/Content.Client/UserInterface/Controls/MainViewport.cs @@ -66,7 +66,8 @@ namespace Content.Client.UserInterface.Controls Viewport.StretchMode = filterMode switch { "nearest" => ScalingViewportStretchMode.Nearest, - "bilinear" => ScalingViewportStretchMode.Bilinear + "bilinear" => ScalingViewportStretchMode.Bilinear, + _ => ScalingViewportStretchMode.Nearest }; Viewport.IgnoreDimension = verticalFit ? ScalingViewportIgnoreDimension.Horizontal : ScalingViewportIgnoreDimension.None; diff --git a/Content.Client/UserInterface/Controls/SplitBar.xaml.cs b/Content.Client/UserInterface/Controls/SplitBar.xaml.cs index 2c0b716448..2f69b15499 100644 --- a/Content.Client/UserInterface/Controls/SplitBar.xaml.cs +++ b/Content.Client/UserInterface/Controls/SplitBar.xaml.cs @@ -1,4 +1,4 @@ -using System.Numerics; +using System.Numerics; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; @@ -19,7 +19,7 @@ namespace Content.Client.UserInterface.Controls public void Clear() { - DisposeAllChildren(); + RemoveAllChildren(); } public void AddEntry(float amount, Color color, string? tooltip = null) diff --git a/Content.Client/UserInterface/StatsWindow.xaml.cs b/Content.Client/UserInterface/StatsWindow.xaml.cs index 29c48fff67..5684be9e5b 100644 --- a/Content.Client/UserInterface/StatsWindow.xaml.cs +++ b/Content.Client/UserInterface/StatsWindow.xaml.cs @@ -17,7 +17,7 @@ namespace Content.Client.UserInterface public void UpdateValues(List headers, List values) { - Values.DisposeAllChildren(); + Values.RemoveAllChildren(); Values.Columns = headers.Count; for (var i = 0; i < headers.Count; i++) diff --git a/Content.Client/UserInterface/Systems/Ghost/Controls/GhostTargetWindow.xaml.cs b/Content.Client/UserInterface/Systems/Ghost/Controls/GhostTargetWindow.xaml.cs index 3ef69578a0..17b4e04d24 100644 --- a/Content.Client/UserInterface/Systems/Ghost/Controls/GhostTargetWindow.xaml.cs +++ b/Content.Client/UserInterface/Systems/Ghost/Controls/GhostTargetWindow.xaml.cs @@ -45,7 +45,7 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls public void Populate() { - ButtonContainer.DisposeAllChildren(); + ButtonContainer.RemoveAllChildren(); AddButtons(); } diff --git a/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesWindow.xaml.cs b/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesWindow.xaml.cs index 9e2ff816b3..dd5e7e6a9b 100644 --- a/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesWindow.xaml.cs +++ b/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/GhostRolesWindow.xaml.cs @@ -26,7 +26,7 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles public void ClearEntries() { NoRolesMessage.Visible = true; - EntryContainer.DisposeAllChildren(); + EntryContainer.RemoveAllChildren(); _collapsibleBoxes.Clear(); } diff --git a/Content.Client/UserInterface/Systems/Hands/Controls/HandsContainer.cs b/Content.Client/UserInterface/Systems/Hands/Controls/HandsContainer.cs index 1421e302b8..d2f24abd6c 100644 --- a/Content.Client/UserInterface/Systems/Hands/Controls/HandsContainer.cs +++ b/Content.Client/UserInterface/Systems/Hands/Controls/HandsContainer.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using Content.Client.UserInterface.Systems.Inventory.Controls; using Robust.Client.UserInterface.Controls; @@ -74,7 +74,7 @@ public sealed class HandsContainer : ItemSlotUIContainer public void Clear() { ClearButtons(); - _grid.DisposeAllChildren(); + _grid.RemoveAllChildren(); } public IEnumerable GetButtons() diff --git a/Content.Client/Verbs/UI/VerbMenuUIController.cs b/Content.Client/Verbs/UI/VerbMenuUIController.cs index 32da302904..a45dc90cb7 100644 --- a/Content.Client/Verbs/UI/VerbMenuUIController.cs +++ b/Content.Client/Verbs/UI/VerbMenuUIController.cs @@ -109,7 +109,7 @@ namespace Content.Client.Verbs.UI Close(); var menu = popup ?? _context.RootMenu; - menu.MenuBody.DisposeAllChildren(); + menu.MenuBody.RemoveAllChildren(); CurrentTarget = target; CurrentVerbs = _verbSystem.GetVerbs(target, user, Verb.VerbTypes, out ExtraCategories, force); @@ -207,7 +207,7 @@ namespace Content.Client.Verbs.UI /// public void AddServerVerbs(List? verbs, ContextMenuPopup popup) { - popup.MenuBody.DisposeAllChildren(); + popup.MenuBody.RemoveAllChildren(); // Verbs may be null if the server does not think we can see the target entity. This **should** not happen. if (verbs == null) diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index 1222096e01..9b0e7729f5 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -271,7 +271,7 @@ namespace Content.IntegrationTests.Tests // We consider only non-audio entities, as some entities will just play sounds when they spawn. int Count(IEntityManager ent) => ent.EntityCount - ent.Count(); - IEnumerable Entities(IEntityManager entMan) => entMan.GetEntities().Where(entMan.HasComponent); + IEnumerable Entities(IEntityManager entMan) => entMan.GetEntities().Where(e => !entMan.HasComponent(e)); await Assert.MultipleAsync(async () => { @@ -311,8 +311,8 @@ namespace Content.IntegrationTests.Tests // Check that the number of entities has gone back to the original value. Assert.That(Count(server.EntMan), Is.EqualTo(count), $"Server prototype {protoId} failed on deletion: count didn't reset properly\n" + BuildDiffString(serverEntities, Entities(server.EntMan), server.EntMan)); - Assert.That(client.EntMan.EntityCount, Is.EqualTo(clientCount), $"Client prototype {protoId} failed on deletion: count didn't reset properly:\n" + - $"Expected {clientCount} and found {client.EntMan.EntityCount}.\n" + + Assert.That(Count(client.EntMan), Is.EqualTo(clientCount), $"Client prototype {protoId} failed on deletion: count didn't reset properly:\n" + + $"Expected {clientCount} and found {Count(client.EntMan)}.\n" + $"Server count was {count}.\n" + BuildDiffString(clientEntities, Entities(client.EntMan), client.EntMan)); } diff --git a/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs b/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs index 0d281d7075..f734d3eb3e 100644 --- a/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs +++ b/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs @@ -1,9 +1,9 @@ using System.Linq; using Content.Server.Emp; -using Content.Server.IdentityManagement; using Content.Shared.Clothing.Components; using Content.Shared.Clothing.EntitySystems; using Content.Shared.Emp; +using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement.Components; using Content.Shared.Inventory; using Content.Shared.Prototypes; diff --git a/Content.Server/CriminalRecords/Systems/CriminalRecordsConsoleSystem.cs b/Content.Server/CriminalRecords/Systems/CriminalRecordsConsoleSystem.cs index 72d66c7638..1b7e50c651 100644 --- a/Content.Server/CriminalRecords/Systems/CriminalRecordsConsoleSystem.cs +++ b/Content.Server/CriminalRecords/Systems/CriminalRecordsConsoleSystem.cs @@ -269,31 +269,4 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS mob = user; return true; } - - /// - /// Checks if the new identity's name has a criminal record attached to it, and gives the entity the icon that - /// belongs to the status if it does. - /// - public void CheckNewIdentity(EntityUid uid) - { - var name = Identity.Name(uid, EntityManager); - var xform = Transform(uid); - - // TODO use the entity's station? Not the station of the map that it happens to currently be on? - var station = _station.GetStationInMap(xform.MapID); - - if (station != null && _records.GetRecordByName(station.Value, name) is { } id) - { - if (_records.TryGetRecord(new StationRecordKey(id, station.Value), - out var record)) - { - if (record.Status != SecurityStatus.None) - { - _criminalRecords.SetCriminalIcon(name, record.Status, uid); - return; - } - } - } - RemComp(uid); - } } diff --git a/Content.Server/Delivery/DeliverySystem.Spawning.cs b/Content.Server/Delivery/DeliverySystem.Spawning.cs index a7496a343b..14662e58c6 100644 --- a/Content.Server/Delivery/DeliverySystem.Spawning.cs +++ b/Content.Server/Delivery/DeliverySystem.Spawning.cs @@ -1,7 +1,7 @@ using Content.Shared.Delivery; using Content.Shared.Power.EntitySystems; -using Content.Server.StationRecords; using Content.Shared.EntityTable; +using Content.Shared.StationRecords; using Robust.Shared.Random; using Robust.Shared.Timing; diff --git a/Content.Server/DeviceNetwork/Systems/DeviceNetworkJammerSystem.cs b/Content.Server/DeviceNetwork/Systems/DeviceNetworkJammerSystem.cs index 1905b752b8..860ff886d4 100644 --- a/Content.Server/DeviceNetwork/Systems/DeviceNetworkJammerSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/DeviceNetworkJammerSystem.cs @@ -30,6 +30,10 @@ public sealed class DeviceNetworkJammerSystem : SharedDeviceNetworkJammerSystem if (!_jammer.GetJammableNetworks((uid, jammerComp)).Contains(ev.NetworkId)) continue; + if (jammerComp.FrequenciesExcluded != null && + jammerComp.FrequenciesExcluded.Contains(ev.Frequency)) + continue; + if (_transform.InRange(jammerXform.Coordinates, ev.SenderTransform.Coordinates, jammerComp.Range) || _transform.InRange(jammerXform.Coordinates, xform.Comp.Coordinates, jammerComp.Range)) { diff --git a/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs b/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs index 4b28fd9bf9..b2a648fc63 100644 --- a/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs @@ -349,7 +349,7 @@ namespace Content.Server.DeviceNetwork.Systems if (connection.Owner == packet.Sender) continue; - BeforePacketSentEvent beforeEv = new(packet.Sender, xform, senderPos, connection.NetIdEnum.ToString()); + BeforePacketSentEvent beforeEv = new(packet.Sender, xform, senderPos, connection.NetIdEnum.ToString(), packet.Frequency); RaiseLocalEvent(connection.Owner, beforeEv, false); if (!beforeEv.Cancelled) diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs index fc31a77041..b459f5c70f 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs @@ -63,16 +63,6 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem public const int MaxExplosionAudioRange = 30; - /// - /// The "default" explosion prototype. - /// - /// - /// Generally components should specify an explosion prototype via a yaml datafield, so that the yaml-linter can - /// find errors. However some components, like rogue arrows, or some commands like the admin-smite need to have - /// a "default" option specified outside of yaml data-fields. Hence this const string. - /// - public static readonly ProtoId DefaultExplosionPrototypeId = "Default"; - public override void Initialize() { base.Initialize(); @@ -222,10 +212,8 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem return r0 * (MathF.Sqrt(12 * totalIntensity / v0 - 3) / 6 + 0.5f); } - /// - /// Queue an explosions, centered on some entity. - /// - public void QueueExplosion(EntityUid uid, + /// + public override void QueueExplosion(EntityUid uid, string typeId, float totalIntensity, float slope, diff --git a/Content.Server/IdentityManagement/IdentitySystem.cs b/Content.Server/IdentityManagement/IdentitySystem.cs deleted file mode 100644 index 131544e569..0000000000 --- a/Content.Server/IdentityManagement/IdentitySystem.cs +++ /dev/null @@ -1,180 +0,0 @@ -using Content.Server.Access.Systems; -using Content.Server.Administration.Logs; -using Content.Server.CriminalRecords.Systems; -using Content.Server.Humanoid; -using Content.Shared.Clothing; -using Content.Shared.Database; -using Content.Shared.Hands; -using Content.Shared.Humanoid; -using Content.Shared.IdentityManagement; -using Content.Shared.IdentityManagement.Components; -using Content.Shared.Inventory; -using Content.Shared.Inventory.Events; -using Robust.Shared.Containers; -using Robust.Shared.Enums; -using Robust.Shared.GameObjects.Components.Localization; - -namespace Content.Server.IdentityManagement; - -/// -/// Responsible for updating the identity of an entity on init or clothing equip/unequip. -/// -public sealed class IdentitySystem : SharedIdentitySystem -{ - [Dependency] private readonly IdCardSystem _idCard = default!; - [Dependency] private readonly IAdminLogManager _adminLog = default!; - [Dependency] private readonly MetaDataSystem _metaData = default!; - [Dependency] private readonly SharedContainerSystem _container = default!; - [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!; - [Dependency] private readonly CriminalRecordsConsoleSystem _criminalRecordsConsole = default!; - [Dependency] private readonly GrammarSystem _grammarSystem = default!; - - private HashSet _queuedIdentityUpdates = new(); - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); - SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); - SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); - SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); - SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); - SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); - SubscribeLocalEvent(OnMapInit); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - foreach (var ent in _queuedIdentityUpdates) - { - if (!TryComp(ent, out var identity)) - continue; - - UpdateIdentityInfo(ent, identity); - } - - _queuedIdentityUpdates.Clear(); - } - - // This is where the magic happens - private void OnMapInit(EntityUid uid, IdentityComponent component, MapInitEvent args) - { - var ident = Spawn(null, Transform(uid).Coordinates); - - _metaData.SetEntityName(ident, "identity"); - QueueIdentityUpdate(uid); - _container.Insert(ident, component.IdentityEntitySlot); - } - - /// - /// Queues an identity update to the start of the next tick. - /// - public override void QueueIdentityUpdate(EntityUid uid) - { - _queuedIdentityUpdates.Add(uid); - } - - #region Private API - - /// - /// Updates the metadata name for the id(entity) from the current state of the character. - /// - private void UpdateIdentityInfo(EntityUid uid, IdentityComponent identity) - { - if (identity.IdentityEntitySlot.ContainedEntity is not { } ident) - return; - - var representation = GetIdentityRepresentation(uid); - var name = GetIdentityName(uid, representation); - - // Clone the old entity's grammar to the identity entity, for loc purposes. - if (TryComp(uid, out var grammar)) - { - var identityGrammar = EnsureComp(ident); - identityGrammar.Attributes.Clear(); - - foreach (var (k, v) in grammar.Attributes) - { - identityGrammar.Attributes.Add(k, v); - } - - // If presumed name is null and we're using that, we set proper noun to be false ("the old woman") - if (name != representation.TrueName && representation.PresumedName == null) - _grammarSystem.SetProperNoun((ident, identityGrammar), false); - - Dirty(ident, identityGrammar); - } - - if (name == Name(ident)) - return; - - _metaData.SetEntityName(ident, name); - - _adminLog.Add(LogType.Identity, LogImpact.Medium, $"{ToPrettyString(uid)} changed identity to {name}"); - var identityChangedEvent = new IdentityChangedEvent(uid, ident); - RaiseLocalEvent(uid, ref identityChangedEvent); - SetIdentityCriminalIcon(uid); - } - - private string GetIdentityName(EntityUid target, IdentityRepresentation representation) - { - var ev = new SeeIdentityAttemptEvent(); - - RaiseLocalEvent(target, ev); - return representation.ToStringKnown(!ev.Cancelled); - } - - /// - /// When the identity of a person is changed, searches the criminal records to see if the name of the new identity - /// has a record. If the new name has a criminal status attached to it, the person will get the criminal status - /// until they change identity again. - /// - private void SetIdentityCriminalIcon(EntityUid uid) - { - _criminalRecordsConsole.CheckNewIdentity(uid); - } - - /// - /// Gets an 'identity representation' of an entity, with their true name being the entity name - /// and their 'presumed name' and 'presumed job' being the name/job on their ID card, if they have one. - /// - private IdentityRepresentation GetIdentityRepresentation(EntityUid target, - InventoryComponent? inventory=null, - HumanoidAppearanceComponent? appearance=null) - { - int age = 18; - Gender gender = Gender.Epicene; - string species = SharedHumanoidAppearanceSystem.DefaultSpecies; - - // Always use their actual age and gender, since that can't really be changed by an ID. - if (Resolve(target, ref appearance, false)) - { - gender = appearance.Gender; - age = appearance.Age; - species = appearance.Species; - } - - var ageString = _humanoid.GetAgeRepresentation(species, age); - var trueName = Name(target); - if (!Resolve(target, ref inventory, false)) - return new(trueName, gender, ageString, string.Empty); - - string? presumedJob = null; - string? presumedName = null; - - // Get their name and job from their ID for their presumed name. - if (_idCard.TryFindIdCard(target, out var id)) - { - presumedName = string.IsNullOrWhiteSpace(id.Comp.FullName) ? null : id.Comp.FullName; - presumedJob = id.Comp.LocalizedJobTitle?.ToLowerInvariant(); - } - - // If it didn't find a job, that's fine. - return new(trueName, gender, ageString, presumedName, presumedJob); - } - - #endregion -} diff --git a/Content.Server/Radio/EntitySystems/JammerSystem.cs b/Content.Server/Radio/EntitySystems/JammerSystem.cs index 1cea981d3c..02c9c64c6e 100644 --- a/Content.Server/Radio/EntitySystems/JammerSystem.cs +++ b/Content.Server/Radio/EntitySystems/JammerSystem.cs @@ -72,6 +72,15 @@ public sealed class JammerSystem : SharedJammerSystem EnsureComp(ent, out var jammingComp); _jammer.SetRange((ent, jammingComp), GetCurrentRange(ent)); _jammer.AddJammableNetwork((ent, jammingComp), DeviceNetworkComponent.DeviceNetIdDefaults.Wireless.ToString()); + + // Add excluded frequencies using the system method + if (ent.Comp.FrequenciesExcluded != null) + { + foreach (var freq in ent.Comp.FrequenciesExcluded) + { + _jammer.AddExcludedFrequency((ent, jammingComp), (uint)freq); + } + } } else { @@ -96,19 +105,23 @@ public sealed class JammerSystem : SharedJammerSystem private void OnRadioSendAttempt(ref RadioSendAttemptEvent args) { - if (ShouldCancelSend(args.RadioSource)) + if (ShouldCancelSend(args.RadioSource, args.Channel.Frequency)) { args.Cancelled = true; } } - private bool ShouldCancelSend(EntityUid sourceUid) + private bool ShouldCancelSend(EntityUid sourceUid, int frequency) { var source = Transform(sourceUid).Coordinates; var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out _, out var jam, out var transform)) { + // Check if this jammer excludes the frequency + if (jam.FrequenciesExcluded != null && jam.FrequenciesExcluded.Contains(frequency)) + continue; + if (_transform.InRange(source, transform.Coordinates, GetCurrentRange((uid, jam)))) { return true; diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index 3967e320a8..ba9487b031 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -1,6 +1,5 @@ using Content.Server.Access.Systems; using Content.Server.Humanoid; -using Content.Server.IdentityManagement; using Content.Server.Mind; using Content.Server.PDA; using Content.Server.Station.Components; @@ -11,6 +10,7 @@ using Content.Shared.Clothing; using Content.Shared.DetailExaminable; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Prototypes; +using Content.Shared.IdentityManagement; using Content.Shared.PDA; using Content.Shared.Preferences; using Content.Shared.Preferences.Loadouts; diff --git a/Content.Server/StationEvents/Events/ImmovableRodRule.cs b/Content.Server/StationEvents/Events/ImmovableRodRule.cs index 5e324a529b..83ab077236 100644 --- a/Content.Server/StationEvents/Events/ImmovableRodRule.cs +++ b/Content.Server/StationEvents/Events/ImmovableRodRule.cs @@ -35,7 +35,8 @@ public sealed class ImmovableRodRule : StationEventSystem - /// Try to get a record from this station's record entries, - /// from the provided station record key. Will always return - /// null if the key does not match the station. - /// - /// Station and key to try and index from the record set. - /// The resulting entry. - /// Station record component. - /// Type to get from the record set. - /// True if the record was obtained, false otherwise. - public bool TryGetRecord(StationRecordKey key, [NotNullWhen(true)] out T? entry, StationRecordsComponent? records = null) - { - entry = default; - - if (!Resolve(key.OriginStation, ref records)) - return false; - - return records.Records.TryGetRecordEntry(key.Id, out entry); - } - /// /// Gets a random record from the station's record entries. /// @@ -257,26 +237,6 @@ public sealed class StationRecordsSystem : SharedStationRecordsSystem return ent.Comp.Records.TryGetRecordEntry(key, out entry); } - /// - /// Returns an id if a record with the same name exists. - /// - /// - /// Linear search so O(n) time complexity. - /// - public uint? GetRecordByName(EntityUid station, string name, StationRecordsComponent? records = null) - { - if (!Resolve(station, ref records, false)) - return null; - - foreach (var (id, record) in GetRecordsOfType(station, records)) - { - if (record.Name == name) - return id; - } - - return null; - } - /// /// Get the name for a record, or an empty string if it has no record. /// @@ -288,21 +248,6 @@ public sealed class StationRecordsSystem : SharedStationRecordsSystem return record.Name; } - /// - /// Gets all records of a specific type from a station. - /// - /// The station to get the records from. - /// Station records component. - /// Type of record to fetch - /// Enumerable of pairs with a station record key, and the entry in question of type T. - public IEnumerable<(uint, T)> GetRecordsOfType(EntityUid station, StationRecordsComponent? records = null) - { - if (!Resolve(station, ref records)) - return Array.Empty<(uint, T)>(); - - return records.Records.GetRecordsOfType(); - } - /// /// Adds a new record entry to a station's record set. /// diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index 5e6c216ad2..5ad1f27d25 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -6,7 +6,6 @@ using Content.Server.Chat.Managers; using Content.Server.Ghost; using Content.Server.Ghost.Roles.Components; using Content.Server.Humanoid; -using Content.Server.IdentityManagement; using Content.Server.Inventory; using Content.Server.Mind; using Content.Server.NPC; @@ -40,6 +39,7 @@ using Content.Shared.Prying.Components; using Content.Shared.Traits.Assorted; using Robust.Shared.Audio.Systems; using Content.Shared.Ghost.Roles.Components; +using Content.Shared.IdentityManagement; using Content.Shared.Tag; using Robust.Shared.Player; using Robust.Shared.Prototypes; diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index a8201cbede..a2a1782553 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -840,7 +840,7 @@ public abstract partial class SharedActionsSystem : EntitySystem if (!_actionsQuery.Resolve(performer, ref performer.Comp, false)) { - DebugTools.Assert(performer == null || TerminatingOrDeleted(performer)); + DebugTools.Assert(TerminatingOrDeleted(performer)); ent.Comp.AttachedEntity = null; // TODO: should this delete the action since it's now orphaned? return; diff --git a/Content.Shared/Clothing/MagbootsSystem.cs b/Content.Shared/Clothing/MagbootsSystem.cs index d00211fa65..225ba3655f 100644 --- a/Content.Shared/Clothing/MagbootsSystem.cs +++ b/Content.Shared/Clothing/MagbootsSystem.cs @@ -14,7 +14,6 @@ namespace Content.Shared.Clothing; public sealed class SharedMagbootsSystem : EntitySystem { [Dependency] private readonly AlertsSystem _alerts = default!; - [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedGravitySystem _gravity = default!; diff --git a/Content.Shared/Coordinates/Helpers/SnapgridHelper.cs b/Content.Shared/Coordinates/Helpers/SnapgridHelper.cs index 264b647afb..aa2bd15586 100644 --- a/Content.Shared/Coordinates/Helpers/SnapgridHelper.cs +++ b/Content.Shared/Coordinates/Helpers/SnapgridHelper.cs @@ -9,12 +9,12 @@ namespace Content.Shared.Coordinates.Helpers public static EntityCoordinates SnapToGrid(this EntityCoordinates coordinates, IEntityManager? entMan = null, IMapManager? mapManager = null) { IoCManager.Resolve(ref entMan, ref mapManager); + var xformSys = entMan.System(); - var gridId = coordinates.GetGridUid(entMan); + var gridId = xformSys.GetGrid(coordinates.EntityId); if (gridId == null) { - var xformSys = entMan.System(); var mapPos = xformSys.ToMapCoordinates(coordinates); var mapX = (int)Math.Floor(mapPos.X) + 0.5f; var mapY = (int)Math.Floor(mapPos.Y) + 0.5f; @@ -24,11 +24,11 @@ namespace Content.Shared.Coordinates.Helpers var grid = entMan.GetComponent(gridId.Value); var tileSize = grid.TileSize; - var localPos = coordinates.WithEntityId(gridId.Value).Position; + var localPos = xformSys.WithEntityId(coordinates, gridId.Value).Position; var x = (int)Math.Floor(localPos.X / tileSize) + tileSize / 2f; var y = (int)Math.Floor(localPos.Y / tileSize) + tileSize / 2f; var gridPos = new EntityCoordinates(gridId.Value, new Vector2(x, y)); - return gridPos.WithEntityId(coordinates.EntityId); + return xformSys.WithEntityId(gridPos, coordinates.EntityId); } public static EntityCoordinates SnapToGrid(this EntityCoordinates coordinates, MapGridComponent grid) diff --git a/Content.Shared/CriminalRecords/Systems/SharedCriminalRecordsConsoleSystem.cs b/Content.Shared/CriminalRecords/Systems/SharedCriminalRecordsConsoleSystem.cs index d3d366ecf9..7b894f0087 100644 --- a/Content.Shared/CriminalRecords/Systems/SharedCriminalRecordsConsoleSystem.cs +++ b/Content.Shared/CriminalRecords/Systems/SharedCriminalRecordsConsoleSystem.cs @@ -1,6 +1,44 @@ +using Content.Shared.IdentityManagement; +using Content.Shared.Security; +using Content.Shared.Security.Components; +using Content.Shared.Station; +using Content.Shared.StationRecords; + namespace Content.Shared.CriminalRecords.Systems; /// /// Station records aren't predicted, just exists for access. /// -public abstract class SharedCriminalRecordsConsoleSystem : EntitySystem; +public abstract class SharedCriminalRecordsConsoleSystem : EntitySystem +{ + [Dependency] private readonly SharedCriminalRecordsSystem _criminalRecords = default!; + [Dependency] private readonly SharedStationRecordsSystem _records = default!; + [Dependency] private readonly SharedStationSystem _station = default!; + + /// + /// Checks if the new identity's name has a criminal record attached to it, and gives the entity the icon that + /// belongs to the status if it does. + /// + public void CheckNewIdentity(EntityUid uid) + { + var name = Identity.Name(uid, EntityManager); + var xform = Transform(uid); + + // TODO use the entity's station? Not the station of the map that it happens to currently be on? + var station = _station.GetStationInMap(xform.MapID); + + if (station != null && _records.GetRecordByName(station.Value, name) is { } id) + { + if (_records.TryGetRecord(new StationRecordKey(id, station.Value), + out var record)) + { + if (record.Status != SecurityStatus.None) + { + _criminalRecords.SetCriminalIcon(name, record.Status, uid); + return; + } + } + } + RemComp(uid); + } +} diff --git a/Content.Shared/Damage/Systems/DamagePopupSystem.cs b/Content.Shared/Damage/Systems/DamagePopupSystem.cs index 83cd551279..fa29b5a018 100644 --- a/Content.Shared/Damage/Systems/DamagePopupSystem.cs +++ b/Content.Shared/Damage/Systems/DamagePopupSystem.cs @@ -31,7 +31,8 @@ public sealed class DamagePopupSystem : EntitySystem _ => "Invalid type", }; - _popupSystem.PopupPredicted(msg, ent.Owner, args.Origin); + // Turn this back into (msg, ent.Owner, args.Origin) when shooting gets predicted. + _popupSystem.PopupPredicted(msg, ent.Owner, null); } } diff --git a/Content.Shared/DeviceNetwork/Components/DeviceNetworkJammerComponent.cs b/Content.Shared/DeviceNetwork/Components/DeviceNetworkJammerComponent.cs index ab320d6d3e..5ee04de0d1 100644 --- a/Content.Shared/DeviceNetwork/Components/DeviceNetworkJammerComponent.cs +++ b/Content.Shared/DeviceNetwork/Components/DeviceNetworkJammerComponent.cs @@ -23,4 +23,10 @@ public sealed partial class DeviceNetworkJammerComponent : Component [DataField, AutoNetworkedField] public HashSet JammableNetworks = []; + /// + /// Device networks frequencies that wont be jammed. + /// + [DataField] + public HashSet FrequenciesExcluded = []; + } diff --git a/Content.Shared/DeviceNetwork/Events/BeforePacketSentEvent.cs b/Content.Shared/DeviceNetwork/Events/BeforePacketSentEvent.cs index 5d5c038dbf..b34995a285 100644 --- a/Content.Shared/DeviceNetwork/Events/BeforePacketSentEvent.cs +++ b/Content.Shared/DeviceNetwork/Events/BeforePacketSentEvent.cs @@ -25,11 +25,17 @@ public sealed class BeforePacketSentEvent : CancellableEntityEventArgs /// public readonly string NetworkId; - public BeforePacketSentEvent(EntityUid sender, TransformComponent xform, Vector2 senderPosition, string networkId) + /// + /// The frequency the packet is sent on. + /// + public readonly uint Frequency; + + public BeforePacketSentEvent(EntityUid sender, TransformComponent xform, Vector2 senderPosition, string networkId, uint frequency) { Sender = sender; SenderTransform = xform; SenderPosition = senderPosition; NetworkId = networkId; + Frequency = frequency; } -} \ No newline at end of file +} diff --git a/Content.Shared/DeviceNetwork/Systems/SharedDeviceNetworkJammerSystem.cs b/Content.Shared/DeviceNetwork/Systems/SharedDeviceNetworkJammerSystem.cs index fc714ea34f..6ed770ffbc 100644 --- a/Content.Shared/DeviceNetwork/Systems/SharedDeviceNetworkJammerSystem.cs +++ b/Content.Shared/DeviceNetwork/Systems/SharedDeviceNetworkJammerSystem.cs @@ -60,4 +60,34 @@ public abstract class SharedDeviceNetworkJammerSystem : EntitySystem ent.Comp.JammableNetworks.Clear(); Dirty(ent); } + + /// + /// Enables this entity to stop packets with the specified frequency from being jammmed. + /// + public void AddExcludedFrequency(Entity ent, uint frequency) + { + if (ent.Comp.FrequenciesExcluded.Add(frequency)) + Dirty(ent); + } + + /// + /// Stops this entity to stop packets with the specified frequency from being jammmed. + /// + public void RemoveExcludedFrequency(Entity ent, uint frequency) + { + if (ent.Comp.FrequenciesExcluded.Remove(frequency)) + Dirty(ent); + } + + /// + /// Stops this entity to stop packets with any frequency from being jammmed. + /// + public void ClearExcludedFrequency(Entity ent) + { + if (ent.Comp.FrequenciesExcluded.Count == 0) + return; + + ent.Comp.FrequenciesExcluded.Clear(); + Dirty(ent); + } } diff --git a/Content.Shared/Explosion/EntitySystems/SharedExplosionSystem.cs b/Content.Shared/Explosion/EntitySystems/SharedExplosionSystem.cs index f298255807..d6053c9c3c 100644 --- a/Content.Shared/Explosion/EntitySystems/SharedExplosionSystem.cs +++ b/Content.Shared/Explosion/EntitySystems/SharedExplosionSystem.cs @@ -1,14 +1,26 @@ using Content.Shared.Armor; using Content.Shared.Explosion.Components; +using Robust.Shared.Prototypes; namespace Content.Shared.Explosion.EntitySystems; +// TODO some sort of struct like DamageSpecifier but for explosions. /// /// Lets code in shared trigger explosions and handles explosion resistance examining. /// All processing is still done clientside. /// public abstract class SharedExplosionSystem : EntitySystem { + /// + /// The "default" explosion prototype. + /// + /// + /// Generally components should specify an explosion prototype via a yaml datafield, so that the yaml-linter can + /// find errors. However some components, like rogue arrows, or some commands like the admin-smite need to have + /// a "default" option specified outside of yaml data-fields. Hence this const string. + /// + public static readonly ProtoId DefaultExplosionPrototypeId = "Default"; + public override void Initialize() { base.Initialize(); @@ -37,4 +49,24 @@ public abstract class SharedExplosionSystem : EntitySystem public virtual void TriggerExplosive(EntityUid uid, ExplosiveComponent? explosive = null, bool delete = true, float? totalIntensity = null, float? radius = null, EntityUid? user = null) { } + + /// + /// Queue an explosion centered on some entity. Bypasses needing . + /// + /// Where the explosion happens. + /// A ProtoId of type . + /// The entity which caused the explosion. + /// Whether to add an admin log about this explosion. Includes user. + public virtual void QueueExplosion(EntityUid uid, + string typeId, + float totalIntensity, + float slope, + float maxTileIntensity, + float tileBreakScale = 1f, + int maxTileBreak = int.MaxValue, + bool canCreateVacuum = true, + EntityUid? user = null, + bool addLog = true) + { + } } diff --git a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs index 8acac4fa90..d63872589c 100644 --- a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs +++ b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs @@ -42,7 +42,7 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem [Dependency] private readonly ISerializationManager _serManager = default!; [Dependency] private readonly MarkingManager _markingManager = default!; [Dependency] private readonly GrammarSystem _grammarSystem = default!; - [Dependency] private readonly SharedIdentitySystem _identity = default!; + [Dependency] private readonly IdentitySystem _identity = default!; private ISharedSponsorsManager? _sponsors; public static readonly ProtoId DefaultSpecies = "Human"; diff --git a/Content.Shared/IdentityManagement/Components/IdentityBlockerComponent.cs b/Content.Shared/IdentityManagement/Components/IdentityBlockerComponent.cs index 308d9c0bf7..cc92a4c078 100644 --- a/Content.Shared/IdentityManagement/Components/IdentityBlockerComponent.cs +++ b/Content.Shared/IdentityManagement/Components/IdentityBlockerComponent.cs @@ -1,12 +1,13 @@ using Content.Shared.Inventory; using Robust.Shared.GameStates; +using Robust.Shared.Serialization; namespace Content.Shared.IdentityManagement.Components; -[RegisterComponent, NetworkedComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class IdentityBlockerComponent : Component { - [DataField] + [DataField, AutoNetworkedField] public bool Enabled = true; /// @@ -16,6 +17,8 @@ public sealed partial class IdentityBlockerComponent : Component public IdentityBlockerCoverage Coverage = IdentityBlockerCoverage.FULL; } +[Flags] +[Serializable, NetSerializable] public enum IdentityBlockerCoverage { NONE = 0, diff --git a/Content.Shared/IdentityManagement/Components/IdentityComponent.cs b/Content.Shared/IdentityManagement/Components/IdentityComponent.cs index 5e4c4531c1..86c07b307f 100644 --- a/Content.Shared/IdentityManagement/Components/IdentityComponent.cs +++ b/Content.Shared/IdentityManagement/Components/IdentityComponent.cs @@ -1,5 +1,6 @@ using Robust.Shared.Containers; using Robust.Shared.Enums; +using Robust.Shared.GameStates; namespace Content.Shared.IdentityManagement.Components; @@ -10,7 +11,7 @@ namespace Content.Shared.IdentityManagement.Components; /// /// This is a and not just a datum entity because we do sort of care that it gets deleted and sent with the user. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent] public sealed partial class IdentityComponent : Component { [ViewVariables] diff --git a/Content.Shared/IdentityManagement/IdentitySystem.cs b/Content.Shared/IdentityManagement/IdentitySystem.cs new file mode 100644 index 0000000000..7c559df629 --- /dev/null +++ b/Content.Shared/IdentityManagement/IdentitySystem.cs @@ -0,0 +1,241 @@ +using Content.Shared.Access.Systems; +using Content.Shared.Administration.Logs; +using Content.Shared.Clothing; +using Content.Shared.CriminalRecords.Systems; +using Content.Shared.Database; +using Content.Shared.Hands; +using Content.Shared.Humanoid; +using Content.Shared.IdentityManagement.Components; +using Content.Shared.Inventory; +using Content.Shared.Inventory.Events; +using Robust.Shared.Containers; +using Robust.Shared.Enums; +using Robust.Shared.GameObjects.Components.Localization; +using Robust.Shared.Timing; + +namespace Content.Shared.IdentityManagement; + +/// +/// Responsible for updating the identity of an entity on init or clothing equip/unequip. +/// +public sealed class IdentitySystem : EntitySystem +{ + [Dependency] private readonly GrammarSystem _grammarSystem = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly ISharedAdminLogManager _adminLog = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly SharedCriminalRecordsConsoleSystem _criminalRecordsConsole = default!; + [Dependency] private readonly SharedHumanoidAppearanceSystem _humanoid = default!; + [Dependency] private readonly SharedIdCardSystem _idCard = default!; + + // The name of the container holding the identity entity + private const string SlotName = "identity"; + + // Recycled hashset for tracking identities each tick that need to update + private readonly HashSet _queuedIdentityUpdates = new(); + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSeeIdentity); + SubscribeLocalEvent>(OnRelaySeeIdentity); + SubscribeLocalEvent(OnMaskToggled); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnComponentInit); + + SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); + SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); + SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); + SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); + SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); + SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); + } + + /// + /// Iterates through all identities that need to be updated. + /// + public override void Update(float frameTime) + { + base.Update(frameTime); + + foreach (var ent in _queuedIdentityUpdates) + { + if (!TryComp(ent, out var identity)) + continue; + + UpdateIdentityInfo((ent, identity)); + } + + _queuedIdentityUpdates.Clear(); + } + + #region Event Handlers + + // Creates an identity entity, and store it in the identity container + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + var ident = Spawn(null, Transform(ent).Coordinates); + + _metaData.SetEntityName(ident, "identity"); + QueueIdentityUpdate(ent); + _container.Insert(ident, ent.Comp.IdentityEntitySlot); + } + + private void OnComponentInit(Entity ent, ref ComponentInit args) + { + ent.Comp.IdentityEntitySlot = _container.EnsureContainer(ent, SlotName); + } + + // Adds an identity blocker's coverage, and cancels the event if coverage is complete. + private void OnSeeIdentity(Entity ent, ref SeeIdentityAttemptEvent args) + { + if (ent.Comp.Enabled) + { + args.TotalCoverage |= ent.Comp.Coverage; + if (args.TotalCoverage == IdentityBlockerCoverage.FULL) + args.Cancel(); + } + } + + private void OnRelaySeeIdentity(Entity ent, ref InventoryRelayedEvent args) + { + OnSeeIdentity(ent, ref args.Args); + } + + // Toggles if a mask is hiding the identity. + private void OnMaskToggled(Entity ent, ref ItemMaskToggledEvent args) + { + ent.Comp.Enabled = !args.Mask.Comp.IsToggled; + Dirty(ent); + } + + #endregion + + /// + /// Queues an identity update to the start of the next tick. + /// + public void QueueIdentityUpdate(EntityUid uid) + { + if (_timing.ApplyingState) + return; + + _queuedIdentityUpdates.Add(uid); + } + #region Private API + + /// + /// Updates the metadata name for the id(entity) from the current state of the character. + /// + private void UpdateIdentityInfo(Entity ent) + { + if (ent.Comp.IdentityEntitySlot.ContainedEntity is not { } ident) + return; + + var representation = GetIdentityRepresentation(ent.Owner); + var name = GetIdentityName(ent, representation); + + // Clone the old entity's grammar to the identity entity, for loc purposes. + if (TryComp(ent, out var grammar)) + { + var identityGrammar = EnsureComp(ident); + identityGrammar.Attributes.Clear(); + + foreach (var (k, v) in grammar.Attributes) + { + identityGrammar.Attributes.Add(k, v); + } + + // If presumed name is null and we're using that, we set proper noun to be false ("the old woman") + if (name != representation.TrueName && representation.PresumedName == null) + _grammarSystem.SetProperNoun((ident, identityGrammar), false); + + Dirty(ident, identityGrammar); + } + + if (name == Name(ident)) + return; + + _metaData.SetEntityName(ident, name); + + _adminLog.Add(LogType.Identity, LogImpact.Medium, $"{ToPrettyString(ent)} changed identity to {name}"); + var identityChangedEvent = new IdentityChangedEvent(ent, ident); + RaiseLocalEvent(ent, ref identityChangedEvent); + SetIdentityCriminalIcon(ent); + } + + /// + /// When the identity of a person is changed, searches the criminal records to see if the name of the new identity + /// has a record. If the new name has a criminal status attached to it, the person will get the criminal status + /// until they change identity again. + /// + private void SetIdentityCriminalIcon(EntityUid uid) + { + _criminalRecordsConsole.CheckNewIdentity(uid); + } + + /// + /// Attempts to get an entity's name. Cancelled if the entity has full coverage from . + /// + /// The entity being targeted. + /// The data structure containing an entity's identities. + /// + /// An entity's real name if isn't cancelled, + /// or a hidden identity such as a fake ID or fully hidden identity like "middle-aged man". + /// + private string GetIdentityName(EntityUid target, IdentityRepresentation representation) + { + var ev = new SeeIdentityAttemptEvent(); + + RaiseLocalEvent(target, ev); + return representation.ToStringKnown(!ev.Cancelled); + } + + /// + /// Gets an 'identity representation' of an entity, with their true name being the entity name + /// and their 'presumed name' and 'presumed job' being the name/job on their ID card, if they have one. + /// + private IdentityRepresentation GetIdentityRepresentation(Entity target) + { + var age = 18; + var gender = Gender.Epicene; + var species = SharedHumanoidAppearanceSystem.DefaultSpecies; + + // Always use their actual age and gender, since that can't really be changed by an ID. + if (Resolve(target, ref target.Comp2, false)) + { + gender = target.Comp2.Gender; + age = target.Comp2.Age; + species = target.Comp2.Species; + } + + var ageString = _humanoid.GetAgeRepresentation(species, age); + var trueName = Name(target); + if (!Resolve(target, ref target.Comp1, false)) + return new(trueName, gender, ageString, string.Empty); + + string? presumedJob = null; + string? presumedName = null; + + // Get their name and job from their ID for their presumed name. + if (_idCard.TryFindIdCard(target, out var id)) + { + presumedName = string.IsNullOrWhiteSpace(id.Comp.FullName) ? null : id.Comp.FullName; + presumedJob = id.Comp.LocalizedJobTitle?.ToLowerInvariant(); + } + + // If it didn't find a job, that's fine. + return new(trueName, gender, ageString, presumedName, presumedJob); + } + + #endregion +} + +/// +/// Gets called whenever an entity changes their identity. +/// +[ByRefEvent] +public record struct IdentityChangedEvent(EntityUid CharacterEntity, EntityUid IdentityEntity); diff --git a/Content.Shared/IdentityManagement/SharedIdentitySystem.cs b/Content.Shared/IdentityManagement/SharedIdentitySystem.cs deleted file mode 100644 index 6b03dc3850..0000000000 --- a/Content.Shared/IdentityManagement/SharedIdentitySystem.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Content.Shared.Clothing; -using Content.Shared.IdentityManagement.Components; -using Content.Shared.Inventory; -using Robust.Shared.Containers; - -namespace Content.Shared.IdentityManagement; - -public abstract class SharedIdentitySystem : EntitySystem -{ - [Dependency] private readonly SharedContainerSystem _container = default!; - private static string SlotName = "identity"; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent(OnSeeIdentity); - SubscribeLocalEvent>((e, c, ev) => OnSeeIdentity(e, c, ev.Args)); - SubscribeLocalEvent(OnMaskToggled); - } - - private void OnSeeIdentity(EntityUid uid, IdentityBlockerComponent component, SeeIdentityAttemptEvent args) - { - if (component.Enabled) - { - args.TotalCoverage |= component.Coverage; - if(args.TotalCoverage == IdentityBlockerCoverage.FULL) - args.Cancel(); - } - } - - protected virtual void OnComponentInit(EntityUid uid, IdentityComponent component, ComponentInit args) - { - component.IdentityEntitySlot = _container.EnsureContainer(uid, SlotName); - } - - private void OnMaskToggled(Entity ent, ref ItemMaskToggledEvent args) - { - ent.Comp.Enabled = !args.Mask.Comp.IsToggled; - } - - /// - /// Queues an identity update to the start of the next tick. - /// - public virtual void QueueIdentityUpdate(EntityUid uid) { } -} -/// -/// Gets called whenever an entity changes their identity. -/// -[ByRefEvent] -public record struct IdentityChangedEvent(EntityUid CharacterEntity, EntityUid IdentityEntity); diff --git a/Content.Shared/Radio/Components/ActiveRadioJammerComponent.cs b/Content.Shared/Radio/Components/ActiveRadioJammerComponent.cs index d5679f1189..87e4e0b3b3 100644 --- a/Content.Shared/Radio/Components/ActiveRadioJammerComponent.cs +++ b/Content.Shared/Radio/Components/ActiveRadioJammerComponent.cs @@ -4,7 +4,7 @@ using Robust.Shared.GameStates; namespace Content.Shared.Radio.Components; /// -/// Prevents all radio in range from sending messages +/// Prevents all non whitelisted radios from sending messages /// [RegisterComponent, NetworkedComponent] [Access(typeof(SharedJammerSystem))] diff --git a/Content.Shared/Radio/Components/RadioJammerComponent.cs b/Content.Shared/Radio/Components/RadioJammerComponent.cs index 8f3519cf7d..af4f9e45c8 100644 --- a/Content.Shared/Radio/Components/RadioJammerComponent.cs +++ b/Content.Shared/Radio/Components/RadioJammerComponent.cs @@ -46,6 +46,12 @@ public sealed partial class RadioJammerComponent : Component [DataField(required: true), ViewVariables(VVAccess.ReadOnly)] public RadioJamSetting[] Settings; + /// + /// Frequencies that are NOT jammed by this jammer. + /// + [DataField] + public HashSet FrequenciesExcluded = []; + /// /// Index of the currently selected setting. /// diff --git a/Content.Shared/RatKing/RatKingRummageableComponent.cs b/Content.Shared/RatKing/Components/RummageableComponent.cs similarity index 59% rename from Content.Shared/RatKing/RatKingRummageableComponent.cs rename to Content.Shared/RatKing/Components/RummageableComponent.cs index f3a389ef76..ea92b55805 100644 --- a/Content.Shared/RatKing/RatKingRummageableComponent.cs +++ b/Content.Shared/RatKing/Components/RummageableComponent.cs @@ -1,17 +1,16 @@ -using Content.Shared.Random; +using Content.Shared.EntityTable.EntitySelectors; using Robust.Shared.Audio; using Robust.Shared.GameStates; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -namespace Content.Shared.RatKing; +namespace Content.Shared.RatKing.Components; /// /// This is used for entities that can be /// rummaged through by the rat king to get loot. /// -[RegisterComponent, NetworkedComponent, Access(typeof(SharedRatKingSystem))] +[RegisterComponent, NetworkedComponent] [AutoGenerateComponentState] -public sealed partial class RatKingRummageableComponent : Component +public sealed partial class RummageableComponent : Component { /// /// Whether or not this entity has been rummaged through already. @@ -28,11 +27,10 @@ public sealed partial class RatKingRummageableComponent : Component public float RummageDuration = 3f; /// - /// A weighted random entity prototype containing the different loot that rummaging can provide. + /// The entity table to select loot from. /// - [DataField("rummageLoot", customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)] - [AutoNetworkedField] - public string RummageLoot = "RatKingLoot"; + [DataField(required: true)] + public EntityTableSelector Table = default!; /// /// Sound played on rummage completion. diff --git a/Content.Shared/RatKing/Components/RummagerComponent.cs b/Content.Shared/RatKing/Components/RummagerComponent.cs new file mode 100644 index 0000000000..338e9eee13 --- /dev/null +++ b/Content.Shared/RatKing/Components/RummagerComponent.cs @@ -0,0 +1,11 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.RatKing.Components; + +/// +/// This is used for entities that can rummage through entities +/// with the +/// +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class RummagerComponent : Component; diff --git a/Content.Shared/RatKing/SharedRatKingSystem.cs b/Content.Shared/RatKing/SharedRatKingSystem.cs index edb2ab90db..3f6c9bdc22 100644 --- a/Content.Shared/RatKing/SharedRatKingSystem.cs +++ b/Content.Shared/RatKing/SharedRatKingSystem.cs @@ -1,26 +1,15 @@ using Content.Shared.Actions; -using Content.Shared.Actions.Components; -using Content.Shared.DoAfter; -using Content.Shared.Random; -using Content.Shared.Random.Helpers; -using Content.Shared.Verbs; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Network; +using Content.Shared.Actions.Components; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Serialization; namespace Content.Shared.RatKing; public abstract class SharedRatKingSystem : EntitySystem { - [Dependency] private readonly INetManager _net = default!; [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; [Dependency] protected readonly IRobustRandom Random = default!; [Dependency] private readonly SharedActionsSystem _action = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; /// public override void Initialize() @@ -28,11 +17,7 @@ public abstract class SharedRatKingSystem : EntitySystem SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnOrderAction); - SubscribeLocalEvent(OnServantShutdown); - - SubscribeLocalEvent>(OnGetVerb); - SubscribeLocalEvent(OnDoAfterComplete); } private void OnStartup(EntityUid uid, RatKingComponent component, ComponentStartup args) @@ -105,43 +90,6 @@ public abstract class SharedRatKingSystem : EntitySystem _action.StartUseDelay(component.ActionOrderLooseEntity); } - private void OnGetVerb(EntityUid uid, RatKingRummageableComponent component, GetVerbsEvent args) - { - if (!HasComp(args.User) || component.Looted) - return; - - args.Verbs.Add(new AlternativeVerb - { - Text = Loc.GetString("rat-king-rummage-text"), - Priority = 0, - Act = () => - { - _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.RummageDuration, - new RatKingRummageDoAfterEvent(), uid, uid) - { - BlockDuplicate = true, - BreakOnDamage = true, - BreakOnMove = true, - DistanceThreshold = 2f - }); - } - }); - } - - private void OnDoAfterComplete(EntityUid uid, RatKingRummageableComponent component, RatKingRummageDoAfterEvent args) - { - if (args.Cancelled || component.Looted) - return; - - component.Looted = true; - Dirty(uid, component); - _audio.PlayPredicted(component.Sound, uid, args.User); - - var spawn = PrototypeManager.Index(component.RummageLoot).Pick(Random); - if (_net.IsServer) - Spawn(spawn, Transform(uid).Coordinates); - } - public void UpdateAllServants(EntityUid uid, RatKingComponent component) { foreach (var servant in component.Servants) @@ -160,9 +108,3 @@ public abstract class SharedRatKingSystem : EntitySystem } } - -[Serializable, NetSerializable] -public sealed partial class RatKingRummageDoAfterEvent : SimpleDoAfterEvent -{ - -} diff --git a/Content.Shared/RatKing/Systems/RummagerSystem.cs b/Content.Shared/RatKing/Systems/RummagerSystem.cs new file mode 100644 index 0000000000..d9e9a87694 --- /dev/null +++ b/Content.Shared/RatKing/Systems/RummagerSystem.cs @@ -0,0 +1,82 @@ +using Content.Shared.DoAfter; +using Content.Shared.EntityTable; +using Content.Shared.RatKing.Components; +using Content.Shared.Verbs; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Network; +using Robust.Shared.Serialization; + +namespace Content.Shared.RatKing.Systems; + +public sealed class RummagerSystem : EntitySystem +{ + [Dependency] private readonly EntityTableSystem _entityTable = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(OnGetVerb); + SubscribeLocalEvent(OnDoAfterComplete); + } + + private void OnGetVerb(Entity ent, ref GetVerbsEvent args) + { + if (!HasComp(args.User) || ent.Comp.Looted) + return; + + var user = args.User; + + args.Verbs.Add(new AlternativeVerb + { + Text = Loc.GetString("rat-king-rummage-text"), + Priority = 0, + Act = () => + { + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, + user, + ent.Comp.RummageDuration, + new RummageDoAfterEvent(), + ent, + ent) + { + BlockDuplicate = true, + BreakOnDamage = true, + BreakOnMove = true, + DistanceThreshold = 2f + }); + } + }); + } + + private void OnDoAfterComplete(Entity ent, ref RummageDoAfterEvent args) + { + if (args.Cancelled || ent.Comp.Looted) + return; + + ent.Comp.Looted = true; + Dirty(ent, ent.Comp); + _audio.PlayPredicted(ent.Comp.Sound, ent, args.User); + + if (_net.IsClient) + return; + + var spawns = _entityTable.GetSpawns(ent.Comp.Table); + var coordinates = Transform(ent).Coordinates; + + foreach (var spawn in spawns) + { + Spawn(spawn, coordinates); + } + } +} + +/// +/// DoAfter event for rummaging through a container with RummageableComponent. +/// +[Serializable, NetSerializable] +public sealed partial class RummageDoAfterEvent : SimpleDoAfterEvent; diff --git a/Content.Shared/StationRecords/SharedStationRecordsSystem.cs b/Content.Shared/StationRecords/SharedStationRecordsSystem.cs index c2cc418f54..e04de09d65 100644 --- a/Content.Shared/StationRecords/SharedStationRecordsSystem.cs +++ b/Content.Shared/StationRecords/SharedStationRecordsSystem.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + namespace Content.Shared.StationRecords; public abstract class SharedStationRecordsSystem : EntitySystem @@ -40,4 +42,60 @@ public abstract class SharedStationRecordsSystem : EntitySystem } return result; } + + /// + /// Try to get a record from this station's record entries, + /// from the provided station record key. Will always return + /// null if the key does not match the station. + /// + /// Station and key to try and index from the record set. + /// The resulting entry. + /// Station record component. + /// Type to get from the record set. + /// True if the record was obtained, false otherwise. Always false on client. + public bool TryGetRecord(StationRecordKey key, [NotNullWhen(true)] out T? entry, StationRecordsComponent? records = null) + { + entry = default; + + if (!Resolve(key.OriginStation, ref records)) + return false; + + return records.Records.TryGetRecordEntry(key.Id, out entry); + } + + /// + /// Gets all records of a specific type from a station. + /// + /// The station to get the records from. + /// Station records component. + /// Type of record to fetch + /// Enumerable of pairs with a station record key, and the entry in question of type T. Always empty on client. + public IEnumerable<(uint, T)> GetRecordsOfType(EntityUid station, StationRecordsComponent? records = null) + { + if (!Resolve(station, ref records)) + return Array.Empty<(uint, T)>(); + + return records.Records.GetRecordsOfType(); + } + + /// + /// Returns an id if a record with the same name exists. + /// + /// + /// Linear search so O(n) time complexity. + /// + /// Returns a station record id. Always null on client. + public uint? GetRecordByName(EntityUid station, string name, StationRecordsComponent? records = null) + { + if (!Resolve(station, ref records, false)) + return null; + + foreach (var (id, record) in GetRecordsOfType(station, records)) + { + if (record.Name == name) + return id; + } + + return null; + } } diff --git a/Content.Server/StationRecords/StationRecordSet.cs b/Content.Shared/StationRecords/StationRecordSet.cs similarity index 98% rename from Content.Server/StationRecords/StationRecordSet.cs rename to Content.Shared/StationRecords/StationRecordSet.cs index b5a4501cea..169e5843d2 100644 --- a/Content.Server/StationRecords/StationRecordSet.cs +++ b/Content.Shared/StationRecords/StationRecordSet.cs @@ -1,9 +1,8 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; -using Content.Shared.StationRecords; using Robust.Shared.Utility; -namespace Content.Server.StationRecords; +namespace Content.Shared.StationRecords; /// /// Set of station records for a single station. StationRecordsComponent stores these. diff --git a/Content.Server/StationRecords/Components/StationRecordsComponent.cs b/Content.Shared/StationRecords/StationRecordsComponent.cs similarity index 70% rename from Content.Server/StationRecords/Components/StationRecordsComponent.cs rename to Content.Shared/StationRecords/StationRecordsComponent.cs index 4ea65522f4..66c60dddc6 100644 --- a/Content.Server/StationRecords/Components/StationRecordsComponent.cs +++ b/Content.Shared/StationRecords/StationRecordsComponent.cs @@ -1,8 +1,6 @@ -using Content.Server.StationRecords.Systems; +namespace Content.Shared.StationRecords; -namespace Content.Server.StationRecords; - -[Access(typeof(StationRecordsSystem))] +[Access(typeof(SharedStationRecordsSystem))] [RegisterComponent] public sealed partial class StationRecordsComponent : Component { diff --git a/Content.Shared/Teleportation/Systems/SharedPortalSystem.cs b/Content.Shared/Teleportation/Systems/SharedPortalSystem.cs index 5ef7619ceb..3ab703704a 100644 --- a/Content.Shared/Teleportation/Systems/SharedPortalSystem.cs +++ b/Content.Shared/Teleportation/Systems/SharedPortalSystem.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using Content.Shared.Ghost; using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Pulling.Systems; @@ -6,7 +6,6 @@ using Content.Shared.Popups; using Content.Shared.Projectiles; using Content.Shared.Teleportation.Components; using Content.Shared.Verbs; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; using Robust.Shared.Network; @@ -19,8 +18,10 @@ using Robust.Shared.Utility; namespace Content.Shared.Teleportation.Systems; /// -/// This handles teleporting entities through portals, and creating new linked portals. +/// This handles teleporting entities from a portal to a linked portal, or to a random nearby destination. +/// Uses to get linked portals. /// +/// public abstract class SharedPortalSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; @@ -39,12 +40,13 @@ public abstract class SharedPortalSystem : EntitySystem /// public override void Initialize() { + SubscribeLocalEvent>(OnGetVerbs); + SubscribeLocalEvent(OnCollide); SubscribeLocalEvent(OnEndCollide); - SubscribeLocalEvent>(OnGetVerbs); } - private void OnGetVerbs(EntityUid uid, PortalComponent component, GetVerbsEvent args) + private void OnGetVerbs(Entity ent, ref GetVerbsEvent args) { // Traversal altverb for ghosts to use that bypasses normal functionality if (!args.CanAccess || !HasComp(args.User)) @@ -52,8 +54,9 @@ public abstract class SharedPortalSystem : EntitySystem // Don't use the verb with unlinked or with multi-output portals // (this is only intended to be useful for ghosts to see where a linked portal leads) - var disabled = !TryComp(uid, out var link) || link.LinkedEntities.Count != 1; + var disabled = !TryComp(ent, out var link) || link.LinkedEntities.Count != 1; + var subject = args.User; args.Verbs.Add(new AlternativeVerb { Priority = 11, @@ -62,8 +65,13 @@ public abstract class SharedPortalSystem : EntitySystem if (link == null || disabled) return; - var ent = link.LinkedEntities.First(); - TeleportEntity(uid, args.User, Transform(ent).Coordinates, ent, false); + // check prediction + if (_netMan.IsClient && !CanPredictTeleport((ent, link))) + return; + + var destination = link.LinkedEntities.First(); + + TeleportEntity(ent, subject, Transform(destination).Coordinates, destination, false); }, Disabled = disabled, Text = Loc.GetString("portal-component-ghost-traverse"), @@ -74,14 +82,7 @@ public abstract class SharedPortalSystem : EntitySystem }); } - private bool ShouldCollide(string ourId, string otherId, Fixture our, Fixture other) - { - // most non-hard fixtures shouldn't pass through portals, but projectiles are non-hard as well - // and they should still pass through - return ourId == PortalFixture && (other.Hard || otherId == ProjectileFixture); - } - - private void OnCollide(EntityUid uid, PortalComponent component, ref StartCollideEvent args) + private void OnCollide(Entity ent, ref StartCollideEvent args) { if (!ShouldCollide(args.OurFixtureId, args.OtherFixtureId, args.OurFixture, args.OtherFixture)) return; @@ -92,7 +93,7 @@ public abstract class SharedPortalSystem : EntitySystem if (Transform(subject).Anchored) return; - // break pulls before portal enter so we dont break shit + // break pulls before portal enter so we don't break shit if (TryComp(subject, out var pullable) && pullable.BeingPulled) { _pulling.TryStopPull(subject, pullable); @@ -110,33 +111,27 @@ public abstract class SharedPortalSystem : EntitySystem return; } - if (TryComp(uid, out var link)) + if (TryComp(ent, out var link)) { if (link.LinkedEntities.Count == 0) return; - // client can't predict outside of simple portal-to-portal interactions due to randomness involved - // --also can't predict if the target doesn't exist on the client / is outside of PVS - if (_netMan.IsClient) - { - var first = link.LinkedEntities.First(); - var exists = Exists(first); - if (link.LinkedEntities.Count != 1 || !exists || (exists && Transform(first).MapID == MapId.Nullspace)) - return; - } + // check prediction + if (_netMan.IsClient && !CanPredictTeleport((ent, link))) + return; // pick a target and teleport there var target = _random.Pick(link.LinkedEntities); if (HasComp(target)) { - // if target is a portal, signal that they shouldn't be immediately portaled back + // if target is a portal, signal that they shouldn't be immediately teleported back var timeout = EnsureComp(subject); - timeout.EnteredPortal = uid; + timeout.EnteredPortal = ent; Dirty(subject, timeout); } - TeleportEntity(uid, subject, Transform(target).Coordinates, target); + TeleportEntity(ent, subject, Transform(target).Coordinates, target); return; } @@ -144,49 +139,86 @@ public abstract class SharedPortalSystem : EntitySystem return; // no linked entity--teleport randomly - if (component.RandomTeleport) - TeleportRandomly(uid, subject, component); + if (ent.Comp.RandomTeleport) + TeleportRandomly(ent, subject); } - private void OnEndCollide(EntityUid uid, PortalComponent component, ref EndCollideEvent args) + private void OnEndCollide(Entity ent, ref EndCollideEvent args) { if (!ShouldCollide(args.OurFixtureId, args.OtherFixtureId, args.OurFixture, args.OtherFixture)) return; var subject = args.OtherEntity; - // if they came from (not us), remove the timeout - if (TryComp(subject, out var timeout) && timeout.EnteredPortal != uid) + // if they came from a different portal, remove the timeout + if (TryComp(subject, out var timeout) && timeout.EnteredPortal != ent) { RemCompDeferred(subject); } } - private void TeleportEntity(EntityUid portal, EntityUid subject, EntityCoordinates target, EntityUid? targetEntity = null, bool playSound = true, - PortalComponent? portalComponent = null) + /// + /// Checks if the colliding fixtures are the ones we want. + /// + /// + /// False if our fixture is not a portal fixture. + /// False if other fixture is not hard, but makes an exception for projectiles. + /// + private bool ShouldCollide(string ourId, string otherId, Fixture our, Fixture other) { - if (!Resolve(portal, ref portalComponent)) - return; + return ourId == PortalFixture && (other.Hard || otherId == ProjectileFixture); + } - var ourCoords = Transform(portal).Coordinates; + /// + /// Checks if the client is able to predict the teleport. + /// Client can't predict outside 1-to-1 portal-to-portal interactions due to randomness involved. + /// + /// + /// False if the linked entity count isn't 1. + /// False if the linked entity doesn't exist on the client / is outside PVS. + /// + private bool CanPredictTeleport(Entity portal) + { + var first = portal.Comp.LinkedEntities.First(); + var exists = Exists(first); + + if (!exists || + portal.Comp.LinkedEntities.Count != 1 || // 0 and >1 use RNG + exists && Transform(first).MapID == MapId.Nullspace) // The linked entity is most likely outside PVS + return false; + + return true; + } + + /// + /// Handles teleporting a subject from the portal entity to a coordinate. + /// Also deletes invalid portals. + /// + /// The portal being collided with. + /// The entity getting teleported. + /// The location to teleport to. + /// The portal on the other side of the teleport. + private void TeleportEntity(Entity ent, EntityUid subject, EntityCoordinates target, EntityUid? targetEntity = null, bool playSound = true) + { + var ourCoords = Transform(ent).Coordinates; var onSameMap = _transform.GetMapId(ourCoords) == _transform.GetMapId(target); - var distanceInvalid = portalComponent.MaxTeleportRadius != null + var distanceInvalid = ent.Comp.MaxTeleportRadius != null && ourCoords.TryDistance(EntityManager, target, out var distance) - && distance > portalComponent.MaxTeleportRadius; + && distance > ent.Comp.MaxTeleportRadius; - if (!onSameMap && !portalComponent.CanTeleportToOtherMaps || distanceInvalid) + // Early out if this is an invalid configuration + if (!onSameMap && !ent.Comp.CanTeleportToOtherMaps || distanceInvalid) { - if (!_netMan.IsServer) + if (_netMan.IsClient) return; - // Early out if this is an invalid configuration _popup.PopupCoordinates(Loc.GetString("portal-component-invalid-configuration-fizzle"), ourCoords, Filter.Pvs(ourCoords, entityMan: EntityManager), true); _popup.PopupCoordinates(Loc.GetString("portal-component-invalid-configuration-fizzle"), target, Filter.Pvs(target, entityMan: EntityManager), true); - QueueDel(portal); + QueueDel(ent); if (targetEntity != null) QueueDel(targetEntity.Value); @@ -194,8 +226,8 @@ public abstract class SharedPortalSystem : EntitySystem return; } - var arrivalSound = CompOrNull(targetEntity)?.ArrivalSound ?? portalComponent.ArrivalSound; - var departureSound = portalComponent.DepartureSound; + var arrivalSound = CompOrNull(targetEntity)?.ArrivalSound ?? ent.Comp.ArrivalSound; + var departureSound = ent.Comp.DepartureSound; // Some special cased stuff: projectiles should stop ignoring shooter when they enter a portal, to avoid // stacking 500 bullets in between 2 portals and instakilling people--you'll just hit yourself instead @@ -205,36 +237,41 @@ public abstract class SharedPortalSystem : EntitySystem projectile.IgnoreShooter = false; } - LogTeleport(portal, subject, Transform(subject).Coordinates, target); + LogTeleport(ent, subject, Transform(subject).Coordinates, target); _transform.SetCoordinates(subject, target); if (!playSound) return; - _audio.PlayPredicted(departureSound, portal, subject); + _audio.PlayPredicted(departureSound, ent, subject); _audio.PlayPredicted(arrivalSound, subject, subject); } - private void TeleportRandomly(EntityUid portal, EntityUid subject, PortalComponent? component = null) + /// + /// Finds a random coordinate within the portal's radius and teleports the subject there. + /// Attempts to not put the subject inside a static entity (e.g. wall). + /// + /// The portal being collided with. + /// The entity getting teleported. + private void TeleportRandomly(Entity ent, EntityUid subject) { - if (!Resolve(portal, ref component)) - return; - - var xform = Transform(portal); + var xform = Transform(ent); var coords = xform.Coordinates; - var newCoords = coords.Offset(_random.NextVector2(component.MaxRandomRadius)); + var newCoords = coords.Offset(_random.NextVector2(ent.Comp.MaxRandomRadius)); for (var i = 0; i < MaxRandomTeleportAttempts; i++) { - var randVector = _random.NextVector2(component.MaxRandomRadius); + var randVector = _random.NextVector2(ent.Comp.MaxRandomRadius); newCoords = coords.Offset(randVector); if (!_lookup.AnyEntitiesIntersecting(_transform.ToMapCoordinates(newCoords), LookupFlags.Static)) { + // newCoords is not a wall break; } + // after "MaxRandomTeleportAttempts" attempts, end up in the walls } - TeleportEntity(portal, subject, newCoords); + TeleportEntity(ent, subject, newCoords); } protected virtual void LogTeleport(EntityUid portal, EntityUid subject, EntityCoordinates source, diff --git a/Content.Shared/Throwing/ThrowingSystem.cs b/Content.Shared/Throwing/ThrowingSystem.cs index db68c3517c..6b121baf58 100644 --- a/Content.Shared/Throwing/ThrowingSystem.cs +++ b/Content.Shared/Throwing/ThrowingSystem.cs @@ -5,7 +5,6 @@ using Content.Shared.CCVar; using Content.Shared.Construction.Components; using Content.Shared.Database; using Content.Shared.Friction; -using Content.Shared.Gravity; using Content.Shared.Projectiles; using Robust.Shared.Configuration; using Robust.Shared.Map; @@ -30,7 +29,6 @@ public sealed class ThrowingSystem : EntitySystem private float _airDamping; [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly SharedGravitySystem _gravity = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly ThrownItemSystem _thrownSystem = default!; diff --git a/Content.Shared/Trigger/Components/Effects/ExplodeOnTriggerComponent.cs b/Content.Shared/Trigger/Components/Effects/ExplodeOnTriggerComponent.cs index 2a1af40a2c..9bb7ce9fa0 100644 --- a/Content.Shared/Trigger/Components/Effects/ExplodeOnTriggerComponent.cs +++ b/Content.Shared/Trigger/Components/Effects/ExplodeOnTriggerComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Explosion.Components; using Robust.Shared.GameStates; namespace Content.Shared.Trigger.Components.Effects; @@ -7,8 +8,6 @@ namespace Content.Shared.Trigger.Components.Effects; /// TargetUser will only work of the user has ExplosiveComponent as well. /// The User will be logged in the admin logs. /// -/// -/// TODO: Allow this to work without an ExplosiveComponent on the user via QueueExplosion. -/// +/// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class ExplodeOnTriggerComponent : BaseXOnTriggerComponent; diff --git a/Content.Shared/Trigger/Components/Effects/ExplosionOnTriggerComponent.cs b/Content.Shared/Trigger/Components/Effects/ExplosionOnTriggerComponent.cs new file mode 100644 index 0000000000..40b59e7a97 --- /dev/null +++ b/Content.Shared/Trigger/Components/Effects/ExplosionOnTriggerComponent.cs @@ -0,0 +1,46 @@ +using Content.Shared.Explosion; +using Content.Shared.Explosion.Components; +using Content.Shared.Explosion.EntitySystems; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Trigger.Components.Effects; + +// TODO some sort of struct like DamageSpecifier but for explosions. +/// +/// Will explode the entity using this component's explosion specifications. +/// If TargetUser is true, they'll explode instead. +/// The User will be logged in the admin logs. +/// +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ExplosionOnTriggerComponent : BaseXOnTriggerComponent +{ + /// + [DataField, AutoNetworkedField] + public ProtoId ExplosionType = SharedExplosionSystem.DefaultExplosionPrototypeId; + + /// + [DataField, AutoNetworkedField] + public float MaxTileIntensity = 4; + + /// + [DataField, AutoNetworkedField] + public float IntensitySlope = 1; + + /// + [DataField, AutoNetworkedField] + public float TotalIntensity = 10; + + /// + [DataField, AutoNetworkedField] + public float TileBreakScale = 1f; + + /// + [DataField, AutoNetworkedField] + public int MaxTileBreak = int.MaxValue; + + /// + [DataField, AutoNetworkedField] + public bool CanCreateVacuum = true; +} diff --git a/Content.Shared/Trigger/Components/Effects/WeatherOnTriggerComponent.cs b/Content.Shared/Trigger/Components/Effects/WeatherOnTriggerComponent.cs new file mode 100644 index 0000000000..44ce576f7b --- /dev/null +++ b/Content.Shared/Trigger/Components/Effects/WeatherOnTriggerComponent.cs @@ -0,0 +1,25 @@ +using Content.Shared.Weather; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Trigger.Components.Effects; + +/// +/// Changes the current weather when triggered. +/// If TargetUser is true then it will change the weather at the user's map instead of the entitys map. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class WeatherOnTriggerComponent : BaseXOnTriggerComponent +{ + /// + /// Weather type. Null to clear the weather. + /// + [DataField, AutoNetworkedField] + public ProtoId? Weather; + + /// + /// How long the weather should last. Null for forever. + /// + [DataField, AutoNetworkedField] + public TimeSpan? Duration; +} diff --git a/Content.Shared/Trigger/Components/Triggers/TriggerOnPlayerSpawnCompleteComponent.cs b/Content.Shared/Trigger/Components/Triggers/TriggerOnPlayerSpawnCompleteComponent.cs new file mode 100644 index 0000000000..2151b7edcc --- /dev/null +++ b/Content.Shared/Trigger/Components/Triggers/TriggerOnPlayerSpawnCompleteComponent.cs @@ -0,0 +1,11 @@ +using Content.Shared.GameTicking; +using Robust.Shared.GameStates; + +namespace Content.Shared.Trigger.Components.Triggers; + +/// +/// A trigger which occurs on . +/// +/// This does not work with , as it would add this component while the event is getting raised. +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class TriggerOnPlayerSpawnCompleteComponent : BaseTriggerOnXComponent; diff --git a/Content.Shared/Trigger/Systems/DnaScrambleOnTriggerSystem.cs b/Content.Shared/Trigger/Systems/DnaScrambleOnTriggerSystem.cs index 246c6a8c7a..db27bd7f74 100644 --- a/Content.Shared/Trigger/Systems/DnaScrambleOnTriggerSystem.cs +++ b/Content.Shared/Trigger/Systems/DnaScrambleOnTriggerSystem.cs @@ -13,7 +13,7 @@ public sealed class DnaScrambleOnTriggerSystem : EntitySystem { [Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly SharedHumanoidAppearanceSystem _humanoidAppearance = default!; - [Dependency] private readonly SharedIdentitySystem _identity = default!; + [Dependency] private readonly IdentitySystem _identity = default!; [Dependency] private readonly SharedForensicsSystem _forensics = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly INetManager _net = default!; diff --git a/Content.Shared/Trigger/Systems/ExplodeOnTriggerSystem.cs b/Content.Shared/Trigger/Systems/ExplodeOnTriggerSystem.cs index 1c773b79a6..120aa23a9d 100644 --- a/Content.Shared/Trigger/Systems/ExplodeOnTriggerSystem.cs +++ b/Content.Shared/Trigger/Systems/ExplodeOnTriggerSystem.cs @@ -11,10 +11,11 @@ public sealed class ExplodeOnTriggerSystem : EntitySystem { base.Initialize(); - SubscribeLocalEvent(OnTrigger); + SubscribeLocalEvent(OnExplodeTrigger); + SubscribeLocalEvent(OnQueueExplosionTrigger); } - private void OnTrigger(Entity ent, ref TriggerEvent args) + private void OnExplodeTrigger(Entity ent, ref TriggerEvent args) { if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key)) return; @@ -27,4 +28,27 @@ public sealed class ExplodeOnTriggerSystem : EntitySystem _explosion.TriggerExplosive(target.Value, user: args.User); args.Handled = true; } + + private void OnQueueExplosionTrigger(Entity ent, ref TriggerEvent args) + { + var (uid, comp) = ent; + if (args.Key != null && !comp.KeysIn.Contains(args.Key)) + return; + + var target = comp.TargetUser ? args.User : uid; + + if (target == null) + return; + + _explosion.QueueExplosion(target.Value, + comp.ExplosionType, + comp.TotalIntensity, + comp.IntensitySlope, + comp.MaxTileIntensity, + comp.TileBreakScale, + comp.MaxTileBreak, + comp.CanCreateVacuum, + args.User); + args.Handled = true; + } } diff --git a/Content.Shared/Trigger/Systems/TriggerSystem.Spawn.cs b/Content.Shared/Trigger/Systems/TriggerSystem.Spawn.cs index 83457361fd..8750110744 100644 --- a/Content.Shared/Trigger/Systems/TriggerSystem.Spawn.cs +++ b/Content.Shared/Trigger/Systems/TriggerSystem.Spawn.cs @@ -1,4 +1,5 @@ -using Content.Shared.Trigger.Components.Effects; +using Content.Shared.GameTicking; +using Content.Shared.Trigger.Components.Effects; using Content.Shared.Trigger.Components.Triggers; using Robust.Shared.Prototypes; @@ -9,6 +10,7 @@ public sealed partial class TriggerSystem private void InitializeSpawn() { SubscribeLocalEvent(OnSpawnInit); + SubscribeLocalEvent(OnPlayerSpawn); SubscribeLocalEvent(HandleSpawnOnTrigger); SubscribeLocalEvent(HandleSpawnTableOnTrigger); @@ -20,6 +22,11 @@ public sealed partial class TriggerSystem Trigger(ent.Owner, null, ent.Comp.KeyOut); } + private void OnPlayerSpawn(Entity ent, ref PlayerSpawnCompleteEvent args) + { + Trigger(ent.Owner, null, ent.Comp.KeyOut); + } + private void HandleSpawnOnTrigger(Entity ent, ref TriggerEvent args) { if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key)) diff --git a/Content.Shared/Trigger/Systems/WeatherTriggerSystem.cs b/Content.Shared/Trigger/Systems/WeatherTriggerSystem.cs new file mode 100644 index 0000000000..6343e08f0b --- /dev/null +++ b/Content.Shared/Trigger/Systems/WeatherTriggerSystem.cs @@ -0,0 +1,44 @@ +using Content.Shared.Trigger.Components.Effects; +using Content.Shared.Weather; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; + +namespace Content.Shared.Trigger.Systems; + +public sealed class WeatherTriggerSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly SharedWeatherSystem _weather = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnTrigger); + } + + private void OnTrigger(Entity ent, ref TriggerEvent args) + { + if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key)) + return; + + var target = ent.Comp.TargetUser ? args.User : ent.Owner; + + if (target == null) + return; + + var xform = Transform(target.Value); + + if (ent.Comp.Weather == null) //Clear weather if nothing is set + { + _weather.SetWeather(xform.MapID, null, null); + return; + } + + var endTime = ent.Comp.Duration == null ? null : ent.Comp.Duration + _timing.CurTime; + + if (_prototypeManager.Resolve(ent.Comp.Weather, out var weatherPrototype)) + _weather.SetWeather(xform.MapID, weatherPrototype, endTime); + } +} diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 088293cf87..20907405fe 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,48 +1,4 @@ Entries: -- author: qwerltaz - changes: - - message: Snipping the panic wire in an air alarm now forces panic mode until the - wire is mended. - type: Tweak - id: 8485 - time: '2025-05-14T20:39:47.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36439 -- author: IProduceWidgets - changes: - - message: more wizard name variety - type: Add - id: 8486 - time: '2025-05-14T21:06:56.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36437 -- author: archee1 - changes: - - message: Bike horns of all varieties have had their textures updated slightly. - type: Tweak - id: 8487 - time: '2025-05-14T22:05:45.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/37413 -- author: slarticodefast - changes: - - message: Fixed paradox clones not copying the mute, snoring and frontal lisp traits. - type: Fix - id: 8488 - time: '2025-05-15T01:45:48.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/37467 -- author: kosticia - changes: - - message: Hamsters, mice, butterflies, mothroaches, bees, bats, snails, rats, ticks - and cockroaches now don't leave organs on gibbing. - type: Fix - id: 8489 - time: '2025-05-15T03:45:50.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/37080 -- author: EmoGarbage404 - changes: - - message: Goliath tentacles no longer miss if you stand still. - type: Fix - id: 8490 - time: '2025-05-15T03:55:05.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/37168 - author: K-Dynamic changes: - message: Industrial advanced welders may be found in welding supplies lockers. @@ -3954,3 +3910,48 @@ id: 8995 time: '2025-09-22T06:40:14.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/40491 +- author: hoshizora-sayo + changes: + - message: Fixed fire helmets alone giving you full temperature protection + type: Fix + id: 8996 + time: '2025-09-23T17:02:50.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/40481 +- author: aada + changes: + - message: Circuit tiles and faux tiles have been moved to the cutter machine. + type: Tweak + id: 8997 + time: '2025-09-24T00:12:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/37982 +- author: Kittygyat + changes: + - message: Added 4 diagnostic huds to the engi-vend + type: Add + id: 8998 + time: '2025-09-24T04:19:38.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/40461 +- author: RedBookcase + changes: + - message: Rechargers can now charge power cells again. + type: Tweak + id: 8999 + time: '2025-09-24T20:33:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38138 +- author: Nox38, BurgerMoth + changes: + - message: Added descriptions to .20 ammo boxes, magazines, and cartridges. + type: Add + - message: Changed the descriptions of the Lecter, Estoc DMR, and M90GL. + type: Tweak + id: 9000 + time: '2025-09-24T21:48:34.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36496 +- author: Keer-Sar + changes: + - message: Added two new markings for lizard snouts, "Lizard Visage (Round)" & "Lizard + Visage (Sharp)." + type: Add + id: 9001 + time: '2025-09-24T23:13:14.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/35294 diff --git a/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl b/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl index b620fdff8c..6d952aea5b 100644 --- a/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl +++ b/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl @@ -1,8 +1,8 @@ -comp-kitchen-spike-begin-hook-self = You begin dragging yourself onto { THE($hook) }! +comp-kitchen-spike-begin-hook-self = You begin dragging yourself onto { THE($hook) }! comp-kitchen-spike-begin-hook-self-other = { CAPITALIZE(THE($victim)) } begins dragging { REFLEXIVE($victim) } onto { THE($hook) }! comp-kitchen-spike-begin-hook-other-self = You begin dragging { CAPITALIZE(THE($victim)) } onto { THE($hook) }! -comp-kitchen-spike-begin-hook-other = { CAPITALIZE(THE($user)) } begins dragging { CAPITALIZE(THE($victim)) } onto { THE($hook) }!a +comp-kitchen-spike-begin-hook-other = { CAPITALIZE(THE($user)) } begins dragging { CAPITALIZE(THE($victim)) } onto { THE($hook) }! comp-kitchen-spike-hook-self = You threw yourself on { THE($hook) }! comp-kitchen-spike-hook-self-other = { CAPITALIZE(THE($victim)) } threw { REFLEXIVE($victim) } on { THE($hook) }! diff --git a/Resources/Locale/en-US/lathe/lathe-categories.ftl b/Resources/Locale/en-US/lathe/lathe-categories.ftl index 0c96aa6edb..209daf1ad3 100644 --- a/Resources/Locale/en-US/lathe/lathe-categories.ftl +++ b/Resources/Locale/en-US/lathe/lathe-categories.ftl @@ -4,7 +4,6 @@ lathe-category-clothing = Clothing lathe-category-lights = Lights lathe-category-machines = Machines lathe-category-parts = Parts -lathe-category-tiles = Tiles lathe-category-tools = Tools lathe-category-weapons = Weapons @@ -24,13 +23,16 @@ lathe-category-service = Service lathe-category-supply = Supply # Cutter -lathe-category-concrete = Concrete -lathe-category-dark = Dark -lathe-category-maints = Maints -lathe-category-steel = Steel -lathe-category-white = White -lathe-category-wood = Wood +lathe-category-tiles = Tiles +lathe-category-circuit-tile = Circuit +lathe-category-concrete-tile = Concrete +lathe-category-dark-tile = Dark +lathe-category-faux-tile = Faux +lathe-category-maints-tile = Maints lathe-category-marble = Marble +lathe-category-steel-tile = Steel +lathe-category-white-tile = White +lathe-category-wood-tile = Wood # Science lathe-category-mechs = Mechs diff --git a/Resources/Locale/en-US/markings/reptilian.ftl b/Resources/Locale/en-US/markings/reptilian.ftl index c5b843109e..f7b7628907 100644 --- a/Resources/Locale/en-US/markings/reptilian.ftl +++ b/Resources/Locale/en-US/markings/reptilian.ftl @@ -117,3 +117,9 @@ marking-LizardChestFin = Lizard Fin marking-LizardSnoutSplotch = Lizard Snout (Splotch) marking-LizardSnoutSplotch-snout_splotch_primary = Muzzle marking-LizardSnoutSplotch-snout_splotch_secondary = Snoot + +marking-LizardSnoutVisageSharp = Lizard Visage (Sharp) +marking-LizardSnoutVisageSharp-visage_sharp = Lizard Visage (Sharp) + +marking-LizardSnoutVisageRound = Lizard Visage (Round) +marking-LizardSnoutVisageRound-visage_round = Lizard Visage (Round) diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/engivend.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/engivend.yml index 63089773b1..a558f646ca 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/engivend.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/engivend.yml @@ -2,6 +2,7 @@ id: EngiVendInventory startingInventory: ClothingEyesGlassesMeson: 4 + ClothingEyesHudDiagnostic: 4 ClothingHeadHatWelding: 6 CrowbarYellow: 8 Multitool: 4 diff --git a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml index f1d3ba838e..bfa54eaf77 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml @@ -273,8 +273,8 @@ quickEquip: true - type: IngestionBlocker - type: TemperatureProtection - heatingCoefficient: 0.01 - coolingCoefficient: 0.2 + heatingCoefficient: 0.5 + coolingCoefficient: 0.8 - type: FireProtection reduction: 0.15 # not fully sealed so less protection - type: IdentityBlocker @@ -301,8 +301,8 @@ quickEquip: true - type: IngestionBlocker - type: TemperatureProtection - heatingCoefficient: 0.01 - coolingCoefficient: 0.2 + heatingCoefficient: 0.3 + coolingCoefficient: 0.8 - type: FireProtection reduction: 0.2 - type: PressureProtection diff --git a/Resources/Prototypes/Entities/Effects/puddle.yml b/Resources/Prototypes/Entities/Effects/puddle.yml index 4e758e4e1c..dbde82b534 100644 --- a/Resources/Prototypes/Entities/Effects/puddle.yml +++ b/Resources/Prototypes/Entities/Effects/puddle.yml @@ -206,7 +206,7 @@ delay: 3 transferAmount: 1 solution: puddle - utensil: None + utensil: Spoon - type: ExaminableSolution solution: puddle locVolume: "examinable-solution-on-examine-volume-puddle" diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml index c58e5a3bb3..f9bae9ef1a 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml @@ -213,6 +213,24 @@ - sprite: Mobs/Customization/reptilian_parts.rsi state: snout_splotch_secondary +- type: marking + id: LizardSnoutVisageRound + bodyPart: Snout + markingCategory: Snout + speciesRestriction: [Reptilian] + sprites: + - sprite: Mobs/Customization/reptilian_parts.rsi + state: visage_round + +- type: marking + id: LizardSnoutVisageSharp + bodyPart: Snout + markingCategory: Snout + speciesRestriction: [Reptilian] + sprites: + - sprite: Mobs/Customization/reptilian_parts.rsi + state: visage_sharp + - type: marking id: LizardChestTiger bodyPart: Chest diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml index cbc557c34a..6fab208f0b 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml @@ -102,6 +102,7 @@ - CannotSuicide - FootstepSound - type: NoSlip + - type: Rummager - type: RatKing hungerPerArmyUse: 25 hungerPerDomainUse: 50 @@ -308,12 +309,16 @@ sprite: Mobs/Effects/onfire.rsi normalState: Mouse_burning -- type: weightedRandomEntity +- type: entityTable id: RatKingLoot - weights: - RandomSpawner100: 66 #garbage - FoodCheese: 28 #food - IngotGold1: 5 #loot + table: !type:GroupSelector + children: + - id: RandomSpawner100 + weight: 66 + - id: FoodCheese + weight: 28 + - id: IngotGold1 + weight: 5 - type: entity parent: BaseAction diff --git a/Resources/Prototypes/Entities/Mobs/Species/vulpkanin.yml b/Resources/Prototypes/Entities/Mobs/Species/vulpkanin.yml index cb9b19fffc..e34b295879 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/vulpkanin.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/vulpkanin.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity abstract: true save: false parent: [BaseMobSpeciesOrganic] @@ -172,9 +172,7 @@ - type: entity parent: [BaseSpeciesDummy] id: MobVulpkaninDummy - name: Vulpkanin Dummy categories: [ HideSpawnMenu ] - description: A dummy Vulpkanin meant to be used in character setup. components: - type: HumanoidAppearance species: Vulpkanin diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml index a1b69a0966..820814a468 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml @@ -9,7 +9,6 @@ - type: FlavorProfile flavors: - sweet - - type: Food - type: Sprite sprite: Objects/Consumable/Food/Baked/cake.rsi - type: SolutionContainerManager diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/donut.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/donut.yml index c13f52194f..2403aefe6d 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/donut.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/donut.yml @@ -6,7 +6,6 @@ abstract: true description: Goes great with robust coffee. components: - - type: Food - type: Tag tags: - Donut diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/egg.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/egg.yml index 7228a21366..20e61869b0 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/egg.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/egg.yml @@ -10,7 +10,7 @@ tags: - Egg - Meat - - type: Food + - type: Edible trash: - Eggshells - type: Sprite diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/frozen.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/frozen.yml index 7f96d07850..cc343d900b 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/frozen.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/frozen.yml @@ -1,4 +1,4 @@ -# When adding new food also add to random spawner located in Resources\Prototypes\Entities\Markers\Spawners\Random\Food_Drinks\food_snack.yml +# When adding new food also add to random spawner located in Resources\Prototypes\Entities\Markers\Spawners\Random\Food_Drinks\food_snack.yml # Base - type: entity @@ -63,7 +63,7 @@ - type: entity parent: FoodFrozenBase id: FoodFrozenCornuto - name: strawberry ice-cream sandwich + name: cornuto description: A Neapolitan vanilla and chocolate ice-cream cone. It menaces with a sprinkling of caramelized nuts. components: - type: Item diff --git a/Resources/Prototypes/Entities/Objects/Devices/chameleon_projector.yml b/Resources/Prototypes/Entities/Objects/Devices/chameleon_projector.yml index 0aaf48020e..9260574c66 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/chameleon_projector.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/chameleon_projector.yml @@ -15,10 +15,16 @@ tags: - Bot # for funny bot moments blacklist: - components: + components: # TODO: This blacklist should be cut down by a lot once Chameleon Projector code is less buggy. See #40510 - ChameleonDisguise # no becoming kleiner + - Door # no faking doors - MindContainer # no - Pda # PDAs currently make you invisible /!\ + - SubFloorHide # no hiding under the floor + tags: + - Catwalk # Catwalks make you invisible + - Wall # Walls make you invisible + - Window # Windows make you invisible disguiseProto: ChameleonDisguise - type: StaticPrice price: 5000 diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml index b10e071fd9..34aff04489 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml @@ -205,7 +205,7 @@ suffix: Full components: - type: Material - - type: Food + - type: Edible transferAmount: 10 - type: BadFood - type: PhysicalComposition diff --git a/Resources/Prototypes/Entities/Objects/Materials/materials.yml b/Resources/Prototypes/Entities/Objects/Materials/materials.yml index 9eeed7af80..4b7477eed3 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/materials.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/materials.yml @@ -226,13 +226,6 @@ state: durathread - type: Stack count: 1 - - type: SolutionContainerManager - solutions: - food: - maxVol: 5 - reagents: #Hell if I know what durathread is made out of. - - ReagentId: Fiber - Quantity: 6 - type: entity parent: MaterialBase diff --git a/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml b/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml index d25812a0b4..d9290a1a3d 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml @@ -598,7 +598,6 @@ - type: UIRequiresLock - type: ActivatableUI key: enum.AgentIDCardUiKey.Key - inHandsOnly: true verbText: agent-id-open-ui-verb - type: Tag tags: diff --git a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml index b92626e6a1..05d4215d3e 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml @@ -735,7 +735,7 @@ - type: Stack stackType: FloorTileMiningLight -# Departamental +# Departmental - type: entity name: freezer tile parent: FloorTileItemBase @@ -1321,32 +1321,6 @@ - type: Stack stackType: FloorTileRCircuit -# Circuits stacks - -- type: entity - parent: FloorTileItemGCircuit - id: FloorTileItemGCircuit4 - suffix: 4 - components: - - type: Stack - count: 4 - -- type: entity - parent: FloorTileItemBCircuit - id: FloorTileItemBCircuit4 - suffix: 4 - components: - - type: Stack - count: 4 - -- type: entity - parent: FloorTileItemRCircuit - id: FloorTileItemRCircuit4 - suffix: 4 - components: - - type: Stack - count: 4 - # Terrain - type: entity name: grass tile diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml index edc0040512..8dc7e2dde4 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity id: BaseBorgModule parent: BaseItem name: borg module @@ -1273,7 +1273,7 @@ - type: entity parent: [ BaseBorgModuleSyndicateAssault, BaseProviderBorgModule, BaseSyndicateContraband ] id: BorgModuleC20r - name: C20-r ROW cyborg module + name: C-20r ROW cyborg module description: A weapons module that comes with a burst-fire C-20r. components: - type: Sprite diff --git a/Resources/Prototypes/Entities/Objects/Tools/jammer.yml b/Resources/Prototypes/Entities/Objects/Tools/jammer.yml index 1fc42ad41f..2922a3d53e 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/jammer.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/jammer.yml @@ -56,6 +56,12 @@ id: XenoborgRadioJammer name: xenoborg radio jammer components: + - type: RadioJammer + frequenciesExcluded: + - 2002 # xenoborg radio + - 2003 # mothership radio + - 2004 # xenoborg network + - 2005 # mothership network - type: ItemSlots slots: cell_slot: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/rifle.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/rifle.yml index e631b9849a..794192c7e3 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/rifle.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/rifle.yml @@ -49,6 +49,7 @@ parent: BaseMagazineBoxRifle id: MagazineBoxRifle name: ammunition box (.20 rifle) + description: A cardboard box of .20 rifle rounds. Intended to hold general-purpose kinetic ammunition. components: - type: BallisticAmmoProvider proto: CartridgeRifle @@ -63,6 +64,7 @@ parent: BaseMagazineBoxRifle id: MagazineBoxRiflePractice name: ammunition box (.20 rifle practice) + description: A cardboard box of .20 rifle rounds. Intended to hold non-harmful chalk ammunition. components: - type: BallisticAmmoProvider proto: CartridgeRiflePractice @@ -78,6 +80,7 @@ id: MagazineBoxRifleIncendiary parent: BaseMagazineBoxRifle name: ammunition box (.20 rifle incendiary) + description: A cardboard box of .20 rifle rounds. Intended to hold self-igniting incendiary ammunition. components: - type: BallisticAmmoProvider proto: CartridgeRifleIncendiary @@ -93,6 +96,7 @@ id: MagazineBoxRifleUranium parent: BaseMagazineBoxRifle name: ammunition box (.20 rifle uranium) + description: A cardboard box of .20 rifle rounds. Intended to hold exotic uranium-core ammunition. components: - type: BallisticAmmoProvider proto: CartridgeRifleUranium diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/rifle.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/rifle.yml index 7e29dbe995..2559349c4a 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/rifle.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/rifle.yml @@ -22,6 +22,7 @@ id: CartridgeRifle name: cartridge (.20 rifle) parent: BaseCartridgeRifle + description: A modern intermediate cartridge for combat rifles. Standard kinetic ammunition is common and useful in most situations. components: - type: CartridgeAmmo proto: BulletRifle @@ -30,6 +31,7 @@ id: CartridgeRiflePractice name: cartridge (.20 rifle practice) parent: BaseCartridgeRifle + description: A modern intermediate cartridge for combat rifles. Chalk ammunition is generally non-harmful, used for practice. components: - type: CartridgeAmmo proto: BulletRiflePractice @@ -45,6 +47,7 @@ id: CartridgeRifleIncendiary name: cartridge (.20 rifle incendiary) parent: BaseCartridgeRifle + description: A modern intermediate cartridge for combat rifles. Incendiary ammunition contains a self-igniting compound that sets the target ablaze. components: - type: CartridgeAmmo proto: BulletRifleIncendiary @@ -60,6 +63,7 @@ id: CartridgeRifleUranium name: cartridge (.20 rifle uranium) parent: BaseCartridgeRifle + description: A modern intermediate cartridge for combat rifles. Uranium ammunition replaces the lead core of the bullet with fissile material, irradiating the target from the inside. components: - type: CartridgeAmmo proto: BulletRifleUranium diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/rifle.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/rifle.yml index 5a83b23475..8e05d114d7 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/rifle.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/rifle.yml @@ -35,6 +35,7 @@ id: MagazineRifle name: "magazine (.20 rifle)" parent: BaseMagazineRifle + description: 25-round double stack magazine for combat rifles. Intended to hold general-purpose kinetic ammunition. components: - type: BallisticAmmoProvider proto: CartridgeRifle @@ -56,6 +57,7 @@ name: "magazine (.20 rifle any)" suffix: empty parent: MagazineRifle + description: 25-round double stack magazine for combat rifles. components: - type: BallisticAmmoProvider proto: null @@ -76,6 +78,7 @@ id: MagazineRifleIncendiary name: "magazine (.20 rifle incendiary)" parent: MagazineRifle + description: 25-round double stack magazine for combat rifles. Intended to hold self-igniting incendiary ammunition. components: - type: BallisticAmmoProvider proto: CartridgeRifleIncendiary @@ -102,6 +105,7 @@ id: MagazineRiflePractice name: "magazine (.20 rifle practice)" parent: BaseMagazineRifle + description: 25-round double stack magazine for combat rifles. Intended to hold non-harmful chalk ammunition. components: - type: BallisticAmmoProvider proto: CartridgeRiflePractice @@ -128,6 +132,7 @@ id: MagazineRifleUranium name: "magazine (.20 rifle uranium)" parent: BaseMagazineRifle + description: 25-round double stack magazine for combat rifles. Intended to hold exotic uranium-core ammunition. components: - type: BallisticAmmoProvider proto: CartridgeRifleUranium diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml index fcb12ef711..6e5580e8a2 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml @@ -103,10 +103,10 @@ - type: Appearance - type: entity - name: M-90gl + name: M90 GL parent: [BaseWeaponRifle, BaseSyndicateContraband] id: WeaponRifleM90GrenadeLauncher - description: An older bullpup carbine model, with an attached underbarrel grenade launcher. Uses .20 rifle ammo. + description: "An older bullpup carbine model, with an attached underbarrel grenade launcher.\nFeeds from .20 rifle magazines." components: - type: Sprite sprite: Objects/Weapons/Guns/Rifles/carbine.rsi @@ -150,7 +150,7 @@ name: Lecter parent: [BaseWeaponRifle, BaseSecurityContraband] id: WeaponRifleLecter - description: A high end military grade assault rifle. Uses .20 rifle ammo. + description: "Popular gas-operated combat rifle used heavily by Nanotrasen and SolGov. Operating in semi or fully automatic, its accuracy, stopping power, and reliability make it excel in all manner of environments.\nFeeds from .20 rifle magazines." components: - type: Sprite sprite: Objects/Weapons/Guns/Rifles/lecter.rsi @@ -224,7 +224,7 @@ name: Estoc DMR parent: [BaseWeaponRifle, BaseSyndicateContraband] id: WeaponRifleEstoc - description: A designated marksman rifle, favored for medium-to-long range engagements. Uses .20 rifle ammo. + description: "A designated marksman rifle firing in 3-round bursts. The Estoc was designed as the Lecter’s long-range counterpart, equipped with an extended 20-inch barrel and telescopic sight.\nFeeds from .20 rifle magazines." components: - type: Sprite sprite: Objects/Weapons/Guns/Rifles/estoc.rsi diff --git a/Resources/Prototypes/Entities/Objects/base_contraband.yml b/Resources/Prototypes/Entities/Objects/base_contraband.yml index 942e36b0cd..fc7bb857f1 100644 --- a/Resources/Prototypes/Entities/Objects/base_contraband.yml +++ b/Resources/Prototypes/Entities/Objects/base_contraband.yml @@ -13,7 +13,7 @@ abstract: true components: - type: Contraband - severity: Major # placeholder until they make a better severity + severity: HighlyIllegal # any type of magical items used by wizards and similiar - type: entity diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml index 5f80b94250..c467f5297d 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity parent: AirlockRCDResistant id: AirlockShuttle suffix: Docking @@ -63,9 +63,7 @@ - type: entity id: AirlockGlassShuttle parent: AirlockShuttle - name: external airlock suffix: Glass, Docking - description: Necessary for connecting two space craft together. components: - type: Sprite sprite: Structures/Doors/Airlocks/Glass/shuttle.rsi @@ -101,9 +99,6 @@ - type: entity id: AirlockGlassShuttleSyndicate parent: AirlockGlassShuttle - name: external airlock - suffix: Glass, Docking - description: Necessary for connecting two space craft together. components: - type: Sprite sprite: Structures/Doors/Airlocks/Glass/shuttle_syndicate.rsi @@ -111,9 +106,6 @@ - type: entity parent: AirlockShuttle id: AirlockShuttleSyndicate - suffix: Docking - name: external airlock - description: Necessary for connecting two space craft together. components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/shuttle_syndicate.rsi @@ -121,9 +113,6 @@ - type: entity parent: AirlockShuttle id: AirlockShuttleXenoborg - suffix: Docking - name: external airlock - description: Necessary for connecting two space craft together. components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index 3af5a9d291..90c450e1c7 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -80,7 +80,9 @@ interfaces: enum.DisposalUnitUiKey.Key: type: DisposalUnitBoundUserInterface - - type: RatKingRummageable + - type: Rummageable + table: !type:NestedSelector + tableId: RatKingLoot - type: SolutionContainerManager solutions: drainBuffer: diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 4c3f7748d5..fb7fd0b8b2 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -209,7 +209,6 @@ - Janitor - Instruments - Equipment - - FauxTiles - type: EmagLatheRecipes emagDynamicPacks: - SecurityAmmo @@ -257,7 +256,6 @@ - CargoBoardsStatic - MedicalBoardsStatic - EngineeringBoardsStatic - - CircuitFloorsStatic dynamicPacks: - EngineeringBoards - CargoBoards @@ -689,7 +687,16 @@ idleState: icon runningState: building staticPacks: - - FloorTilesStatic + - FloorDarkTilesStatic + - FloorSteelTilesStatic + - FloorWhiteTilesStatic + - FloorMaintsTilesStatic + - FloorWoodTilesStatic + - FloorConcreteTilesStatic + - CircuitFloorsStatic + - FloorMarbleTilesStatic + dynamicPacks: + - FauxTiles - type: MaterialStorage whitelist: tags: diff --git a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml index 1420b304d9..a4841034d6 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml @@ -1,4 +1,4 @@ -# base +# base - type: entity id: VendingMachine @@ -951,13 +951,11 @@ parent: VendingMachineMedicalBase id: VendingMachineMedical name: NanoMed Plus - description: It's a medical drug dispenser. Natural chemicals only! components: - type: VendingMachine pack: NanoMedPlusInventory offState: off - type: Sprite - sprite: Structures/Machines/VendingMachines/medical.rsi layers: - state: "off" map: ["enum.VendingMachineVisualLayers.Base"] @@ -2267,7 +2265,7 @@ parent: VendingMachineWallmount id: VendingMachineWallMedicalCivilian name: NanoMed band-aid - description: "It's a wall-mounted medical equipment dispenser. Natural chemicals only!" + description: It's a wall-mounted medical equipment dispenser. Natural chemicals only! components: - type: VendingMachine pack: NanoMedCivilianWallInventory @@ -2297,13 +2295,11 @@ parent: VendingMachineWallMedicalCivilian id: VendingMachineWallMedical name: NanoMed - description: "It's a wall-mounted medical equipment dispenser. Natural chemicals only!" components: - type: VendingMachine pack: NanoMedInventory offState: off - type: Sprite - sprite: Structures/Machines/VendingMachines/wallmed.rsi layers: - state: "off" map: ["enum.VendingMachineVisualLayers.Base"] diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml index dda7d51b69..6a9f0b30bc 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml @@ -97,7 +97,9 @@ interfaces: enum.DisposalUnitUiKey.Key: type: DisposalUnitBoundUserInterface - - type: RatKingRummageable + - type: Rummageable + table: !type:NestedSelector + tableId: RatKingLoot - type: RequireProjectileTarget - type: entity diff --git a/Resources/Prototypes/Entities/Structures/Power/chargers.yml b/Resources/Prototypes/Entities/Structures/Power/chargers.yml index 75e38f1187..0bbe95efb5 100644 --- a/Resources/Prototypes/Entities/Structures/Power/chargers.yml +++ b/Resources/Prototypes/Entities/Structures/Power/chargers.yml @@ -72,6 +72,7 @@ parent: BaseItemRecharger id: PowerCellRecharger name: cell recharger + description: An older model recharger that can recharge power cells. components: - type: Sprite sprite: Structures/Power/cell_recharger.rsi @@ -105,11 +106,15 @@ tags: - PowerCell - PowerCellSmall + blacklist: + tags: + - PotatoBattery - type: entity parent: [ BaseItemRecharger, ConstructibleMachine ] id: PowerCageRecharger name: cage recharger + description: A specialized machine made for recharging the heavy cage batteries used by ship-mounted weapons. components: - type: Fixtures fixtures: @@ -150,12 +155,12 @@ parent: BaseItemRecharger id: WeaponCapacitorRecharger name: recharger + description: A modern recharging station that can fit both power cells and small electronic devices. components: - type: Sprite sprite: Structures/Power/recharger.rsi - type: Machine board: WeaponCapacitorRechargerCircuitboard - # no powercellslot since stun baton etc arent powercells - type: ItemSlots slots: charger_slot: @@ -165,12 +170,16 @@ - HitscanBatteryAmmoProvider - ProjectileBatteryAmmoProvider - Stunbaton + - PowerCell + blacklist: + tags: + - PotatoBattery - type: entity parent: BaseItemRecharger id: TurboItemRecharger name: turbo recharger - description: An overclocked recharger that's been adapted with a global port. + description: An overclocked recharger. Not recommended for use around asthmatics. components: - type: Sprite sprite: Structures/Power/turbo_recharger.rsi @@ -196,6 +205,7 @@ parent: [ BaseItemRecharger, BaseWallmount ] id: WallWeaponCapacitorRecharger name: wall recharger + description: A compact wall-mounted recharger. It can only recharge electronic devices and has no space for power cells. components: - type: Sprite sprite: Structures/Power/wall_recharger.rsi @@ -216,6 +226,9 @@ - HitscanBatteryAmmoProvider - ProjectileBatteryAmmoProvider - Stunbaton + blacklist: + tags: + - PotatoBattery - type: entity parent: BaseRecharger diff --git a/Resources/Prototypes/Recipes/Lathes/Packs/science.yml b/Resources/Prototypes/Recipes/Lathes/Packs/science.yml index ae8dab762d..e71a2e28c8 100644 --- a/Resources/Prototypes/Recipes/Lathes/Packs/science.yml +++ b/Resources/Prototypes/Recipes/Lathes/Packs/science.yml @@ -10,13 +10,6 @@ - CutterMachineCircuitboard - BorgChargerCircuitboard -- type: latheRecipePack - id: CircuitFloorsStatic - recipes: - - FloorGreenCircuit - - FloorBlueCircuit - - FloorRedCircuit - ## Dynamic - type: latheRecipePack @@ -62,19 +55,6 @@ - WeaponTetherGun - WeaponGauntletGorilla -- type: latheRecipePack - id: FauxTiles - recipes: - - FauxTileAstroGrass - - FauxTileMowedAstroGrass - - FauxTileJungleAstroGrass - - FauxTileDarkAstroGrass - - FauxTileLightAstroGrass - - FauxTileAstroIce - - FauxTileAstroSnow - - FauxTileAstroAsteroidSand - - FauxTileDesertAstroSand - # Only contains parts for making basic modular grenades, no actual explosives - type: latheRecipePack id: ScienceExplosives diff --git a/Resources/Prototypes/Recipes/Lathes/Packs/tiles.yml b/Resources/Prototypes/Recipes/Lathes/Packs/tiles.yml index 94ffd376e0..a3286709cb 100644 --- a/Resources/Prototypes/Recipes/Lathes/Packs/tiles.yml +++ b/Resources/Prototypes/Recipes/Lathes/Packs/tiles.yml @@ -1,7 +1,7 @@ ## Static - type: latheRecipePack - id: FloorTilesStatic + id: FloorDarkTilesStatic recipes: - FloorTileItemDark - FloorTileItemDarkDiagonalMini @@ -12,8 +12,10 @@ - FloorTileItemDarkPavement - FloorTileItemDarkPavementVertical - FloorTileItemDarkOffset - - FloorTileItemDarkSquiggly - - FloorTileItemSteelCheckerDark + +- type: latheRecipePack + id: FloorSteelTilesStatic + recipes: - FloorTileItemSteel - FloorTileItemSteelOffset - FloorTileItemSteelDiagonalMini @@ -23,6 +25,12 @@ - FloorTileItemSteelMono - FloorTileItemSteelPavement - FloorTileItemSteelPavementVertical + - FloorTileItemSteelCheckerDark + - FloorTileItemSteelCheckerLight + +- type: latheRecipePack + id: FloorWhiteTilesStatic + recipes: - FloorTileItemWhite - FloorTileItemWhiteOffset - FloorTileItemWhiteDiagonalMini @@ -32,14 +40,25 @@ - FloorTileItemWhiteMono - FloorTileItemWhitePavement - FloorTileItemWhitePavementVertical - - FloorTileItemSteelCheckerLight + +- type: latheRecipePack + id: FloorMaintsTilesStatic + recipes: - FloorTileItemGratingMaint - FloorTileItemTechmaint - FloorTileItemSteelMaint - FloorTileItemTechmaintDark + +- type: latheRecipePack + id: FloorWoodTilesStatic + recipes: - FloorTileItemWood - FloorTileItemWoodLarge - FloorTileItemWoodPattern + +- type: latheRecipePack + id: FloorConcreteTilesStatic + recipes: - FloorTileItemConcrete - FloorTileItemConcreteMono - FloorTileItemConcreteSmooth @@ -49,5 +68,31 @@ - FloorTileItemOldConcrete - FloorTileItemOldConcreteMono - FloorTileItemOldConcreteSmooth + +- type: latheRecipePack + id: CircuitFloorsStatic + recipes: + - FloorGreenCircuit + - FloorBlueCircuit + - FloorRedCircuit + +- type: latheRecipePack + id: FloorMarbleTilesStatic + recipes: - FloorTileItemWhiteMarble - FloorTileItemDarkMarble + +## Dynamic + +- type: latheRecipePack + id: FauxTiles + recipes: + - FauxTileAstroGrass + - FauxTileMowedAstroGrass + - FauxTileJungleAstroGrass + - FauxTileDarkAstroGrass + - FauxTileLightAstroGrass + - FauxTileAstroIce + - FauxTileAstroSnow + - FauxTileAstroAsteroidSand + - FauxTileDesertAstroSand diff --git a/Resources/Prototypes/Recipes/Lathes/categories.yml b/Resources/Prototypes/Recipes/Lathes/categories.yml index 7c9bfb93a4..fd25394e67 100644 --- a/Resources/Prototypes/Recipes/Lathes/categories.yml +++ b/Resources/Prototypes/Recipes/Lathes/categories.yml @@ -19,10 +19,6 @@ id: Parts name: lathe-category-parts -- type: latheCategory - id: Tiles - name: lathe-category-tiles - - type: latheCategory id: Tools name: lathe-category-tools @@ -78,29 +74,41 @@ name: lathe-category-supply # Cutter machine +- type: latheCategory + id: Tiles + name: lathe-category-tiles + +- type: latheCategory + id: Circuit + name: lathe-category-circuit-tile + - type: latheCategory id: Concrete - name: lathe-category-concrete + name: lathe-category-concrete-tile - type: latheCategory id: Dark - name: lathe-category-dark + name: lathe-category-dark-tile + +- type: latheCategory + id: Faux + name: lathe-category-faux-tile - type: latheCategory id: Maints - name: lathe-category-maints + name: lathe-category-maints-tile - type: latheCategory id: Steel - name: lathe-category-steel + name: lathe-category-steel-tile - type: latheCategory id: White - name: lathe-category-white + name: lathe-category-white-tile - type: latheCategory id: Wood - name: lathe-category-wood + name: lathe-category-wood-tile - type: latheCategory id: Marble diff --git a/Resources/Prototypes/Recipes/Lathes/misc.yml b/Resources/Prototypes/Recipes/Lathes/misc.yml index 1633811124..206a8856e3 100644 --- a/Resources/Prototypes/Recipes/Lathes/misc.yml +++ b/Resources/Prototypes/Recipes/Lathes/misc.yml @@ -10,13 +10,6 @@ Steel: 50 Glass: 50 -- type: latheRecipe - abstract: true - id: BaseFauxTileRecipe - completetime: 1 - materials: - Plastic: 100 - # Recipes ## Lights @@ -185,75 +178,6 @@ Steel: 750 Plastic: 100 -- type: latheRecipe - parent: BaseFauxTileRecipe - id: FauxTileAstroGrass - result: FloorTileItemAstroGrass - -- type: latheRecipe - parent: BaseFauxTileRecipe - id: FauxTileMowedAstroGrass - result: FloorTileItemMowedAstroGrass - -- type: latheRecipe - parent: BaseFauxTileRecipe - id: FauxTileJungleAstroGrass - result: FloorTileItemJungleAstroGrass - -- type: latheRecipe - parent: BaseFauxTileRecipe - id: FauxTileDarkAstroGrass - result: FloorTileItemDarkAstroGrass - -- type: latheRecipe - parent: BaseFauxTileRecipe - id: FauxTileLightAstroGrass - result: FloorTileItemLightAstroGrass - -- type: latheRecipe - parent: BaseFauxTileRecipe - id: FauxTileAstroIce - result: FloorTileItemAstroIce - -- type: latheRecipe - parent: BaseFauxTileRecipe - id: FauxTileAstroSnow - result: FloorTileItemAstroSnow - -- type: latheRecipe - parent: BaseFauxTileRecipe - id: FauxTileAstroAsteroidSand - result: FloorTileItemAstroAsteroidSand - -- type: latheRecipe - parent: BaseFauxTileRecipe - id: FauxTileAstroAsteroidSandBorderless - result: FloorTileItemAstroAsteroidSandBorderless - -- type: latheRecipe - parent: BaseFauxTileRecipe - id: FauxTileDesertAstroSand - result: FloorTileItemDesertAstroSand - -- type: latheRecipe - id: FloorGreenCircuit - result: FloorTileItemGCircuit4 - completetime: 2 - materials: - Steel: 100 - -- type: latheRecipe - parent: FloorGreenCircuit - id: FloorBlueCircuit - result: FloorTileItemBCircuit4 - -- type: latheRecipe - id: FloorRedCircuit - result: FloorTileItemRCircuit4 - completetime: 2 - materials: - Steel: 100 - - type: latheRecipe id: HandheldStationMap result: HandheldStationMapEmpty diff --git a/Resources/Prototypes/Recipes/Lathes/tiles.yml b/Resources/Prototypes/Recipes/Lathes/tiles.yml index b0e6d63f68..a28f1b7a50 100644 --- a/Resources/Prototypes/Recipes/Lathes/tiles.yml +++ b/Resources/Prototypes/Recipes/Lathes/tiles.yml @@ -13,6 +13,7 @@ parent: BaseTileRecipe id: BaseSteelTileRecipe categories: + - Tiles - Steel materials: Steel: 25 @@ -22,6 +23,7 @@ parent: BaseSteelTileRecipe id: BaseDarkTileRecipe categories: + - Tiles - Dark - type: latheRecipe @@ -29,6 +31,7 @@ parent: BaseSteelTileRecipe id: BaseWhiteTileRecipe categories: + - Tiles - White - type: latheRecipe @@ -36,13 +39,23 @@ parent: BaseSteelTileRecipe id: BaseMaintTileRecipe categories: + - Tiles - Maints +- type: latheRecipe + abstract: true + parent: BaseSteelTileRecipe + id: BaseCircuitTileRecipe + categories: + - Tiles + - Circuit + - type: latheRecipe abstract: true parent: BaseTileRecipe id: BaseWoodTileRecipe categories: + - Tiles - Wood materials: Wood: 25 @@ -52,16 +65,28 @@ parent: BaseTileRecipe id: BaseConcreteTileRecipe categories: + - Tiles - Concrete materials: Steel: 25 Plastic: 25 +- type: latheRecipe + abstract: true + parent: BaseTileRecipe + id: BaseFauxTileRecipe + categories: + - Tiles + - Faux + materials: + Plastic: 100 + - type: latheRecipe abstract: true parent: BaseTileRecipe id: BaseMarbleTileRecipe categories: + - Tiles - Marble materials: Steel: 25 @@ -222,7 +247,7 @@ id: FloorTileItemWhitePavementVertical result: FloorTileItemWhitePavementVertical -# Other steel +# Maints - type: latheRecipe parent: BaseMaintTileRecipe id: FloorTileItemGratingMaint @@ -243,6 +268,22 @@ id: FloorTileItemTechmaintDark result: FloorTileItemTechmaintDark +# Circuit +- type: latheRecipe + parent: BaseCircuitTileRecipe + id: FloorGreenCircuit + result: FloorTileItemGCircuit + +- type: latheRecipe + parent: BaseCircuitTileRecipe + id: FloorBlueCircuit + result: FloorTileItemBCircuit + +- type: latheRecipe + parent: BaseCircuitTileRecipe + id: FloorRedCircuit + result: FloorTileItemRCircuit + # Wood - type: latheRecipe parent: BaseWoodTileRecipe @@ -305,6 +346,57 @@ id: FloorTileItemOldConcreteSmooth result: FloorTileItemOldConcreteSmooth +# Faux +- type: latheRecipe + parent: BaseFauxTileRecipe + id: FauxTileAstroGrass + result: FloorTileItemAstroGrass + +- type: latheRecipe + parent: BaseFauxTileRecipe + id: FauxTileMowedAstroGrass + result: FloorTileItemMowedAstroGrass + +- type: latheRecipe + parent: BaseFauxTileRecipe + id: FauxTileJungleAstroGrass + result: FloorTileItemJungleAstroGrass + +- type: latheRecipe + parent: BaseFauxTileRecipe + id: FauxTileDarkAstroGrass + result: FloorTileItemDarkAstroGrass + +- type: latheRecipe + parent: BaseFauxTileRecipe + id: FauxTileLightAstroGrass + result: FloorTileItemLightAstroGrass + +- type: latheRecipe + parent: BaseFauxTileRecipe + id: FauxTileAstroIce + result: FloorTileItemAstroIce + +- type: latheRecipe + parent: BaseFauxTileRecipe + id: FauxTileAstroSnow + result: FloorTileItemAstroSnow + +- type: latheRecipe + parent: BaseFauxTileRecipe + id: FauxTileAstroAsteroidSand + result: FloorTileItemAstroAsteroidSand + +- type: latheRecipe + parent: BaseFauxTileRecipe + id: FauxTileAstroAsteroidSandBorderless + result: FloorTileItemAstroAsteroidSandBorderless + +- type: latheRecipe + parent: BaseFauxTileRecipe + id: FauxTileDesertAstroSand + result: FloorTileItemDesertAstroSand + # Marble - type: latheRecipe parent: BaseMarbleTileRecipe diff --git a/Resources/Prototypes/Tiles/floors.yml b/Resources/Prototypes/Tiles/floors.yml index 894fe29ec6..61e6b37b8d 100644 --- a/Resources/Prototypes/Tiles/floors.yml +++ b/Resources/Prototypes/Tiles/floors.yml @@ -780,7 +780,7 @@ itemDrop: FloorTileItemMiningLight heatCapacity: 10000 -# Departamental +# Departmental - type: tile id: FloorFreezer name: tiles-freezer diff --git a/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/meta.json b/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/meta.json index 46f717d063..71c37e9153 100644 --- a/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/meta.json +++ b/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/meta.json @@ -1,8 +1,8 @@ { "version": 1, "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/Skyrat-SS13/Skyrat-tg/tree/40e3cdbb15b8bc0d5ef2fb46133adf805bda5297, while Argali, Ayrshire, Myrsore, Bighorn and Demonic are drawn by Ubaser, and Kobold Ears are drawn by Pigeonpeas. Back fin by alzore_ (Discord), Aquatic tail modified from smooth tail by alzore_ (discord). Body_underbelly made by Nairod(github) for SS14. Large drawn by Ubaser. Wagging tail by SonicDC. Splotch modified from Sharp by KittenColony(github). Frills neckfull come from: https://github.com/Bubberstation/Bubberstation/commit/8bc6b83404803466a560b694bf22ef3c0ac266a2, large wag by TiniestShark (github), Lizard Visage (Round & sharp) by Keer-Sar.", "copyright": "https://github.com/Skyrat-SS13/Skyrat-tg/tree/40e3cdbb15b8bc0d5ef2fb46133adf805bda5297, while Argali, Ayrshire, Myrsore and Bighorn are drawn by Ubaser, and Kobold Ears are drawn by Pigeonpeas. Body_underbelly made by Nairod(github) for SS14. Large drawn by Ubaser. Wagging tail by SonicDC. Splotch modified from Sharp by KittenColony(github). Frills neckfull come from: https://github.com/Bubberstation/Bubberstation/commit/8bc6b83404803466a560b694bf22ef3c0ac266a2, resprited by @mishutka09", - "copyright": "https://github.com/Skyrat-SS13/Skyrat-tg/tree/40e3cdbb15b8bc0d5ef2fb46133adf805bda5297, while Argali, Ayrshire, Myrsore, Bighorn and Demonic are drawn by Ubaser, and Kobold Ears are drawn by Pigeonpeas. Back fin by alzore_ (Discord), Aquatic tail modified from smooth tail by alzore_ (discord). Body_underbelly made by Nairod(github) for SS14. Large drawn by Ubaser. Wagging tail by SonicDC. Splotch modified from Sharp by KittenColony(github). Frills neckfull come from: https://github.com/Bubberstation/Bubberstation/commit/8bc6b83404803466a560b694bf22ef3c0ac266a2, large wag by TiniestShark (github)", "size": { "x": 32, "y": 32 @@ -789,6 +789,14 @@ { "name": "frills_neckfull", "directions": 4 + }, + { + "name": "visage_round", + "directions": 4 + }, + { + "name": "visage_sharp", + "directions": 4 } ] } diff --git a/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/visage_round.png b/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/visage_round.png new file mode 100644 index 0000000000..59abf4b069 Binary files /dev/null and b/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/visage_round.png differ diff --git a/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/visage_sharp.png b/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/visage_sharp.png new file mode 100644 index 0000000000..738af2da79 Binary files /dev/null and b/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/visage_sharp.png differ diff --git a/Resources/Textures/Structures/Power/recharger.rsi/light-empty.png b/Resources/Textures/Structures/Power/recharger.rsi/light-empty.png index 1c2d4ea6fc..1bbf6b9542 100644 Binary files a/Resources/Textures/Structures/Power/recharger.rsi/light-empty.png and b/Resources/Textures/Structures/Power/recharger.rsi/light-empty.png differ diff --git a/Resources/migration.yml b/Resources/migration.yml index e7ad8e3237..75a0fb027b 100644 --- a/Resources/migration.yml +++ b/Resources/migration.yml @@ -674,6 +674,11 @@ SpawnHonkBot: SpawnMobHonkBot CrateFoodPizzaLarge: CrateFoodPizza CrateFoodSoftdrinksLarge: CrateFoodSoftdrinks +# 2025-06-03 +FloorTileItemGCircuit4: FloorTileItemGCircuit +FloorTileItemBCircuit4: FloorTileItemBCircuit +FloorTileItemRCircuit4: FloorTileItemRCircuit + # 2025-06-06 MagazineLightRifleMaxim: null MagazineLightRiflePkBox: null