diff --git a/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs b/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs index 86cf0a9eb8..78185ce6b0 100644 --- a/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs +++ b/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs @@ -1,5 +1,4 @@ using Content.Client.Atmos.Overlays; -using Content.Shared.Atmos; using Content.Shared.Atmos.Components; using Content.Shared.Atmos.EntitySystems; using JetBrains.Annotations; @@ -37,38 +36,28 @@ namespace Content.Client.Atmos.EntitySystems private void OnHandleState(EntityUid gridUid, GasTileOverlayComponent comp, ref ComponentHandleState args) { - Dictionary modifiedChunks; + if (args.Current is not GasTileOverlayState state) + return; - switch (args.Current) + // is this a delta or full state? + if (!state.FullState) { - // is this a delta or full state? - case GasTileOverlayDeltaState delta: + foreach (var index in comp.Chunks.Keys) { - modifiedChunks = delta.ModifiedChunks; - foreach (var index in comp.Chunks.Keys) - { - if (!delta.AllChunks.Contains(index)) - comp.Chunks.Remove(index); - } - - break; + if (!state.AllChunks!.Contains(index)) + comp.Chunks.Remove(index); } - case GasTileOverlayState state: + } + else + { + foreach (var index in comp.Chunks.Keys) { - modifiedChunks = state.Chunks; - foreach (var index in comp.Chunks.Keys) - { - if (!state.Chunks.ContainsKey(index)) - comp.Chunks.Remove(index); - } - - break; + if (!state.Chunks.ContainsKey(index)) + comp.Chunks.Remove(index); } - default: - return; } - foreach (var (index, data) in modifiedChunks) + foreach (var (index, data) in state.Chunks) { comp.Chunks[index] = data; } diff --git a/Content.Client/Decals/DecalSystem.cs b/Content.Client/Decals/DecalSystem.cs index 41e5f39c28..901ab270fb 100644 --- a/Content.Client/Decals/DecalSystem.cs +++ b/Content.Client/Decals/DecalSystem.cs @@ -56,43 +56,34 @@ namespace Content.Client.Decals private void OnHandleState(EntityUid gridUid, DecalGridComponent gridComp, ref ComponentHandleState args) { + if (args.Current is not DecalGridState state) + return; + // is this a delta or full state? _removedChunks.Clear(); - Dictionary modifiedChunks; - switch (args.Current) + if (!state.FullState) { - case DecalGridDeltaState delta: + foreach (var key in gridComp.ChunkCollection.ChunkCollection.Keys) { - modifiedChunks = delta.ModifiedChunks; - foreach (var key in gridComp.ChunkCollection.ChunkCollection.Keys) - { - if (!delta.AllChunks.Contains(key)) - _removedChunks.Add(key); - } - - break; + if (!state.AllChunks!.Contains(key)) + _removedChunks.Add(key); } - case DecalGridState state: + } + else + { + foreach (var key in gridComp.ChunkCollection.ChunkCollection.Keys) { - modifiedChunks = state.Chunks; - foreach (var key in gridComp.ChunkCollection.ChunkCollection.Keys) - { - if (!state.Chunks.ContainsKey(key)) - _removedChunks.Add(key); - } - - break; + if (!state.Chunks.ContainsKey(key)) + _removedChunks.Add(key); } - default: - return; } if (_removedChunks.Count > 0) RemoveChunks(gridUid, gridComp, _removedChunks); - if (modifiedChunks.Count > 0) - UpdateChunks(gridUid, gridComp, modifiedChunks); + if (state.Chunks.Count > 0) + UpdateChunks(gridUid, gridComp, state.Chunks); } private void OnChunkUpdate(DecalChunkUpdateEvent ev) diff --git a/Content.Client/Examine/ExamineSystem.cs b/Content.Client/Examine/ExamineSystem.cs index b787e2e573..0bd82ea9c0 100644 --- a/Content.Client/Examine/ExamineSystem.cs +++ b/Content.Client/Examine/ExamineSystem.cs @@ -239,10 +239,8 @@ namespace Content.Client.Examine if (knowTarget) { - // TODO: FormattedMessage.RemoveMarkupPermissive - // var itemName = FormattedMessage.RemoveMarkupPermissive(Identity.Name(target, EntityManager, player)); - var itemName = FormattedMessage.FromMarkupPermissive(Identity.Name(target, EntityManager, player)).ToString(); - var labelMessage = FormattedMessage.FromMarkupPermissive($"[bold]{itemName}[/bold]"); + var itemName = FormattedMessage.RemoveMarkup(Identity.Name(target, EntityManager, player)); + var labelMessage = FormattedMessage.FromMarkup($"[bold]{itemName}[/bold]"); var label = new RichTextLabel(); label.SetMessage(labelMessage); hBox.AddChild(label); diff --git a/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs b/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs index 0f5729f55b..2a846ff708 100644 --- a/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs +++ b/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs @@ -37,8 +37,14 @@ namespace Content.Client.Instruments.UI protected override void ReceiveMessage(BoundUserInterfaceMessage message) { - if (message is InstrumentBandResponseBuiMessage bandRx) - _bandMenu?.Populate(bandRx.Nearby, EntMan); + switch (message) + { + case InstrumentBandResponseBuiMessage bandRx: + _bandMenu?.Populate(bandRx.Nearby, EntMan); + break; + default: + break; + } } protected override void Open() diff --git a/Content.Client/Interactable/InteractionSystem.cs b/Content.Client/Interactable/InteractionSystem.cs index ff0a607920..0af8830e9a 100644 --- a/Content.Client/Interactable/InteractionSystem.cs +++ b/Content.Client/Interactable/InteractionSystem.cs @@ -4,6 +4,24 @@ using Robust.Shared.Containers; namespace Content.Client.Interactable { - // TODO Remove Shared prefix - public sealed class InteractionSystem : SharedInteractionSystem; + public sealed class InteractionSystem : SharedInteractionSystem + { + [Dependency] private readonly SharedContainerSystem _container = default!; + + public override bool CanAccessViaStorage(EntityUid user, EntityUid target) + { + if (!EntityManager.EntityExists(target)) + return false; + + if (!_container.TryGetContainingContainer(target, out var container)) + return false; + + if (!HasComp(container.Owner)) + return false; + + // we don't check if the user can access the storage entity itself. This should be handed by the UI system. + // Need to return if UI is open or not + return true; + } + } } diff --git a/Content.Client/Interaction/DragDropHelper.cs b/Content.Client/Interaction/DragDropHelper.cs index e453dfd74b..abe35bf6d9 100644 --- a/Content.Client/Interaction/DragDropHelper.cs +++ b/Content.Client/Interaction/DragDropHelper.cs @@ -73,6 +73,11 @@ public sealed class DragDropHelper _cfg.OnValueChanged(CCVars.DragDropDeadZone, SetDeadZone, true); } + ~DragDropHelper() + { + _cfg.UnsubValueChanged(CCVars.DragDropDeadZone, SetDeadZone); + } + /// /// Tell the helper that the mouse button was pressed down on /// a target, thus a drag has the possibility to begin for this target. diff --git a/Content.Client/Options/UI/OptionsMenu.xaml b/Content.Client/Options/UI/OptionsMenu.xaml index 4f624c1bb6..7f44a5a554 100644 --- a/Content.Client/Options/UI/OptionsMenu.xaml +++ b/Content.Client/Options/UI/OptionsMenu.xaml @@ -1,11 +1,13 @@ + MinSize="980 580"> + + diff --git a/Content.Client/Options/UI/OptionsMenu.xaml.cs b/Content.Client/Options/UI/OptionsMenu.xaml.cs index 35a3f751bb..f174764de6 100644 --- a/Content.Client/Options/UI/OptionsMenu.xaml.cs +++ b/Content.Client/Options/UI/OptionsMenu.xaml.cs @@ -1,15 +1,17 @@ +using Content.Client.Administration.Managers; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; -using Robust.Shared.IoC; using Content.Client.Options.UI.Tabs; - +using Robust.Shared.Timing; namespace Content.Client.Options.UI { [GenerateTypedNameReferences] public sealed partial class OptionsMenu : DefaultWindow { + [Dependency] private readonly IClientAdminManager _clientAdminManager = default!; + public OptionsMenu() { RobustXamlLoader.Load(this); @@ -19,6 +21,8 @@ namespace Content.Client.Options.UI Tabs.SetTabTitle(1, Loc.GetString("ui-options-tab-graphics")); Tabs.SetTabTitle(2, Loc.GetString("ui-options-tab-controls")); Tabs.SetTabTitle(3, Loc.GetString("ui-options-tab-audio")); + Tabs.SetTabTitle(4, Loc.GetString("ui-options-tab-network")); + Tabs.SetTabTitle(5, "Админ"); UpdateTabs(); } @@ -27,5 +31,11 @@ namespace Content.Client.Options.UI { GraphicsTab.UpdateProperties(); } + + protected override void FrameUpdate(FrameEventArgs args) + { + Tabs.SetTabVisible(5, _clientAdminManager.IsActive()); + base.FrameUpdate(args); + } } } diff --git a/Content.Client/Options/UI/Tabs/NetworkTab.xaml b/Content.Client/Options/UI/Tabs/NetworkTab.xaml new file mode 100644 index 0000000000..67c412b08b --- /dev/null +++ b/Content.Client/Options/UI/Tabs/NetworkTab.xaml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + public async Task SetAntagPref(ProtoId id, bool value) { + /* Впадлу фиксить тесты var prefMan = Server.ResolveDependency(); var prefs = prefMan.GetPreferences(Client.User!.Value); @@ -155,5 +156,6 @@ public sealed partial class TestPair var newPrefs = prefMan.GetPreferences(Client.User.Value); var newProf = (HumanoidCharacterProfile) newPrefs.SelectedCharacter; Assert.That(newProf.AntagPreferences.Contains(id), Is.EqualTo(value)); + */ } } diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index 54af64122b..d3b1fb4722 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -350,12 +350,8 @@ namespace Content.IntegrationTests.Tests "DebrisFeaturePlacerController", // Above. "LoadedChunk", // Worldgen chunk loading malding. "BiomeSelection", // Whaddya know, requires config. - "ActivatableUI", // Requires enum key }; - // TODO TESTS - // auto ignore any components that have a "required" data field. - await using var pair = await PoolManager.GetServerClient(); var server = pair.Server; var entityManager = server.ResolveDependency(); diff --git a/Content.Server/Access/Systems/IdCardConsoleSystem.cs b/Content.Server/Access/Systems/IdCardConsoleSystem.cs index ce575ff784..3974998769 100644 --- a/Content.Server/Access/Systems/IdCardConsoleSystem.cs +++ b/Content.Server/Access/Systems/IdCardConsoleSystem.cs @@ -44,7 +44,8 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem if (args.Actor is not { Valid: true } player) return; - TryWriteToTargetId(uid, args.FullName, args.JobTitle, args.AccessList, args.JobPrototype, args.SelectedIcon, player, component); + TryWriteToTargetId(uid, args.FullName, args.JobTitle, args.AccessList, args.JobPrototype, args.SelectedIcon, + player, component); UpdateUserInterface(uid, component, args); } @@ -152,9 +153,6 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem _idCard.TryChangeJobDepartment(targetId, job); } - UpdateStationRecord(uid, targetId, newFullName, newJobTitle, job, newJobIcon); - - if (!newAccessList.TrueForAll(x => component.AccessLevels.Contains(x))) { _sawmill.Warning($"User {ToPrettyString(uid)} tried to write unknown access tag."); diff --git a/Content.Server/Antag/AntagObjectivesSystem.cs b/Content.Server/Antag/AntagObjectivesSystem.cs deleted file mode 100644 index 5aa31f66f6..0000000000 --- a/Content.Server/Antag/AntagObjectivesSystem.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Content.Server.Antag.Components; -using Content.Server.Objectives; -using Content.Shared.Mind; -using Content.Shared.Objectives.Systems; - -namespace Content.Server.Antag; - -/// -/// Adds fixed objectives to an antag made with AntagObjectivesComponent. -/// -public sealed class AntagObjectivesSystem : EntitySystem -{ - [Dependency] private readonly SharedMindSystem _mind = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnAntagSelected); - } - - private void OnAntagSelected(Entity ent, ref AfterAntagEntitySelectedEvent args) - { - if (!_mind.TryGetMind(args.Session, out var mindId, out var mind)) - { - Log.Error($"Antag {ToPrettyString(args.EntityUid):player} was selected by {ToPrettyString(ent):rule} but had no mind attached!"); - return; - } - - foreach (var id in ent.Comp.Objectives) - { - _mind.TryAddObjective(mindId, mind, id); - } - } -} diff --git a/Content.Server/Antag/AntagRandomObjectivesSystem.cs b/Content.Server/Antag/AntagRandomObjectivesSystem.cs deleted file mode 100644 index c935b8c064..0000000000 --- a/Content.Server/Antag/AntagRandomObjectivesSystem.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Content.Server.Antag.Components; -using Content.Server.Objectives; -using Content.Shared.Mind; -using Content.Shared.Objectives.Components; -using Content.Shared.Objectives.Systems; -using Robust.Shared.Random; - -namespace Content.Server.Antag; - -/// -/// Adds fixed objectives to an antag made with AntagRandomObjectivesComponent. -/// -public sealed class AntagRandomObjectivesSystem : EntitySystem -{ - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; - [Dependency] private readonly ObjectivesSystem _objectives = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnAntagSelected); - } - - private void OnAntagSelected(Entity ent, ref AfterAntagEntitySelectedEvent args) - { - if (!_mind.TryGetMind(args.Session, out var mindId, out var mind)) - { - Log.Error($"Antag {ToPrettyString(args.EntityUid):player} was selected by {ToPrettyString(ent):rule} but had no mind attached!"); - return; - } - - var difficulty = 0f; - foreach (var set in ent.Comp.Sets) - { - if (!_random.Prob(set.Prob)) - continue; - - for (var pick = 0; pick < set.MaxPicks && ent.Comp.MaxDifficulty > difficulty; pick++) - { - if (_objectives.GetRandomObjective(mindId, mind, set.Groups) is not {} objective) - continue; - - _mind.AddObjective(mindId, mind, objective); - var adding = Comp(objective).Difficulty; - difficulty += adding; - Log.Debug($"Added objective {ToPrettyString(objective):objective} to {ToPrettyString(args.EntityUid):player} with {adding} difficulty"); - } - } - } -} diff --git a/Content.Server/Antag/AntagSelectionSystem.API.cs b/Content.Server/Antag/AntagSelectionSystem.API.cs index 976458ba92..14492ded4e 100644 --- a/Content.Server/Antag/AntagSelectionSystem.API.cs +++ b/Content.Server/Antag/AntagSelectionSystem.API.cs @@ -147,7 +147,7 @@ public sealed partial class AntagSelectionSystem } /// - /// Helper to get just the mind entities and not names. + /// Helper specifically for /// public List GetAntagMindEntityUids(Entity ent) { diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index a46d4171b7..a7ff0810e1 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -7,7 +7,6 @@ using Content.Server.GameTicking.Rules; using Content.Server.Ghost.Roles; using Content.Server.Ghost.Roles.Components; using Content.Server.Mind; -using Content.Server.Objectives; using Content.Server.Preferences.Managers; using Content.Server.Roles; using Content.Server.Roles.Jobs; @@ -26,11 +25,10 @@ using Robust.Shared.Enums; using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Random; -using Robust.Shared.Utility; using Content.Server._Miracle.GulagSystem; using Content.Server._White.Sponsors; using Content.Server.Inventory; -using Content.Shared.GameTicking; +using FastAccessors; using Robust.Shared.Utility; namespace Content.Server.Antag; @@ -61,8 +59,6 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem(OnTakeGhostRole); - SubscribeLocalEvent(OnObjectivesTextGetInfo); - SubscribeLocalEvent(OnPlayerSpawning); SubscribeLocalEvent(OnJobsAssigned); SubscribeLocalEvent(OnSpawnComplete); @@ -460,15 +456,6 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem ent, ref ObjectivesTextGetInfoEvent args) - { - if (ent.Comp.AgentName is not {} name) - return; - - args.Minds = ent.Comp.SelectedMinds; - args.AgentName = Loc.GetString(name); - } - public float GetPremiumPoolChance(ICommonSession session) { if (!_sponsors.TryGetInfo(session.UserId, out var info)) diff --git a/Content.Server/Antag/Components/AntagObjectivesComponent.cs b/Content.Server/Antag/Components/AntagObjectivesComponent.cs deleted file mode 100644 index 357c138f46..0000000000 --- a/Content.Server/Antag/Components/AntagObjectivesComponent.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Content.Server.Antag; -using Content.Shared.Objectives.Components; -using Robust.Shared.Prototypes; - -namespace Content.Server.Antag.Components; - -/// -/// Gives antags selected by this rule a fixed list of objectives. -/// -[RegisterComponent, Access(typeof(AntagObjectivesSystem))] -public sealed partial class AntagObjectivesComponent : Component -{ - /// - /// List of static objectives to give. - /// - [DataField(required: true)] - public List> Objectives = new(); -} diff --git a/Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs b/Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs deleted file mode 100644 index 9a551acc49..0000000000 --- a/Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Content.Server.Antag; -using Content.Shared.Random; -using Robust.Shared.Prototypes; - -namespace Content.Server.Antag.Components; - -/// -/// Gives antags selected by this rule a random list of objectives. -/// -[RegisterComponent, Access(typeof(AntagRandomObjectivesSystem))] -public sealed partial class AntagRandomObjectivesComponent : Component -{ - /// - /// Each set of objectives to add. - /// - [DataField(required: true)] - public List Sets = new(); - - /// - /// If the total difficulty of the currently given objectives exceeds, no more will be given. - /// - [DataField(required: true)] - public float MaxDifficulty; -} - -/// -/// A set of objectives to try picking. -/// Difficulty is checked over all sets, but each set has its own probability and pick count. -/// -[DataRecord] -public record struct AntagObjectiveSet() -{ - /// - /// The grouping used by the objective system to pick random objectives. - /// First a group is picked from these, then an objective from that group. - /// - [DataField(required: true)] - public ProtoId Groups = string.Empty; - - /// - /// Probability of this set being used. - /// - [DataField] - public float Prob = 1f; - - /// - /// Number of times to try picking objectives from this set. - /// Even if there is enough difficulty remaining, no more will be given after this. - /// - [DataField] - public int MaxPicks = 20; -} diff --git a/Content.Server/Antag/Components/AntagSelectionComponent.cs b/Content.Server/Antag/Components/AntagSelectionComponent.cs index 61b0eb0611..d8fdd2be95 100644 --- a/Content.Server/Antag/Components/AntagSelectionComponent.cs +++ b/Content.Server/Antag/Components/AntagSelectionComponent.cs @@ -41,13 +41,6 @@ public sealed partial class AntagSelectionComponent : Component /// Is not serialized. /// public HashSet SelectedSessions = new(); - - /// - /// Locale id for the name of the antag. - /// If this is set then the antag is listed in the round-end summary. - /// - [DataField] - public LocId? AgentName; } [DataDefinition] diff --git a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs index 1ee8e8dbdb..2dc8ecf201 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs @@ -20,8 +20,6 @@ namespace Content.Server.Cargo.Systems { public sealed partial class CargoSystem { - [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - /// /// How much time to wait (in seconds) before increasing bank accounts balance. /// @@ -496,9 +494,6 @@ namespace Content.Server.Cargo.Systems // Create the item itself var item = Spawn(order.ProductId, spawn); - // Ensure the item doesn't start anchored - _transformSystem.Unanchor(item, Transform(item)); - // Create a sheet of paper to write the order details on var printed = EntityManager.SpawnEntity(paperProto, spawn); if (TryComp(printed, out var paper)) diff --git a/Content.Server/Changeling/ChangelingRuleSystem.cs b/Content.Server/Changeling/ChangelingRuleSystem.cs index f8f2d4ea6e..ae88a7db57 100644 --- a/Content.Server/Changeling/ChangelingRuleSystem.cs +++ b/Content.Server/Changeling/ChangelingRuleSystem.cs @@ -1,11 +1,9 @@ -using System.Linq; using Content.Server._Miracle.GulagSystem; using Content.Server.Antag; using Content.Server.GameTicking.Rules; using Content.Server.Mind; using Content.Server.Objectives; using Content.Shared._White.Mood; -using Content.Shared.Mind; using Content.Shared.Objectives.Components; namespace Content.Server.Changeling; @@ -32,7 +30,7 @@ public sealed class ChangelingRuleSystem : GameRuleSystem (mindId, Comp(mindId).CharacterName ?? "?")).ToList(); + args.Minds = comp.ChangelingMinds; args.AgentName = Loc.GetString("changeling-round-end-agent-name"); } diff --git a/Content.Server/Chemistry/ReagentEffects/CauseZombieInfection.cs b/Content.Server/Chemistry/ReagentEffects/CauseZombieInfection.cs index 96c57f7465..029b149500 100644 --- a/Content.Server/Chemistry/ReagentEffects/CauseZombieInfection.cs +++ b/Content.Server/Chemistry/ReagentEffects/CauseZombieInfection.cs @@ -2,7 +2,6 @@ using Content.Server.Zombies; using Content.Shared.Chemistry.Reagent; using Robust.Shared.Configuration; using Robust.Shared.Prototypes; -using Content.Shared.Zombies; namespace Content.Server.Chemistry.ReagentEffects; diff --git a/Content.Server/Chemistry/ReagentEffects/CureZombieInfection.cs b/Content.Server/Chemistry/ReagentEffects/CureZombieInfection.cs index 20e2c015c4..d56fc11531 100644 --- a/Content.Server/Chemistry/ReagentEffects/CureZombieInfection.cs +++ b/Content.Server/Chemistry/ReagentEffects/CureZombieInfection.cs @@ -2,7 +2,6 @@ using Content.Server.Zombies; using Content.Shared.Chemistry.Reagent; using Robust.Shared.Configuration; using Robust.Shared.Prototypes; -using Content.Shared.Zombies; namespace Content.Server.Chemistry.ReagentEffects; diff --git a/Content.Server/Configurable/ConfigurationSystem.cs b/Content.Server/Configurable/ConfigurationSystem.cs index 5f5f1ef7d1..2683bf4e09 100644 --- a/Content.Server/Configurable/ConfigurationSystem.cs +++ b/Content.Server/Configurable/ConfigurationSystem.cs @@ -24,7 +24,6 @@ public sealed class ConfigurationSystem : EntitySystem private void OnInteractUsing(EntityUid uid, ConfigurationComponent component, InteractUsingEvent args) { - // TODO use activatable ui system if (args.Handled) return; diff --git a/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs b/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs index 402d005dd4..3460124158 100644 --- a/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs @@ -63,21 +63,9 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem SubscribeLocalEvent(OnToggleLinks); SubscribeLocalEvent(OnConfigButtonPressed); - SubscribeLocalEvent(OnUiRangeCheck); - SubscribeLocalEvent(OnComponentRemoved); } - private void OnUiRangeCheck(Entity ent, ref BoundUserInterfaceCheckRangeEvent args) - { - if (ent.Comp.ActiveDeviceList == null || args.Result == BoundUserInterfaceRangeResult.Fail) - return; - - DebugTools.Assert(Exists(ent.Comp.ActiveDeviceList)); - if (!_interactionSystem.InRangeUnobstructed(args.Actor!, ent.Comp.ActiveDeviceList.Value)) - args.Result = BoundUserInterfaceRangeResult.Fail; - } - private void OnShutdown(EntityUid uid, NetworkConfiguratorComponent component, ComponentShutdown args) { ClearDevices(uid, component); @@ -87,6 +75,23 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem component.ActiveDeviceList = null; } + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var component)) + { + if (component.ActiveDeviceList != null + && EntityManager.EntityExists(component.ActiveDeviceList.Value) + && _interactionSystem.InRangeUnobstructed(uid, component.ActiveDeviceList.Value)) + continue; + + //The network configurator is a handheld device. There can only ever be an ui session open for the player holding the device. + _uiSystem.CloseUi(uid, NetworkConfiguratorUiKey.Configure); + } + } + private void OnMapInit(EntityUid uid, NetworkConfiguratorComponent component, MapInitEvent args) { UpdateListUiState(uid, component); diff --git a/Content.Server/GameTicking/Rules/AntagLoadProfileRuleSystem.cs b/Content.Server/GameTicking/Rules/AntagLoadProfileRuleSystem.cs deleted file mode 100644 index fd3fb6cd65..0000000000 --- a/Content.Server/GameTicking/Rules/AntagLoadProfileRuleSystem.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Content.Server.Antag; -using Content.Server.GameTicking.Rules.Components; -using Content.Server.Humanoid; -using Content.Server.Preferences.Managers; -using Content.Shared.Humanoid; -using Content.Shared.Humanoid.Prototypes; -using Content.Shared.Preferences; -using Robust.Shared.Prototypes; - -namespace Content.Server.GameTicking.Rules; - -public sealed class AntagLoadProfileRuleSystem : GameRuleSystem -{ - [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly IServerPreferencesManager _prefs = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnSelectEntity); - } - - private void OnSelectEntity(Entity ent, ref AntagSelectEntityEvent args) - { - if (args.Handled) - return; - - var profile = args.Session != null - ? _prefs.GetPreferences(args.Session.UserId).SelectedCharacter as HumanoidCharacterProfile - : HumanoidCharacterProfile.RandomWithSpecies(); - if (profile?.Species is not {} speciesId || !_proto.TryIndex(speciesId, out var species)) - species = _proto.Index(SharedHumanoidAppearanceSystem.DefaultSpecies); - - args.Entity = Spawn(species.Prototype); - _humanoid.LoadProfile(args.Entity.Value, profile); - } -} diff --git a/Content.Server/GameTicking/Rules/Components/AntagLoadProfileRuleCOmponent.cs b/Content.Server/GameTicking/Rules/Components/AntagLoadProfileRuleCOmponent.cs deleted file mode 100644 index 5e58fd14fc..0000000000 --- a/Content.Server/GameTicking/Rules/Components/AntagLoadProfileRuleCOmponent.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Content.Server.GameTicking.Rules.Components; - -/// -/// Makes this rules antags spawn a humanoid, either from the player's profile or a random one. -/// -[RegisterComponent] -public sealed partial class AntagLoadProfileRuleComponent : Component; diff --git a/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs index 6ad1e17775..01a078625a 100644 --- a/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs @@ -8,4 +8,23 @@ namespace Content.Server.GameTicking.Rules.Components; /// Stores data for . /// [RegisterComponent, Access(typeof(ThiefRuleSystem))] -public sealed partial class ThiefRuleComponent : Component; +public sealed partial class ThiefRuleComponent : Component +{ + [DataField] + public ProtoId BigObjectiveGroup = "ThiefBigObjectiveGroups"; + + [DataField] + public ProtoId SmallObjectiveGroup = "ThiefObjectiveGroups"; + + [DataField] + public ProtoId EscapeObjectiveGroup = "ThiefEscapeObjectiveGroups"; + + [DataField] + public float BigObjectiveChance = 0.7f; + + [DataField] + public float MaxObjectiveDifficulty = 2.5f; + + [DataField] + public int MaxStealObjectives = 10; +} diff --git a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs index 50d2a15d34..76ef518215 100644 --- a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs @@ -23,6 +23,9 @@ public sealed partial class TraitorRuleComponent : Component [DataField] public ProtoId SyndicateFaction = "Syndicate"; + [DataField] + public ProtoId ObjectiveGroup = "TraitorObjectiveGroups"; + [DataField] public ProtoId CodewordAdjectives = "adjectives"; @@ -75,4 +78,7 @@ public sealed partial class TraitorRuleComponent : Component /// [DataField] public int StartingBalance = 20; + + [DataField] + public int MaxDifficulty = 5; } diff --git a/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs b/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs index 0367aa1460..81bdda706b 100644 --- a/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs @@ -1,8 +1,6 @@ using Content.Server.GameTicking.Rules.Components; using Content.Server.Objectives; -using Content.Shared.Mind; using System.Diagnostics.CodeAnalysis; -using System.Linq; namespace Content.Server.GameTicking.Rules; @@ -49,8 +47,7 @@ public sealed class GenericAntagRuleSystem : GameRuleSystem (mindId, Comp(mindId).CharacterName ?? "?")).ToList(); + args.Minds = comp.Minds; args.AgentName = Loc.GetString(comp.AgentName); } } diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index bdf43aacd8..b7af4cbb59 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -1,9 +1,11 @@ using Content.Server.Antag; using Content.Server.Communications; using Content.Server.GameTicking.Rules.Components; +using Content.Server.Humanoid; using Content.Server.Nuke; using Content.Server.NukeOps; using Content.Server.Popups; +using Content.Server.Preferences.Managers; using Content.Server.Roles; using Content.Server.RoundEnd; using Content.Server.Shuttles.Events; @@ -11,39 +13,37 @@ using Content.Server.Shuttles.Systems; using Content.Server.Station.Components; using Content.Server.Store.Components; using Content.Server.Store.Systems; +using Content.Shared.Humanoid; +using Content.Shared.Humanoid.Prototypes; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.NPC.Components; using Content.Shared.NPC.Systems; using Content.Shared.Nuke; using Content.Shared.NukeOps; +using Content.Shared.Preferences; using Content.Shared.Store; using Content.Shared.Tag; using Content.Shared.Zombies; using Robust.Shared.Map; +using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; using System.Linq; using Content.Server.GameTicking.Components; -using Content.Server.Humanoid; -using Content.Server.Preferences.Managers; using Content.Server.StationEvents.Components; using Content.Shared._White.Antag; using Content.Shared.FixedPoint; -using Content.Shared.Humanoid; -using Content.Shared.Humanoid.Prototypes; using Content.Shared.Mind; -using Content.Shared.Preferences; -using Robust.Shared.Prototypes; namespace Content.Server.GameTicking.Rules; public sealed class NukeopsRuleSystem : GameRuleSystem { - [Dependency] private readonly IServerPreferencesManager _prefs = default!; - [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IServerPreferencesManager _prefs = default!; [Dependency] private readonly EmergencyShuttleSystem _emergency = default!; + [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!; [Dependency] private readonly NpcFactionSystem _npcFaction = default!; [Dependency] private readonly AntagSelectionSystem _antag = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; @@ -75,6 +75,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem SubscribeLocalEvent(OnWarDeclared); SubscribeLocalEvent(OnShuttleCallAttempt); + SubscribeLocalEvent(OnAntagSelectEntity); SubscribeLocalEvent(OnAfterAntagEntSelected); } diff --git a/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs b/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs index faec4a9e9c..083085fa0d 100644 --- a/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs @@ -24,6 +24,7 @@ public sealed class ThiefRuleSystem : GameRuleSystem SubscribeLocalEvent(AfterAntagSelected); SubscribeLocalEvent(OnGetBriefing); + SubscribeLocalEvent(OnObjectivesTextGetInfo); } private void AfterAntagSelected(Entity ent, ref AfterAntagEntitySelectedEvent args) @@ -32,9 +33,41 @@ public sealed class ThiefRuleSystem : GameRuleSystem return; //Generate objectives + GenerateObjectives(mindId, mind, ent); _antag.SendBriefing(args.EntityUid, MakeBriefing(args.EntityUid), null, null); } + private void GenerateObjectives(EntityUid mindId, MindComponent mind, ThiefRuleComponent thiefRule) + { + // Give thieves their objectives + var difficulty = 0f; + + if (_random.Prob(thiefRule.BigObjectiveChance)) // 70% chance to 1 big objective (structure or animal) + { + var objective = _objectives.GetRandomObjective(mindId, mind, thiefRule.BigObjectiveGroup); + if (objective != null) + { + _mindSystem.AddObjective(mindId, mind, objective.Value); + difficulty += Comp(objective.Value).Difficulty; + } + } + + for (var i = 0; i < thiefRule.MaxStealObjectives && thiefRule.MaxObjectiveDifficulty > difficulty; i++) // Many small objectives + { + var objective = _objectives.GetRandomObjective(mindId, mind, thiefRule.SmallObjectiveGroup); + if (objective == null) + continue; + + _mindSystem.AddObjective(mindId, mind, objective.Value); + difficulty += Comp(objective.Value).Difficulty; + } + + //Escape target + var escapeObjective = _objectives.GetRandomObjective(mindId, mind, thiefRule.EscapeObjectiveGroup); + if (escapeObjective != null) + _mindSystem.AddObjective(mindId, mind, escapeObjective.Value); + } + //Add mind briefing private void OnGetBriefing(Entity thief, ref GetBriefingEvent args) { @@ -54,4 +87,10 @@ public sealed class ThiefRuleSystem : GameRuleSystem briefing += "\n \n" + Loc.GetString("thief-role-greeting-equipment") + "\n"; return briefing; } + + private void OnObjectivesTextGetInfo(Entity ent, ref ObjectivesTextGetInfoEvent args) + { + args.Minds = _antag.GetAntagMindEntityUids(ent.Owner); + args.AgentName = Loc.GetString("thief-round-end-agent-name"); + } } diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index b87e26780b..bf24cfac7d 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -38,12 +38,15 @@ public sealed class TraitorRuleSystem : GameRuleSystem [Dependency] private readonly IChatManager _chatManager = default!; [Dependency] private readonly GameTicker _gameTicker = default!; + public const int MaxPicks = 20; + public override void Initialize() { base.Initialize(); SubscribeLocalEvent(AfterEntitySelected); + SubscribeLocalEvent(OnObjectivesTextGetInfo); SubscribeLocalEvent(OnObjectivesTextPrepend); } @@ -71,7 +74,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem } } - public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true) + public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true, bool giveObjectives = true) { //Grab the mind if it wasnt provided if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind)) @@ -124,16 +127,37 @@ public sealed class TraitorRuleSystem : GameRuleSystem if (richAspect) // WD TraitorRichAspect.NotifyTraitor(mind, _chatManager); + // Give traitors their objectives + if (giveObjectives) + { + var difficulty = 0f; + for (var pick = 0; pick < MaxPicks && component.MaxDifficulty > difficulty; pick++) + { + var objective = _objectives.GetRandomObjective(mindId, mind, component.ObjectiveGroup); + if (objective == null) + continue; + + _mindSystem.AddObjective(mindId, mind, objective.Value); + var adding = Comp(objective.Value).Difficulty; + difficulty += adding; + Log.Debug($"Added objective {ToPrettyString(objective):objective} with {adding} difficulty"); + } + } + return true; } - // TODO: AntagCodewordsComponent + private void OnObjectivesTextGetInfo(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextGetInfoEvent args) + { + args.Minds = _antag.GetAntagMindEntityUids(uid); + args.AgentName = Loc.GetString("traitor-round-end-agent-name"); + } + private void OnObjectivesTextPrepend(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextPrependEvent args) { args.Text += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", comp.Codewords))); } - // TODO: figure out how to handle this? add priority to briefing event? private string GenerateBriefing(string[] codewords, Note[]? uplinkCode, string? objectiveIssuer = null) { var sb = new StringBuilder(); diff --git a/Content.Server/Interaction/InteractionSystem.cs b/Content.Server/Interaction/InteractionSystem.cs index 9ac82b2185..4eac7e9ef1 100644 --- a/Content.Server/Interaction/InteractionSystem.cs +++ b/Content.Server/Interaction/InteractionSystem.cs @@ -7,6 +7,31 @@ using Robust.Shared.Player; namespace Content.Server.Interaction { - // TODO Remove Shared prefix - public sealed class InteractionSystem : SharedInteractionSystem; + /// + /// Governs interactions during clicking on entities + /// + [UsedImplicitly] + public sealed partial class InteractionSystem : SharedInteractionSystem + { + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + + public override bool CanAccessViaStorage(EntityUid user, EntityUid target) + { + if (Deleted(target)) + return false; + + if (!_container.TryGetContainingContainer(target, out var container)) + return false; + + if (!TryComp(container.Owner, out StorageComponent? storage)) + return false; + + if (storage.Container?.ID != container.ID) + return false; + + // we don't check if the user can access the storage entity itself. This should be handed by the UI system. + return _uiSystem.IsUiOpen(container.Owner, StorageComponent.StorageUiKey.Key, user); + } + } } diff --git a/Content.Shared/Lock/ActivatableUIRequiresLockComponent.cs b/Content.Server/Lock/Components/ActivatableUIRequiresLockComponent.cs similarity index 66% rename from Content.Shared/Lock/ActivatableUIRequiresLockComponent.cs rename to Content.Server/Lock/Components/ActivatableUIRequiresLockComponent.cs index 7d701ffd87..dac677c1c2 100644 --- a/Content.Shared/Lock/ActivatableUIRequiresLockComponent.cs +++ b/Content.Server/Lock/Components/ActivatableUIRequiresLockComponent.cs @@ -1,17 +1,15 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Lock; +namespace Content.Server.Lock.Components; /// /// This is used for activatable UIs that require the entity to have a lock in a certain state. /// -[RegisterComponent, NetworkedComponent, Access(typeof(LockSystem))] +[RegisterComponent] public sealed partial class ActivatableUIRequiresLockComponent : Component { /// /// TRUE: the lock must be locked to access the UI. /// FALSE: the lock must be unlocked to access the UI. /// - [DataField] - public bool RequireLocked; + [DataField("requireLocked"), ViewVariables(VVAccess.ReadWrite)] + public bool requireLocked = false; } diff --git a/Content.Server/Lock/EntitySystems/ActivatableUIRequiresLockSystem.cs b/Content.Server/Lock/EntitySystems/ActivatableUIRequiresLockSystem.cs new file mode 100644 index 0000000000..04f8e2eb54 --- /dev/null +++ b/Content.Server/Lock/EntitySystems/ActivatableUIRequiresLockSystem.cs @@ -0,0 +1,43 @@ +using Content.Server.Lock.Components; +using Content.Server.Popups; +using Content.Shared.UserInterface; +using Content.Shared.Lock; +using Content.Server.UserInterface; +using ActivatableUISystem = Content.Shared.UserInterface.ActivatableUISystem; + +namespace Content.Server.Lock.EntitySystems; +public sealed class ActivatableUIRequiresLockSystem : EntitySystem +{ + [Dependency] private readonly ActivatableUISystem _activatableUI = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnUIOpenAttempt); + SubscribeLocalEvent(LockToggled); + } + + private void OnUIOpenAttempt(EntityUid uid, ActivatableUIRequiresLockComponent component, ActivatableUIOpenAttemptEvent args) + { + if (args.Cancelled) + return; + + if (TryComp(uid, out var lockComp) && lockComp.Locked != component.requireLocked) + { + args.Cancel(); + if (lockComp.Locked) + _popupSystem.PopupEntity(Loc.GetString("entity-storage-component-locked-message"), uid, args.User); + } + } + + private void LockToggled(EntityUid uid, ActivatableUIRequiresLockComponent component, LockToggledEvent args) + { + if (!TryComp(uid, out var lockComp) || lockComp.Locked == component.requireLocked) + return; + + _activatableUI.CloseAll(uid); + } +} + diff --git a/Content.Server/Objectives/ObjectivesSystem.cs b/Content.Server/Objectives/ObjectivesSystem.cs index f8ecc22828..47fe4eb5f8 100644 --- a/Content.Server/Objectives/ObjectivesSystem.cs +++ b/Content.Server/Objectives/ObjectivesSystem.cs @@ -36,14 +36,14 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem private void OnRoundEndText(RoundEndTextAppendEvent ev) { // go through each gamerule getting data for the roundend summary. - var summaries = new Dictionary>>(); + var summaries = new Dictionary>>(); var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var gameRule)) { if (!_gameTicker.IsGameRuleAdded(uid, gameRule)) continue; - var info = new ObjectivesTextGetInfoEvent(new List<(EntityUid, string)>(), string.Empty); + var info = new ObjectivesTextGetInfoEvent(new List(), string.Empty); RaiseLocalEvent(uid, ref info); if (info.Minds.Count == 0) continue; @@ -51,7 +51,7 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem // first group the gamerules by their agents, for example 2 different dragons var agent = info.AgentName; if (!summaries.ContainsKey(agent)) - summaries[agent] = new Dictionary>(); + summaries[agent] = new Dictionary>(); var prepend = new ObjectivesTextPrependEvent(""); RaiseLocalEvent(uid, ref prepend); @@ -79,7 +79,7 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem foreach (var (_, minds) in summary) { total += minds.Count; - totalInCustody += minds.Where(pair => IsInCustody(pair.Item1)).Count(); + totalInCustody += minds.Where(m => IsInCustody(m)).Count(); } var result = new StringBuilder(); @@ -104,16 +104,19 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem } } - private void AddSummary(StringBuilder result, string agent, List<(EntityUid, string)> minds) + private void AddSummary(StringBuilder result, string agent, List minds) { var agentSummaries = new List<(string summary, float successRate, int completedObjectives)>(); - foreach (var (mindId, name) in minds) + foreach (var mindId in minds) { - if (!TryComp(mindId, out var mind)) + if (!TryComp(mindId, out MindComponent? mind)) + continue; + + var title = GetTitle(mindId, mind); + if (title == null) continue; - var title = GetTitle((mindId, mind), name); var custody = IsInCustody(mindId, mind) ? Loc.GetString("objectives-in-custody") : string.Empty; var objectives = mind.Objectives; @@ -235,18 +238,34 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem /// /// Get the title for a player's mind used in round end. - /// Pass in the original entity name which is shown alongside username. /// - public string GetTitle(Entity mind, string name) + public string? GetTitle(EntityUid mindId, MindComponent? mind = null) { - if (Resolve(mind, ref mind.Comp) && - mind.Comp.OriginalOwnerUserId != null && - _player.TryGetPlayerData(mind.Comp.OriginalOwnerUserId.Value, out var sessionData)) + if (!Resolve(mindId, ref mind)) + return null; + + var name = mind.CharacterName; + var username = (string?) null; + + if (mind.OriginalOwnerUserId != null && + _player.TryGetPlayerData(mind.OriginalOwnerUserId.Value, out var sessionData)) { - var username = sessionData.UserName; - return Loc.GetString("objectives-player-user-named", ("user", username), ("name", name)); + username = sessionData.UserName; } + + if (username != null) + { + if (name != null) + return Loc.GetString("objectives-player-user-named", ("user", username), ("name", name)); + + return Loc.GetString("objectives-player-user", ("user", username)); + } + + // nothing to identify the player by, just give up + if (name == null) + return null; + return Loc.GetString("objectives-player-named", ("name", name)); } } @@ -260,7 +279,7 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem /// The objectives system already checks if the game rule is added so you don't need to check that in this event's handler. /// [ByRefEvent] -public record struct ObjectivesTextGetInfoEvent(List<(EntityUid, string)> Minds, string AgentName); +public record struct ObjectivesTextGetInfoEvent(List Minds, string AgentName); /// /// Raised on the game rule before text for each agent's objectives is added, letting you prepend something. diff --git a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs index 49747aea3a..971a248702 100644 --- a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs @@ -26,7 +26,6 @@ namespace Content.Server.Preferences.Managers [Dependency] private readonly IServerDbManager _db = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IDependencyCollection _dependencies = default!; - [Dependency] private readonly ILogManager _log = default!; // WD-EDIT [Dependency] private readonly SponsorsManager _sponsors = default!; @@ -37,9 +36,7 @@ namespace Content.Server.Preferences.Managers private readonly Dictionary _cachedPlayerPrefs = new(); - private ISawmill _sawmill = default!; - - private int MaxCharacterSlots => _cfg.GetCVar(CCVars.GameMaxCharacterSlots); + private readonly ISawmill _sawmill = default!; public void Init() { @@ -47,7 +44,6 @@ namespace Content.Server.Preferences.Managers _netManager.RegisterNetMessage(HandleSelectCharacterMessage); _netManager.RegisterNetMessage(HandleUpdateCharacterMessage); _netManager.RegisterNetMessage(HandleDeleteCharacterMessage); - _sawmill = _log.GetSawmill("prefs"); } private async void HandleSelectCharacterMessage(MsgSelectCharacter message) diff --git a/Content.Server/Silicons/Borgs/BorgSystem.cs b/Content.Server/Silicons/Borgs/BorgSystem.cs index ec7a25368f..ceab044d4c 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.cs @@ -5,6 +5,7 @@ using Content.Server.DeviceNetwork.Systems; using Content.Server.Explosion.EntitySystems; using Content.Server.Hands.Systems; using Content.Server.PowerCell; +using Content.Shared.UserInterface; using Content.Shared.Access.Systems; using Content.Shared.Alert; using Content.Shared.Database; @@ -69,6 +70,7 @@ public sealed partial class BorgSystem : SharedBorgSystem SubscribeLocalEvent(OnMobStateChanged); SubscribeLocalEvent(OnPowerCellChanged); SubscribeLocalEvent(OnPowerCellSlotEmpty); + SubscribeLocalEvent(OnUIOpenAttempt); SubscribeLocalEvent(OnGetDeadIC); SubscribeLocalEvent(OnBrainMindAdded); @@ -212,6 +214,13 @@ public sealed partial class BorgSystem : SharedBorgSystem UpdateUI(uid, component); } + private void OnUIOpenAttempt(EntityUid uid, BorgChassisComponent component, ActivatableUIOpenAttemptEvent args) + { + // borgs can't view their own ui + if (args.User == uid) + args.Cancel(); + } + private void OnGetDeadIC(EntityUid uid, BorgChassisComponent component, ref GetCharactedDeadIcEvent args) { args.Dead = true; diff --git a/Content.Server/StationEvents/Events/ImmovableRodRule.cs b/Content.Server/StationEvents/Events/ImmovableRodRule.cs index aa193f2f4c..cacb839cd3 100644 --- a/Content.Server/StationEvents/Events/ImmovableRodRule.cs +++ b/Content.Server/StationEvents/Events/ImmovableRodRule.cs @@ -28,9 +28,7 @@ public sealed class ImmovableRodRule : StationEventSystem(out var rod) && proto.TryGetComponent(out var despawn)) { - if (!TryFindRandomTile(out _, out _, out _, out var targetCoords)) - return; - + TryFindRandomTile(out _, out _, out _, out var targetCoords); var speed = RobustRandom.NextFloat(rod.MinSpeed, rod.MaxSpeed); var angle = RobustRandom.NextAngle(); var direction = angle.ToVec(); diff --git a/Content.Server/Strip/StrippableSystem.cs b/Content.Server/Strip/StrippableSystem.cs index e63401fe9f..0ae569b392 100644 --- a/Content.Server/Strip/StrippableSystem.cs +++ b/Content.Server/Strip/StrippableSystem.cs @@ -1,6 +1,7 @@ using System.Linq; using Content.Server.Administration.Logs; using Content.Server.Ensnaring; +using Content.Shared.CombatMode; using Content.Shared.Cuffs; using Content.Shared.Cuffs.Components; using Content.Shared.Database; @@ -9,6 +10,7 @@ using Content.Shared.Ensnaring.Components; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Inventory; using Content.Shared.Inventory.VirtualItem; @@ -27,6 +29,7 @@ namespace Content.Server.Strip { [Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly EnsnareableSystem _ensnaringSystem = default!; + [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly SharedCuffableSystem _cuffableSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; @@ -43,6 +46,7 @@ namespace Content.Server.Strip SubscribeLocalEvent>(AddStripVerb); SubscribeLocalEvent>(AddStripExamineVerb); + SubscribeLocalEvent(OnActivateInWorld); // BUI SubscribeLocalEvent(OnStripButtonPressed); @@ -65,7 +69,7 @@ namespace Content.Server.Strip { Text = Loc.GetString("strip-verb-get-data-text"), Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/outfit.svg.192dpi.png")), - Act = () => TryOpenStrippingUi(args.User, (uid, component), true), + Act = () => StartOpeningStripper(args.User, (uid, component), true), }; args.Verbs.Add(verb); @@ -83,13 +87,37 @@ namespace Content.Server.Strip { Text = Loc.GetString("strip-verb-get-data-text"), Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/outfit.svg.192dpi.png")), - Act = () => TryOpenStrippingUi(args.User, (uid, component), true), + Act = () => StartOpeningStripper(args.User, (uid, component), true), Category = VerbCategory.Examine, }; args.Verbs.Add(verb); } + private void OnActivateInWorld(EntityUid uid, StrippableComponent component, ActivateInWorldEvent args) + { + if (args.Target == args.User) + return; + + if (!HasComp(args.User)) + return; + + StartOpeningStripper(args.User, (uid, component)); + } + + public override void StartOpeningStripper(EntityUid user, Entity strippable, bool openInCombat = false) + { + base.StartOpeningStripper(user, strippable, openInCombat); + + if (TryComp(user, out var mode) && mode.IsInCombatMode && !openInCombat) + return; + + if (HasComp(user)) + { + _userInterfaceSystem.OpenUi(strippable.Owner, StrippingUiKey.Key, user); + } + } + private void OnStripButtonPressed(Entity strippable, ref StrippingSlotButtonPressed args) { if (args.Actor is not { Valid: true } user || diff --git a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs index 190a2d0263..2612e99ec9 100644 --- a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs @@ -187,10 +187,15 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem if (session is { } pSession) { (targetCoordinates, targetLocalAngle) = _lag.GetCoordinatesAngle(target, pSession); - return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range); + } + else + { + var xform = Transform(target); + targetCoordinates = xform.Coordinates; + targetLocalAngle = xform.LocalRotation; } - return Interaction.InRangeUnobstructed(user, target, range); + return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range); } protected override void DoDamageEffect(List targets, EntityUid? user, TransformComponent targetXform) diff --git a/Content.Shared/Wires/ActivatableUIRequiresPanelComponent.cs b/Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs similarity index 71% rename from Content.Shared/Wires/ActivatableUIRequiresPanelComponent.cs rename to Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs index 6e8fff6e49..f92df3d3d5 100644 --- a/Content.Shared/Wires/ActivatableUIRequiresPanelComponent.cs +++ b/Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs @@ -1,17 +1,15 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Wires; +namespace Content.Server.Wires; /// /// This is used for activatable UIs that require the entity to have a panel in a certain state. /// -[RegisterComponent, NetworkedComponent, Access(typeof(SharedWiresSystem))] +[RegisterComponent] public sealed partial class ActivatableUIRequiresPanelComponent : Component { /// /// TRUE: the panel must be open to access the UI. /// FALSE: the panel must be closed to access the UI. /// - [DataField] + [DataField("requireOpen"), ViewVariables(VVAccess.ReadWrite)] public bool RequireOpen = true; } diff --git a/Content.Server/Wires/WiresSystem.cs b/Content.Server/Wires/WiresSystem.cs index 944b0a0e25..c643759f50 100644 --- a/Content.Server/Wires/WiresSystem.cs +++ b/Content.Server/Wires/WiresSystem.cs @@ -4,23 +4,27 @@ using System.Threading; using Content.Server.Construction; using Content.Server.Construction.Components; using Content.Server.Power.Components; +using Content.Server.UserInterface; using Content.Shared.DoAfter; using Content.Shared.GameTicking; using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.Popups; using Content.Shared.Tools.Components; +using Content.Shared.UserInterface; using Content.Shared.Wires; using Robust.Server.GameObjects; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using ActivatableUISystem = Content.Shared.UserInterface.ActivatableUISystem; namespace Content.Server.Wires; public sealed class WiresSystem : SharedWiresSystem { [Dependency] private readonly IPrototypeManager _protoMan = default!; + [Dependency] private readonly ActivatableUISystem _activatableUI = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; @@ -48,6 +52,8 @@ public sealed class WiresSystem : SharedWiresSystem SubscribeLocalEvent(OnTimedWire); SubscribeLocalEvent(OnWiresPowered); SubscribeLocalEvent(OnDoAfter); + SubscribeLocalEvent(OnAttemptOpenActivatableUI); + SubscribeLocalEvent(OnActivatableUIPanelChanged); SubscribeLocalEvent(SetWiresPanelSecurity); } @@ -467,6 +473,23 @@ public sealed class WiresSystem : SharedWiresSystem _uiSystem.CloseUi(ent.Owner, WiresUiKey.Key); } + private void OnAttemptOpenActivatableUI(EntityUid uid, ActivatableUIRequiresPanelComponent component, ActivatableUIOpenAttemptEvent args) + { + if (args.Cancelled || !TryComp(uid, out var wires)) + return; + + if (component.RequireOpen != wires.Open) + args.Cancel(); + } + + private void OnActivatableUIPanelChanged(EntityUid uid, ActivatableUIRequiresPanelComponent component, ref PanelChangedEvent args) + { + if (args.Open == component.RequireOpen) + return; + + _activatableUI.CloseAll(uid); + } + private void OnMapInit(EntityUid uid, WiresComponent component, MapInitEvent args) { if (!string.IsNullOrEmpty(component.LayoutId)) diff --git a/Content.Shared/Zombies/PendingZombieComponent.cs b/Content.Server/Zombies/PendingZombieComponent.cs similarity index 94% rename from Content.Shared/Zombies/PendingZombieComponent.cs rename to Content.Server/Zombies/PendingZombieComponent.cs index 0fb61c84df..811d3f9644 100644 --- a/Content.Shared/Zombies/PendingZombieComponent.cs +++ b/Content.Server/Zombies/PendingZombieComponent.cs @@ -1,13 +1,12 @@ using Content.Shared.Damage; -using Robust.Shared.GameStates; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -namespace Content.Shared.Zombies; +namespace Content.Server.Zombies; /// /// Temporary because diseases suck. /// -[RegisterComponent, NetworkedComponent] +[RegisterComponent] public sealed partial class PendingZombieComponent : Component { /// diff --git a/Content.Server/_White/Wizard/WizardRuleSystem.cs b/Content.Server/_White/Wizard/WizardRuleSystem.cs index fb6684e9d2..7158f6e298 100644 --- a/Content.Server/_White/Wizard/WizardRuleSystem.cs +++ b/Content.Server/_White/Wizard/WizardRuleSystem.cs @@ -39,7 +39,7 @@ public sealed class WizardRuleSystem : GameRuleSystem private void OnObjectivesTextGetInfo(Entity ent, ref ObjectivesTextGetInfoEvent args) { - args.Minds = ent.Comp.WizardMinds.Select(mindId => (mindId, Comp(mindId).CharacterName ?? "?")).ToList(); + args.Minds = ent.Comp.WizardMinds; args.AgentName = Loc.GetString("wizard-round-end-agent-name"); } diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index baa149ba11..39127ecfb9 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -508,7 +508,13 @@ public abstract class SharedActionsSystem : EntitySystem return distance <= action.Range; } - return _interactionSystem.InRangeAndAccessible(user, target, range: action.Range); + if (_interactionSystem.InRangeUnobstructed(user, target, range: action.Range) + && _containerSystem.IsInSameOrParentContainer(user, target)) + { + return true; + } + + return _interactionSystem.CanAccessViaStorage(user, target); } public bool ValidateWorldTarget(EntityUid user, EntityCoordinates coords, Entity action) diff --git a/Content.Shared/Atmos/Components/GasTileOverlayComponent.cs b/Content.Shared/Atmos/Components/GasTileOverlayComponent.cs index 2c3149b11a..e72a1d6758 100644 --- a/Content.Shared/Atmos/Components/GasTileOverlayComponent.cs +++ b/Content.Shared/Atmos/Components/GasTileOverlayComponent.cs @@ -1,6 +1,7 @@ using Robust.Shared.GameStates; using Robust.Shared.Serialization; using Robust.Shared.Timing; +using Robust.Shared.Utility; namespace Content.Shared.Atmos.Components; @@ -23,47 +24,55 @@ public sealed partial class GasTileOverlayComponent : Component public GameTick ForceTick { get; set; } } -[Serializable, NetSerializable] -public sealed class GasTileOverlayState(Dictionary chunks) : ComponentState -{ - public readonly Dictionary Chunks = chunks; -} [Serializable, NetSerializable] -public sealed class GasTileOverlayDeltaState( - Dictionary modifiedChunks, - HashSet allChunks) - : ComponentState, IComponentDeltaState +public sealed class GasTileOverlayState : ComponentState, IComponentDeltaState { - public readonly Dictionary ModifiedChunks = modifiedChunks; - public readonly HashSet AllChunks = allChunks; + public readonly Dictionary Chunks; + public bool FullState => AllChunks == null; - public void ApplyToFullState(GasTileOverlayState state) + // required to infer deleted/missing chunks for delta states + public HashSet? AllChunks; + + public GasTileOverlayState(Dictionary chunks) { + Chunks = chunks; + } + + public void ApplyToFullState(IComponentState fullState) + { + DebugTools.Assert(!FullState); + var state = (GasTileOverlayState) fullState; + DebugTools.Assert(state.FullState); + foreach (var key in state.Chunks.Keys) { - if (!AllChunks.Contains(key)) + if (!AllChunks!.Contains(key)) state.Chunks.Remove(key); } - foreach (var (chunk, data) in ModifiedChunks) + foreach (var (chunk, data) in Chunks) { state.Chunks[chunk] = new(data); } } - public GasTileOverlayState CreateNewFullState(GasTileOverlayState state) + public IComponentState CreateNewFullState(IComponentState fullState) { - var chunks = new Dictionary(AllChunks.Count); + DebugTools.Assert(!FullState); + var state = (GasTileOverlayState) fullState; + DebugTools.Assert(state.FullState); - foreach (var (chunk, data) in ModifiedChunks) + var chunks = new Dictionary(state.Chunks.Count); + + foreach (var (chunk, data) in Chunks) { chunks[chunk] = new(data); } foreach (var (chunk, data) in state.Chunks) { - if (AllChunks.Contains(chunk)) + if (AllChunks!.Contains(chunk)) chunks.TryAdd(chunk, new(data)); } diff --git a/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs b/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs index 8e7dfdedaf..f468724db3 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs @@ -55,7 +55,7 @@ namespace Content.Shared.Atmos.EntitySystems data[index] = chunk; } - args.State = new GasTileOverlayDeltaState(data, new(component.Chunks.Keys)); + args.State = new GasTileOverlayState(data) { AllChunks = new(component.Chunks.Keys) }; } public static Vector2i GetGasChunkIndices(Vector2i indices) diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs index 731f9984d7..3287ced1bc 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs @@ -58,7 +58,7 @@ public abstract partial class SharedBuckleSystem return; var strapPosition = Transform(strapUid).Coordinates; - if (ev.NewPosition.EntityId.IsValid() && ev.NewPosition.InRange(EntityManager, _transform, strapPosition, strapComp.MaxBuckleDistance)) + if (ev.NewPosition.InRange(EntityManager, _transform, strapPosition, strapComp.MaxBuckleDistance)) return; TryUnbuckle(uid, uid, true, component); diff --git a/Content.Shared/Damage/Components/DamageableComponent.cs b/Content.Shared/Damage/Components/DamageableComponent.cs index f8205568f1..be66d51e3b 100644 --- a/Content.Shared/Damage/Components/DamageableComponent.cs +++ b/Content.Shared/Damage/Components/DamageableComponent.cs @@ -5,6 +5,8 @@ using Content.Shared.StatusIcon; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; namespace Content.Shared.Damage { @@ -16,7 +18,7 @@ namespace Content.Shared.Damage /// may also have resistances to certain damage types, defined via a . /// [RegisterComponent] - [NetworkedComponent] + [NetworkedComponent()] [Access(typeof(DamageableSystem), Other = AccessPermissions.ReadExecute)] public sealed partial class DamageableComponent : Component { @@ -24,8 +26,8 @@ namespace Content.Shared.Damage /// This specifies what damage types are supported by this component. /// If null, all damage types will be supported. /// - [DataField("damageContainer")] - public ProtoId? DamageContainerID; + [DataField("damageContainer", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string? DamageContainerID; /// /// This will be applied to any damage that is dealt to this container, @@ -35,8 +37,8 @@ namespace Content.Shared.Damage /// Though DamageModifierSets can be deserialized directly, we only want to use the prototype version here /// to reduce duplication. /// - [DataField("damageModifierSet")] - public ProtoId? DamageModifierSetId; + [DataField("damageModifierSet", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string? DamageModifierSetId; /// /// All the damage information is stored in this . @@ -44,7 +46,7 @@ namespace Content.Shared.Damage /// /// If this data-field is specified, this allows damageable components to be initialized with non-zero damage. /// - [DataField(readOnly: true)] //todo remove this readonly when implementing writing to damagespecifier + [DataField("damage", readOnly: true)] //todo remove this readonly when implementing writing to damagespecifier public DamageSpecifier Damage = new(); /// @@ -62,8 +64,8 @@ namespace Content.Shared.Damage [ViewVariables] public FixedPoint2 TotalDamage; - [DataField("radiationDamageTypes")] - public List> RadiationDamageTypeIDs = new() { "Radiation" }; + [DataField("radiationDamageTypes", customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List RadiationDamageTypeIDs = new() { "Radiation" }; [DataField] public Dictionary> HealthIcons = new() @@ -75,9 +77,6 @@ namespace Content.Shared.Damage [DataField] public ProtoId RottingIcon = "HealthIconRotting"; - - [DataField] - public FixedPoint2? HealthBarThreshold; } [Serializable, NetSerializable] @@ -85,16 +84,13 @@ namespace Content.Shared.Damage { public readonly Dictionary DamageDict; public readonly string? ModifierSetId; - public readonly FixedPoint2? HealthBarThreshold; public DamageableComponentState( Dictionary damageDict, - string? modifierSetId, - FixedPoint2? healthBarThreshold) + string? modifierSetId) { DamageDict = damageDict; ModifierSetId = modifierSetId; - HealthBarThreshold = healthBarThreshold; } } } diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index dd7a81cdaa..6b84bfc012 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Shared.Administration.Logs; using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; using Content.Shared.Inventory; @@ -239,12 +240,12 @@ namespace Content.Shared.Damage { if (_netMan.IsServer) { - args.State = new DamageableComponentState(component.Damage.DamageDict, component.DamageModifierSetId, component.HealthBarThreshold); + args.State = new DamageableComponentState(component.Damage.DamageDict, component.DamageModifierSetId); } else { // avoid mispredicting damage on newly spawned entities. - args.State = new DamageableComponentState(component.Damage.DamageDict.ShallowClone(), component.DamageModifierSetId, component.HealthBarThreshold); + args.State = new DamageableComponentState(component.Damage.DamageDict.ShallowClone(), component.DamageModifierSetId); } } @@ -278,7 +279,6 @@ namespace Content.Shared.Damage } component.DamageModifierSetId = state.ModifierSetId; - component.HealthBarThreshold = state.HealthBarThreshold; // Has the damage actually changed? DamageSpecifier newDamage = new() { DamageDict = new(state.DamageDict) }; diff --git a/Content.Shared/Decals/DecalGridComponent.cs b/Content.Shared/Decals/DecalGridComponent.cs index 67a9c03769..8ac05cb280 100644 --- a/Content.Shared/Decals/DecalGridComponent.cs +++ b/Content.Shared/Decals/DecalGridComponent.cs @@ -62,37 +62,46 @@ namespace Content.Shared.Decals } [Serializable, NetSerializable] - public sealed class DecalGridState(Dictionary chunks) : ComponentState + public sealed class DecalGridState : ComponentState, IComponentDeltaState { - public Dictionary Chunks = chunks; - } + public Dictionary Chunks; + public bool FullState => AllChunks == null; - [Serializable, NetSerializable] - public sealed class DecalGridDeltaState(Dictionary modifiedChunks, HashSet allChunks) - : ComponentState, IComponentDeltaState - { - public Dictionary ModifiedChunks = modifiedChunks; - public HashSet AllChunks = allChunks; + // required to infer deleted/missing chunks for delta states + public HashSet? AllChunks; - public void ApplyToFullState(DecalGridState state) + public DecalGridState(Dictionary chunks) { + Chunks = chunks; + } + + public void ApplyToFullState(IComponentState fullState) + { + DebugTools.Assert(!FullState); + var state = (DecalGridState) fullState; + DebugTools.Assert(state.FullState); + foreach (var key in state.Chunks.Keys) { if (!AllChunks!.Contains(key)) state.Chunks.Remove(key); } - foreach (var (chunk, data) in ModifiedChunks) + foreach (var (chunk, data) in Chunks) { state.Chunks[chunk] = new(data); } } - public DecalGridState CreateNewFullState(DecalGridState state) + public IComponentState CreateNewFullState(IComponentState fullState) { + DebugTools.Assert(!FullState); + var state = (DecalGridState) fullState; + DebugTools.Assert(state.FullState); + var chunks = new Dictionary(state.Chunks.Count); - foreach (var (chunk, data) in ModifiedChunks) + foreach (var (chunk, data) in Chunks) { chunks[chunk] = new(data); } diff --git a/Content.Shared/Decals/SharedDecalSystem.cs b/Content.Shared/Decals/SharedDecalSystem.cs index 0a2349ea29..0665ccbf84 100644 --- a/Content.Shared/Decals/SharedDecalSystem.cs +++ b/Content.Shared/Decals/SharedDecalSystem.cs @@ -49,7 +49,7 @@ namespace Content.Shared.Decals data[index] = chunk; } - args.State = new DecalGridDeltaState(data, new(component.ChunkCollection.ChunkCollection.Keys)); + args.State = new DecalGridState(data) { AllChunks = new(component.ChunkCollection.ChunkCollection.Keys) }; } private void OnGridInitialize(GridInitializeEvent msg) diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index e8529d26f5..94e5cefc31 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -1,10 +1,11 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.ActionBlocker; +using Content.Shared.Administration; using Content.Shared.Administration.Logs; +using Content.Shared.Administration.Managers; using Content.Shared.CombatMode; using Content.Shared.Database; -using Content.Shared.Ghost; using Content.Shared.Hands; using Content.Shared.Hands.Components; using Content.Shared.Input; @@ -14,20 +15,16 @@ using Content.Shared.Inventory; using Content.Shared.Inventory.Events; using Content.Shared.Item; using Content.Shared.Movement.Components; +using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Physics; using Content.Shared.Popups; -using Content.Shared.Storage; using Content.Shared.Tag; using Content.Shared.Timing; -using Content.Shared.UserInterface; using Content.Shared.Verbs; using Content.Shared.Wall; using Content.Shared.Weapons.Ranged.Components; using Content.Shared._White.MeatyOre; -using Content.Shared.Ghost; -using Content.Shared.Storage; -using Content.Shared.UserInterface; using JetBrains.Annotations; using Robust.Shared.Containers; using Robust.Shared.Input; @@ -41,8 +38,6 @@ using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Serialization; using Robust.Shared.Timing; -using Robust.Shared.Utility; -using Robust.Shared.Utility; #pragma warning disable 618 @@ -57,11 +52,12 @@ namespace Content.Shared.Interaction [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly ISharedAdminManager _adminManager = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; - [Dependency] private readonly SharedPhysicsSystem _broadphase = default!; + [Dependency] private readonly SharedPhysicsSystem _sharedBroadphaseSystem = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedVerbSystem _verbSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; @@ -70,18 +66,6 @@ namespace Content.Shared.Interaction [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly TagSystem _tagSystem = default!; - [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; - - private EntityQuery _ignoreUiRangeQuery; - private EntityQuery _fixtureQuery; - private EntityQuery _itemQuery; - private EntityQuery _physicsQuery; - private EntityQuery _handsQuery; - private EntityQuery _relayQuery; - private EntityQuery _combatQuery; - private EntityQuery _wallMountQuery; - private EntityQuery _delayQuery; - private EntityQuery _uiQuery; private const CollisionGroup InRangeUnobstructedMask = CollisionGroup.Impassable | CollisionGroup.InteractImpassable; @@ -94,17 +78,6 @@ namespace Content.Shared.Interaction public override void Initialize() { - _ignoreUiRangeQuery = GetEntityQuery(); - _fixtureQuery = GetEntityQuery(); - _itemQuery = GetEntityQuery(); - _physicsQuery = GetEntityQuery(); - _handsQuery = GetEntityQuery(); - _relayQuery = GetEntityQuery(); - _combatQuery = GetEntityQuery(); - _wallMountQuery = GetEntityQuery(); - _delayQuery = GetEntityQuery(); - _uiQuery = GetEntityQuery(); - SubscribeLocalEvent(HandleUserInterfaceRangeCheck); SubscribeLocalEvent(OnBoundInterfaceInteractAttempt); @@ -140,57 +113,34 @@ namespace Content.Shared.Interaction /// private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev) { - _uiQuery.TryComp(ev.Target, out var uiComp); - if (!_actionBlockerSystem.CanInteract(ev.Actor, ev.Target)) - { - // We permit ghosts to open uis unless explicitly blocked - if (ev.Message is not OpenBoundInterfaceMessage || !HasComp(ev.Actor) || uiComp?.BlockSpectators == true) - { - ev.Cancel(); - return; - } - } + var user = ev.Actor; - var range = _ui.GetUiRange(ev.Target, ev.UiKey); - - // As long as range>0, the UI frame updates should have auto-closed the UI if it is out of range. - DebugTools.Assert(range <= 0 || UiRangeCheck(ev.Actor, ev.Target, range)); - - if (range <= 0 && !IsAccessible(ev.Actor, ev.Target)) + if (!_actionBlockerSystem.CanInteract(user, ev.Target)) { ev.Cancel(); return; } - if (uiComp == null) - return; - - if (uiComp.SingleUser && uiComp.CurrentSingleUser != ev.Actor) + // Check if the bound entity is accessible. Note that we allow admins to ignore this restriction, so that + // they can fiddle with UI's that people can't normally interact with (e.g., placing things directly into + // other people's backpacks). + if (!_containerSystem.IsInSameOrParentContainer(user, ev.Target) + && !CanAccessViaStorage(user, ev.Target) + && !_adminManager.HasAdminFlag(user, AdminFlags.Admin)) { ev.Cancel(); return; } - if (!uiComp.RequireHands) + if (CompOrNull(ev.Target) != null) + { return; + } - if (!_handsQuery.TryComp(ev.Actor, out var hands) || hands.Hands.Count == 0) + if (!InRangeUnobstructed(user, ev.Target)) + { ev.Cancel(); - } - - private bool UiRangeCheck(Entity user, Entity target, float range) - { - if (!Resolve(target, ref target.Comp)) - return false; - - if (user.Owner == target.Owner) - return true; - - // Fast check: if the user is the parent of the entity (e.g., holding it), we always assume that it is in range - if (target.Comp.ParentUid == user.Owner) - return true; - - return InRangeAndAccessible(user, target, range) || _ignoreUiRangeQuery.HasComp(user); + } } /// @@ -247,7 +197,10 @@ namespace Content.Shared.Interaction if (!InRangeUnobstructed(userEntity.Value, uid, popup: true)) return false; - _pullSystem.TogglePull(uid, userEntity.Value); + if (!TryComp(uid, out PullableComponent? pull)) + return false; + + _pullSystem.TogglePull(uid, userEntity.Value, pull); return false; } @@ -323,7 +276,7 @@ namespace Content.Shared.Interaction public bool CombatModeCanHandInteract(EntityUid user, EntityUid? target) { // Always allow attack in these cases - if (target == null || !_handsQuery.TryComp(user, out var hands) || hands.ActiveHand?.HeldEntity is not null) + if (target == null || !TryComp(user, out var hands) || hands.ActiveHand?.HeldEntity is not null) return false; // Only eat input if: @@ -331,7 +284,7 @@ namespace Content.Shared.Interaction // - Target doesn't cancel should-interact event // This is intended to allow items to be picked up in combat mode, // but to also allow items to force attacks anyway (like mobs which are items, e.g. mice) - if (!_itemQuery.HasComp(target)) + if (!HasComp(target)) return false; var combatEv = new CombatModeShouldHandInteractEvent(); @@ -361,7 +314,7 @@ namespace Content.Shared.Interaction bool checkAccess = true, bool checkCanUse = true) { - if (_relayQuery.TryComp(user, out var relay) && relay.RelayEntity is not null) + if (TryComp(user, out var relay) && relay.RelayEntity is not null) { // TODO this needs to be handled better. This probably bypasses many complex can-interact checks in weird roundabout ways. if (_actionBlockerSystem.CanInteract(user, target)) @@ -375,7 +328,7 @@ namespace Content.Shared.Interaction if (target != null && Deleted(target.Value)) return; - if (!altInteract && _combatQuery.TryComp(user, out var combatMode) && combatMode.IsInCombatMode) + if (!altInteract && TryComp(user, out var combatMode) && combatMode.IsInCombatMode) { if (!CombatModeCanHandInteract(user, target)) return; @@ -397,7 +350,10 @@ namespace Content.Shared.Interaction // Check if interacted entity is in the same container, the direct child, or direct parent of the user. // Also checks if the item is accessible via some storage UI (e.g., open backpack) - if (checkAccess && target != null && !IsAccessible(user, target.Value)) + if (checkAccess + && target != null + && !_containerSystem.IsInSameOrParentContainer(user, target.Value) + && !CanAccessViaStorage(user, target.Value)) return; var inRangeUnobstructed = target == null @@ -405,7 +361,7 @@ namespace Content.Shared.Interaction : !checkAccess || InRangeUnobstructed(user, target.Value); // permits interactions with wall mounted entities // Does the user have hands? - if (!_handsQuery.TryComp(user, out var hands) || hands.ActiveHand == null) + if (!TryComp(user, out var hands) || hands.ActiveHand == null) { var ev = new InteractNoHandEvent(user, target, coordinates); RaiseLocalEvent(user, ev); @@ -545,7 +501,7 @@ namespace Content.Shared.Interaction predicate ??= _ => false; var ray = new CollisionRay(origin.Position, dir.Normalized(), collisionMask); - var rayResults = _broadphase.IntersectRayWithPredicate(origin.MapId, ray, dir.Length(), predicate.Invoke, false).ToList(); + var rayResults = _sharedBroadphaseSystem.IntersectRayWithPredicate(origin.MapId, ray, dir.Length(), predicate.Invoke, false).ToList(); if (rayResults.Count == 0) return dir.Length(); @@ -608,29 +564,23 @@ namespace Content.Shared.Interaction } var ray = new CollisionRay(origin.Position, dir.Normalized(), (int) collisionMask); - var rayResults = _broadphase.IntersectRayWithPredicate(origin.MapId, ray, length, predicate.Invoke, false).ToList(); + var rayResults = _sharedBroadphaseSystem.IntersectRayWithPredicate(origin.MapId, ray, length, predicate.Invoke, false).ToList(); return rayResults.Count == 0; } public bool InRangeUnobstructed( - Entity origin, - Entity other, + EntityUid origin, + EntityUid other, float range = InteractionRange, CollisionGroup collisionMask = InRangeUnobstructedMask, Ignored? predicate = null, bool popup = false) { - if (!Resolve(other, ref other.Comp)) + if (!TryComp(other, out TransformComponent? otherXform)) return false; - return InRangeUnobstructed(origin, - other, - other.Comp.Coordinates, - other.Comp.LocalRotation, - range, - collisionMask, - predicate, + return InRangeUnobstructed(origin, other, otherXform.Coordinates, otherXform.LocalRotation, range, collisionMask, predicate, popup); } @@ -662,8 +612,8 @@ namespace Content.Shared.Interaction /// True if the two points are within a given range without being obstructed. /// public bool InRangeUnobstructed( - Entity origin, - Entity other, + EntityUid origin, + EntityUid other, EntityCoordinates otherCoordinates, Angle otherAngle, float range = InteractionRange, @@ -671,10 +621,10 @@ namespace Content.Shared.Interaction Ignored? predicate = null, bool popup = false) { - Ignored combinedPredicate = e => e == origin.Owner || (predicate?.Invoke(e) ?? false); + Ignored combinedPredicate = e => e == origin || (predicate?.Invoke(e) ?? false); var inRange = true; MapCoordinates originPos = default; - var targetPos = _transform.ToMapCoordinates(otherCoordinates); + var targetPos = otherCoordinates.ToMap(EntityManager, _transform); Angle targetRot = default; // So essentially: @@ -684,30 +634,23 @@ namespace Content.Shared.Interaction // Alternatively we could check centre distances first though // that means we wouldn't be able to easily check overlap interactions. if (range > 0f && - _fixtureQuery.TryComp(origin, out var fixtureA) && + TryComp(origin, out var fixtureA) && // These fixture counts are stuff that has the component but no fixtures for (e.g. buttons). // At least until they get removed. fixtureA.FixtureCount > 0 && - _fixtureQuery.TryComp(other, out var fixtureB) && + TryComp(other, out var fixtureB) && fixtureB.FixtureCount > 0 && - Resolve(origin, ref origin.Comp)) + TryComp(origin, out TransformComponent? xformA)) { - var (worldPosA, worldRotA) = origin.Comp.GetWorldPositionRotation(); + var (worldPosA, worldRotA) = xformA.GetWorldPositionRotation(); var xfA = new Transform(worldPosA, worldRotA); var parentRotB = _transform.GetWorldRotation(otherCoordinates.EntityId); var xfB = new Transform(targetPos.Position, parentRotB + otherAngle); // Different map or the likes. - if (!_broadphase.TryGetNearest( - origin, - other, - out _, - out _, - out var distance, - xfA, - xfB, - fixtureA, - fixtureB)) + if (!_sharedBroadphaseSystem.TryGetNearest(origin, other, + out _, out _, out var distance, + xfA, xfB, fixtureA, fixtureB)) { inRange = false; } @@ -729,15 +672,15 @@ namespace Content.Shared.Interaction else { // We'll still do the raycast from the centres but we'll bump the range as we know they're in range. - originPos = _transform.GetMapCoordinates(origin, xform: origin.Comp); + originPos = _transform.GetMapCoordinates(origin, xform: xformA); range = (originPos.Position - targetPos.Position).Length(); } } // No fixtures, e.g. wallmounts. else { - originPos = _transform.GetMapCoordinates(origin, origin); - var otherParent = (other.Comp ?? Transform(other)).ParentUid; + originPos = _transform.GetMapCoordinates(origin); + var otherParent = Transform(other).ParentUid; targetRot = otherParent.IsValid() ? Transform(otherParent).LocalRotation + otherAngle : otherAngle; } @@ -788,13 +731,13 @@ namespace Content.Shared.Interaction { HashSet ignored = new(); - if (_itemQuery.HasComp(target) && _physicsQuery.TryComp(target, out var physics) && physics.CanCollide) + if (HasComp(target) && TryComp(target, out PhysicsComponent? physics) && physics.CanCollide) { // If the target is an item, we ignore any colliding entities. Currently done so that if items get stuck // inside of walls, users can still pick them up. - ignored.UnionWith(_broadphase.GetEntitiesIntersectingBody(target, (int) collisionMask, false, physics)); + ignored.UnionWith(_sharedBroadphaseSystem.GetEntitiesIntersectingBody(target, (int) collisionMask, false, physics)); } - else if (_wallMountQuery.TryComp(target, out var wallMount)) + else if (TryComp(target, out WallMountComponent? wallMount)) { // wall-mount exemptions may be restricted to a specific angle range.da @@ -812,7 +755,13 @@ namespace Content.Shared.Interaction ignored.UnionWith(grid.GetAnchoredEntities(targetCoords)); } - Ignored combinedPredicate = e => e == target || (predicate?.Invoke(e) ?? false) || ignored.Contains(e); + Ignored combinedPredicate = e => + { + return e == target + || (predicate?.Invoke(e) ?? false) + || ignored.Contains(e); + }; + return combinedPredicate; } @@ -1009,8 +958,10 @@ namespace Content.Shared.Interaction bool checkUseDelay = true, bool checkAccess = true) { - _delayQuery.TryComp(used, out var delayComponent); - if (checkUseDelay && delayComponent != null && _useDelay.IsDelayed((used, delayComponent))) + UseDelayComponent? delayComponent = null; + if (checkUseDelay + && TryComp(used, out delayComponent) + && _useDelay.IsDelayed((used, delayComponent))) return false; if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used)) @@ -1021,11 +972,11 @@ namespace Content.Shared.Interaction // Check if interacted entity is in the same container, the direct child, or direct parent of the user. // This is bypassed IF the interaction happened through an item slot (e.g., backpack UI) - if (checkAccess && !IsAccessible(user, used)) + if (checkAccess && !_containerSystem.IsInSameOrParentContainer(user, used) && !CanAccessViaStorage(user, used)) return false; // Does the user have hands? - if (!_handsQuery.HasComp(user)) + if (!HasComp(user)) return false; var activateMsg = new ActivateInWorldEvent(user, used); @@ -1035,9 +986,7 @@ namespace Content.Shared.Interaction DoContactInteraction(user, used, activateMsg); // Still need to call this even without checkUseDelay in case this gets relayed from Activate. - if (delayComponent != null) - _useDelay.TryResetDelay(used, component: delayComponent); - + _useDelay.TryResetDelay(used, component: delayComponent); if (!activateMsg.WasLogged) _adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}"); return true; @@ -1058,8 +1007,11 @@ namespace Content.Shared.Interaction bool checkCanInteract = true, bool checkUseDelay = true) { - _delayQuery.TryComp(used, out var delayComponent); - if (checkUseDelay && delayComponent != null && _useDelay.IsDelayed((used, delayComponent))) + UseDelayComponent? delayComponent = null; + + if (checkUseDelay + && TryComp(used, out delayComponent) + && _useDelay.IsDelayed((used, delayComponent))) return true; // if the item is on cooldown, we consider this handled. if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used)) @@ -1121,60 +1073,11 @@ namespace Content.Shared.Interaction } #endregion - /// - /// Check if a user can access a target (stored in the same containers) and is in range without obstructions. - /// - public bool InRangeAndAccessible( - Entity user, - Entity target, - float range = InteractionRange, - CollisionGroup collisionMask = InRangeUnobstructedMask, - Ignored? predicate = null) - { - if (user == target) - return true; - - if (!Resolve(user, ref user.Comp)) - return false; - - if (!Resolve(target, ref target.Comp)) - return false; - - return IsAccessible(user, target) && InRangeUnobstructed(user, target, range, collisionMask, predicate); - } - - /// - /// Check if a user can access a target or if they are stored in different containers. - /// - public bool IsAccessible(Entity user, Entity target) - { - if (_containerSystem.IsInSameOrParentContainer(user, target, out _, out var container)) - return true; - - return container != null && CanAccessViaStorage(user, target, container); - } - /// /// If a target is in range, but not in the same container as the user, it may be inside of a backpack. This /// checks if the user can access the item in these situations. /// - public bool CanAccessViaStorage(EntityUid user, EntityUid target) - { - if (!_containerSystem.TryGetContainingContainer(target, out var container)) - return false; - - return CanAccessViaStorage(user, target, container); - } - - /// - public bool CanAccessViaStorage(EntityUid user, EntityUid target, BaseContainer container) - { - if (StorageComponent.ContainerId != container.ID) - return false; - - // we don't check if the user can access the storage entity itself. This should be handed by the UI system. - return _ui.IsUiOpen(container.Owner, StorageComponent.StorageUiKey.Key, user); - } + public abstract bool CanAccessViaStorage(EntityUid user, EntityUid target); /// /// Checks whether an entity currently equipped by another player is accessible to some user. This shouldn't @@ -1255,15 +1158,19 @@ namespace Content.Shared.Interaction RaiseLocalEvent(uidB.Value, new ContactInteractionEvent(uidA)); } - private void HandleUserInterfaceRangeCheck(ref BoundUserInterfaceCheckRangeEvent ev) { if (ev.Result == BoundUserInterfaceRangeResult.Fail) return; - ev.Result = UiRangeCheck(ev.Actor!, ev.Target, ev.Data.InteractionRange) - ? BoundUserInterfaceRangeResult.Pass - : BoundUserInterfaceRangeResult.Fail; + if (InRangeUnobstructed(ev.Actor, ev.Target, ev.Data.InteractionRange)) + { + ev.Result = BoundUserInterfaceRangeResult.Pass; + } + else + { + ev.Result = BoundUserInterfaceRangeResult.Fail; + } } } diff --git a/Content.Shared/Inventory/InventorySystem.Equip.cs b/Content.Shared/Inventory/InventorySystem.Equip.cs index 7fd156213b..400dfb0beb 100644 --- a/Content.Shared/Inventory/InventorySystem.Equip.cs +++ b/Content.Shared/Inventory/InventorySystem.Equip.cs @@ -210,7 +210,11 @@ public abstract partial class InventorySystem return false; // Can the actor reach the item? - if (_interactionSystem.InRangeAndAccessible(actor, itemUid)) + if (_interactionSystem.InRangeUnobstructed(actor, itemUid) && _containerSystem.IsInSameOrParentContainer(actor, itemUid)) + return true; + + // Is the item in an open storage UI, i.e., is the user quick-equipping from an open backpack? + if (_interactionSystem.CanAccessViaStorage(actor, itemUid)) return true; // Is the actor currently stripping the target? Here we could check if the actor has the stripping UI open, but diff --git a/Content.Shared/Lock/LockSystem.cs b/Content.Shared/Lock/LockSystem.cs index 1935f3c5e8..c87304b420 100644 --- a/Content.Shared/Lock/LockSystem.cs +++ b/Content.Shared/Lock/LockSystem.cs @@ -9,7 +9,6 @@ using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Popups; using Content.Shared.Storage.Components; -using Content.Shared.UserInterface; using Content.Shared.Verbs; using Content.Shared.Wires; using JetBrains.Annotations; @@ -25,7 +24,6 @@ namespace Content.Shared.Lock; public sealed class LockSystem : EntitySystem { [Dependency] private readonly AccessReaderSystem _accessReader = default!; - [Dependency] private readonly ActivatableUISystem _activatableUI = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedPopupSystem _sharedPopupSystem = default!; @@ -48,9 +46,6 @@ public sealed class LockSystem : EntitySystem SubscribeLocalEvent(OnLockToggleAttempt); SubscribeLocalEvent(OnAttemptChangePanel); SubscribeLocalEvent(OnUnanchorAttempt); - - SubscribeLocalEvent(OnUIOpenAttempt); - SubscribeLocalEvent(LockToggled); } private void OnStartup(EntityUid uid, LockComponent lockComp, ComponentStartup args) @@ -352,25 +347,4 @@ public sealed class LockSystem : EntitySystem args.User); args.Cancel(); } - - private void OnUIOpenAttempt(EntityUid uid, ActivatableUIRequiresLockComponent component, ActivatableUIOpenAttemptEvent args) - { - if (args.Cancelled) - return; - - if (TryComp(uid, out var lockComp) && lockComp.Locked != component.RequireLocked) - { - args.Cancel(); - if (lockComp.Locked) - _sharedPopupSystem.PopupEntity(Loc.GetString("entity-storage-component-locked-message"), uid, args.User); - } - } - - private void LockToggled(EntityUid uid, ActivatableUIRequiresLockComponent component, LockToggledEvent args) - { - if (!TryComp(uid, out var lockComp) || lockComp.Locked == component.RequireLocked) - return; - - _activatableUI.CloseAll(uid); - } -} +} \ No newline at end of file diff --git a/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs b/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs index ea5838a723..f9c941ffe3 100644 --- a/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs +++ b/Content.Shared/MagicMirror/SharedMagicMirrorSystem.cs @@ -2,7 +2,6 @@ using Content.Shared.DoAfter; using Content.Shared.Humanoid.Markings; using Content.Shared.Interaction; using Robust.Shared.Serialization; -using Robust.Shared.Utility; namespace Content.Shared.MagicMirror; @@ -18,13 +17,10 @@ public abstract class SharedMagicMirrorSystem : EntitySystem private void OnMirrorRangeCheck(EntityUid uid, MagicMirrorComponent component, ref BoundUserInterfaceCheckRangeEvent args) { - if (args.Result == BoundUserInterfaceRangeResult.Fail) - return; - - DebugTools.Assert(component.Target != null && Exists(component.Target)); - - if (!_interaction.InRangeUnobstructed(uid, component.Target.Value)) + if (!Exists(component.Target) || !_interaction.InRangeUnobstructed(uid, component.Target.Value)) + { args.Result = BoundUserInterfaceRangeResult.Fail; + } } } diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index 61b6c0fd37..a9fc4cb2a6 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -344,17 +344,14 @@ public sealed class PullingSystem : EntitySystem return !startPull.Cancelled && !getPulled.Cancelled; } - public bool TogglePull(Entity pullable, EntityUid pullerUid) + public bool TogglePull(EntityUid pullableUid, EntityUid pullerUid, PullableComponent pullable) { - if (!Resolve(pullable, ref pullable.Comp, false)) - return false; - - if (pullable.Comp.Puller == pullerUid) + if (pullable.Puller == pullerUid) { - return TryStopPull(pullable, pullable.Comp); + return TryStopPull(pullableUid, pullable); } - return TryStartPull(pullerUid, pullable, pullableComp: pullable); + return TryStartPull(pullerUid, pullableUid, pullableComp: pullable); } public bool TogglePull(EntityUid pullerUid, PullerComponent puller) @@ -362,7 +359,7 @@ public sealed class PullingSystem : EntitySystem if (!TryComp(puller.Pulling, out var pullable)) return false; - return TogglePull((puller.Pulling.Value, pullable), pullerUid); + return TogglePull(puller.Pulling.Value, pullerUid, pullable); } public bool TryStartPull(EntityUid pullerUid, EntityUid pullableUid, diff --git a/Content.Shared/Pinpointer/SharedNavMapSystem.cs b/Content.Shared/Pinpointer/SharedNavMapSystem.cs index 7c12321b5d..ffe81c2d0e 100644 --- a/Content.Shared/Pinpointer/SharedNavMapSystem.cs +++ b/Content.Shared/Pinpointer/SharedNavMapSystem.cs @@ -96,7 +96,7 @@ public abstract class SharedNavMapSystem : EntitySystem chunks.Add(origin, chunk.TileData); } - args.State = new NavMapState(chunks, component.Beacons); + args.State = new NavMapComponentState(chunks, component.Beacons); return; } @@ -109,7 +109,12 @@ public abstract class SharedNavMapSystem : EntitySystem chunks.Add(origin, chunk.TileData); } - args.State = new NavMapDeltaState(chunks, component.Beacons, new(component.Chunks.Keys)); + args.State = new NavMapComponentState(chunks, component.Beacons) + { + // TODO NAVMAP cache a single AllChunks hashset in the component. + // Or maybe just only send them if a chunk gets removed. + AllChunks = new(component.Chunks.Keys), + }; } #endregion @@ -117,35 +122,32 @@ public abstract class SharedNavMapSystem : EntitySystem #region: System messages [Serializable, NetSerializable] - protected sealed class NavMapState( + protected sealed class NavMapComponentState( Dictionary chunks, Dictionary beacons) - : ComponentState + : ComponentState, IComponentDeltaState { public Dictionary Chunks = chunks; public Dictionary Beacons = beacons; - } - [Serializable, NetSerializable] - protected sealed class NavMapDeltaState( - Dictionary modifiedChunks, - Dictionary beacons, - HashSet allChunks) - : ComponentState, IComponentDeltaState - { - public Dictionary ModifiedChunks = modifiedChunks; - public Dictionary Beacons = beacons; - public HashSet AllChunks = allChunks; + // Required to infer deleted/missing chunks for delta states + public HashSet? AllChunks; - public void ApplyToFullState(NavMapState state) + public bool FullState => AllChunks == null; + + public void ApplyToFullState(IComponentState fullState) { + DebugTools.Assert(!FullState); + var state = (NavMapComponentState) fullState; + DebugTools.Assert(state.FullState); + foreach (var key in state.Chunks.Keys) { if (!AllChunks!.Contains(key)) state.Chunks.Remove(key); } - foreach (var (index, data) in ModifiedChunks) + foreach (var (index, data) in Chunks) { if (!state.Chunks.TryGetValue(index, out var stateValue)) state.Chunks[index] = stateValue = new int[data.Length]; @@ -160,8 +162,12 @@ public abstract class SharedNavMapSystem : EntitySystem } } - public NavMapState CreateNewFullState(NavMapState state) + public IComponentState CreateNewFullState(IComponentState fullState) { + DebugTools.Assert(!FullState); + var state = (NavMapComponentState) fullState; + DebugTools.Assert(state.FullState); + var chunks = new Dictionary(state.Chunks.Count); foreach (var (index, data) in state.Chunks) { @@ -170,13 +176,13 @@ public abstract class SharedNavMapSystem : EntitySystem var newData = chunks[index] = new int[ArraySize]; - if (ModifiedChunks.TryGetValue(index, out var updatedData)) + if (Chunks.TryGetValue(index, out var updatedData)) Array.Copy(newData, updatedData, ArraySize); else Array.Copy(newData, data, ArraySize); } - return new NavMapState(chunks, new(Beacons)); + return new NavMapComponentState(chunks, new(Beacons)); } } diff --git a/Content.Shared/Preferences/Loadouts/RoleLoadout.cs b/Content.Shared/Preferences/Loadouts/RoleLoadout.cs index bb3e46c49b..2094011873 100644 --- a/Content.Shared/Preferences/Loadouts/RoleLoadout.cs +++ b/Content.Shared/Preferences/Loadouts/RoleLoadout.cs @@ -74,19 +74,18 @@ public sealed class RoleLoadout { var loadout = loadouts[i]; - // Old prototype or otherwise invalid. if (!protoManager.TryIndex(loadout.Prototype, out var loadoutProto)) { loadouts.RemoveAt(i); continue; } - // Malicious client maybe, check the group even has it. - if (!groupProto.Loadouts.Contains(loadout.Prototype)) - { - loadouts.RemoveAt(i); - continue; - } + // Похуй FIXME + //if (!IsValid(profile, session, loadout.Prototype, collection, out _)) + // { + // loadouts.RemoveAt(i); + // continue; + // } Apply(loadoutProto); } diff --git a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs index fe4ceb6a2c..e6672a1cf2 100644 --- a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs +++ b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs @@ -6,7 +6,6 @@ using Content.Shared.Movement.Systems; using Content.Shared.Popups; using Content.Shared.PowerCell.Components; using Content.Shared.Silicons.Borgs.Components; -using Content.Shared.UserInterface; using Content.Shared.Wires; using Robust.Shared.Containers; using Robust.Shared.Prototypes; @@ -38,8 +37,6 @@ public abstract partial class SharedBorgSystem : EntitySystem SubscribeLocalEvent(OnInserted); SubscribeLocalEvent(OnRemoved); SubscribeLocalEvent(OnRefreshMovementSpeedModifiers); - SubscribeLocalEvent(OnUIOpenAttempt); - //Honk SubscribeLocalEvent(RandomTTS); @@ -110,13 +107,6 @@ public abstract partial class SharedBorgSystem : EntitySystem component.ModuleContainer = Container.EnsureContainer(uid, component.ModuleContainerId, containerManager); } - private void OnUIOpenAttempt(EntityUid uid, BorgChassisComponent component, ActivatableUIOpenAttemptEvent args) - { - // borgs can't view their own ui - if (args.User == uid) - args.Cancel(); - } - protected virtual void OnInserted(EntityUid uid, BorgChassisComponent component, EntInsertedIntoContainerMessage args) { diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 7d23038682..c53c3a9e2e 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -25,7 +25,6 @@ using Content.Shared.Stacks; using Content.Shared.Storage.Components; using Content.Shared.Timing; using Content.Shared.Verbs; -using Content.Shared.Whitelist; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -157,9 +156,7 @@ public abstract class SharedStorageSystem : EntitySystem Grid = new List(component.Grid), MaxItemSize = component.MaxItemSize, StoredItems = storedItems, - SavedLocations = component.SavedLocations, - Whitelist = component.Whitelist, - Blacklist = component.Blacklist + SavedLocations = component.SavedLocations }; } @@ -171,8 +168,6 @@ public abstract class SharedStorageSystem : EntitySystem component.Grid.Clear(); component.Grid.AddRange(state.Grid); component.MaxItemSize = state.MaxItemSize; - component.Whitelist = state.Whitelist; - component.Blacklist = state.Blacklist; component.StoredItems.Clear(); @@ -1105,7 +1100,7 @@ public abstract class SharedStorageSystem : EntitySystem /// true if inserted, false otherwise public bool PlayerInsertEntityInWorld(Entity uid, EntityUid player, EntityUid toInsert) { - if (!Resolve(uid, ref uid.Comp) || !_interactionSystem.InRangeUnobstructed(player, uid.Owner)) + if (!Resolve(uid, ref uid.Comp) || !_interactionSystem.InRangeUnobstructed(player, uid)) return false; if (!Insert(uid, toInsert, out _, user: player, uid.Comp)) @@ -1506,9 +1501,5 @@ public abstract class SharedStorageSystem : EntitySystem public List Grid = new(); public ProtoId? MaxItemSize; - - public EntityWhitelist? Whitelist; - - public EntityWhitelist? Blacklist; } } diff --git a/Content.Shared/Strip/SharedStrippableSystem.cs b/Content.Shared/Strip/SharedStrippableSystem.cs index 075cf81a4c..59b24ec943 100644 --- a/Content.Shared/Strip/SharedStrippableSystem.cs +++ b/Content.Shared/Strip/SharedStrippableSystem.cs @@ -1,31 +1,17 @@ -using Content.Shared.CombatMode; using Content.Shared.DragDrop; using Content.Shared.Hands.Components; -using Content.Shared.Interaction; using Content.Shared.Strip.Components; namespace Content.Shared.Strip; public abstract class SharedStrippableSystem : EntitySystem { - [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; - public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnCanDropOn); SubscribeLocalEvent(OnCanDrop); SubscribeLocalEvent(OnDragDrop); - SubscribeLocalEvent(OnActivateInWorld); - } - - private void OnActivateInWorld(EntityUid uid, StrippableComponent component, ActivateInWorldEvent args) - { - if (args.Handled || args.Target == args.User) - return; - - if (TryOpenStrippingUi(args.User, (uid, component))) - args.Handled = true; } public (TimeSpan Time, bool Stealth) GetStripTimeModifiers(EntityUid user, EntityUid target, TimeSpan initialTime) @@ -43,20 +29,13 @@ public abstract class SharedStrippableSystem : EntitySystem if (args.Handled || args.Target != args.User) return; - if (TryOpenStrippingUi(args.User, (uid, component))) - args.Handled = true; + StartOpeningStripper(args.User, (uid, component)); + args.Handled = true; } - public bool TryOpenStrippingUi(EntityUid user, Entity target, bool openInCombat = false) + public virtual void StartOpeningStripper(EntityUid user, Entity component, bool openInCombat = false) { - if (!openInCombat && TryComp(user, out var mode) && mode.IsInCombatMode) - return false; - if (!HasComp(user)) - return false; - - _ui.OpenUi(target.Owner, StrippingUiKey.Key, user); - return true; } private void OnCanDropOn(EntityUid uid, StrippingComponent component, ref CanDropTargetEvent args) diff --git a/Content.Shared/UserInterface/ActivatableUIComponent.cs b/Content.Shared/UserInterface/ActivatableUIComponent.cs index 93f05acac0..3f83816b7d 100644 --- a/Content.Shared/UserInterface/ActivatableUIComponent.cs +++ b/Content.Shared/UserInterface/ActivatableUIComponent.cs @@ -57,7 +57,7 @@ namespace Content.Shared.UserInterface /// [ViewVariables(VVAccess.ReadWrite)] [DataField] - public bool BlockSpectators; + public bool AllowSpectator = true; /// /// Whether the item must be in the user's currently selected/active hand. diff --git a/Content.Shared/UserInterface/ActivatableUISystem.cs b/Content.Shared/UserInterface/ActivatableUISystem.cs index c1822c4ee3..a6d27ac545 100644 --- a/Content.Shared/UserInterface/ActivatableUISystem.cs +++ b/Content.Shared/UserInterface/ActivatableUISystem.cs @@ -8,7 +8,7 @@ using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Popups; using Content.Shared.Verbs; -using Robust.Shared.Utility; +using Robust.Shared.Containers; namespace Content.Shared.UserInterface; @@ -19,12 +19,15 @@ public sealed partial class ActivatableUISystem : EntitySystem [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly SharedInteractionSystem _interaction = default!; + + private readonly List _toClose = new(); public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnUseInHand); SubscribeLocalEvent(OnActivate); SubscribeLocalEvent(OnInteractUsing); @@ -34,24 +37,28 @@ public sealed partial class ActivatableUISystem : EntitySystem SubscribeLocalEvent>(GetActivationVerb); SubscribeLocalEvent>(GetVerb); + // TODO ActivatableUI + // Add UI-user component, and listen for user container changes. + // I.e., should lose a computer UI if a player gets shut into a locker. + SubscribeLocalEvent(OnGotInserted); + SubscribeLocalEvent(OnGotRemoved); + + SubscribeLocalEvent(OnBoundInterfaceInteractAttempt); SubscribeLocalEvent(OnActionPerform); InitializePower(); } - private void OnStartup(Entity ent, ref ComponentStartup args) + private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev) { - if (ent.Comp.Key == null) - { - Log.Error($"Missing UI Key for entity: {ToPrettyString(ent)}"); + if (!TryComp(ev.Target, out ActivatableUIComponent? comp)) return; - } - // TODO BUI - // set interaction range to zero to avoid constant range checks. - // - // if (ent.Comp.InHandsOnly && _uiSystem.TryGetInterfaceData(ent.Owner, ent.Comp.Key, out var data)) - // data.InteractionRange = 0; + if (!comp.RequireHands) + return; + + if (!TryComp(ev.Actor, out HandsComponent? hands) || hands.Hands.Count == 0) + ev.Cancel(); } private void OnActionPerform(EntityUid uid, UserInterfaceComponent component, OpenUiActionEvent args) @@ -70,10 +77,9 @@ public sealed partial class ActivatableUISystem : EntitySystem args.Verbs.Add(new ActivationVerb { + // TODO VERBS add "open UI" icon Act = () => InteractUI(args.User, uid, component), - Text = Loc.GetString(component.VerbText), - // TODO VERB ICON find a better icon - Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")), + Text = Loc.GetString(component.VerbText) }); } @@ -84,10 +90,9 @@ public sealed partial class ActivatableUISystem : EntitySystem args.Verbs.Add(new Verb { + // TODO VERBS add "open UI" icon Act = () => InteractUI(args.User, uid, component), - Text = Loc.GetString(component.VerbText), - // TODO VERB ICON find a better icon - Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")), + Text = Loc.GetString(component.VerbText) }); } @@ -114,7 +119,7 @@ public sealed partial class ActivatableUISystem : EntitySystem } } - return args.CanInteract || HasComp(args.User) && !component.BlockSpectators; + return args.CanInteract || component.AllowSpectator && HasComp(args.User); } private void OnUseInHand(EntityUid uid, ActivatableUIComponent component, UseInHandEvent args) @@ -186,7 +191,7 @@ public sealed partial class ActivatableUISystem : EntitySystem return true; } - if (!_blockerSystem.CanInteract(user, uiEntity) && (!HasComp(user) || aui.BlockSpectators)) + if (!_blockerSystem.CanInteract(user, uiEntity) && (!aui.AllowSpectator || !HasComp(user))) return false; if (aui.RequireHands) @@ -281,4 +286,47 @@ public sealed partial class ActivatableUISystem : EntitySystem if (ent.Comp.RequireHands && ent.Comp.InHandsOnly) CloseAll(ent, ent); } + + private void OnGotInserted(Entity ent, ref EntGotInsertedIntoContainerMessage args) + { + CheckAccess((ent, ent)); + } + + private void OnGotRemoved(Entity ent, ref EntGotRemovedFromContainerMessage args) + { + CheckAccess((ent, ent)); + } + + public void CheckAccess(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + if (ent.Comp.Key == null) + { + Log.Error($"Encountered null key in activatable ui on entity {ToPrettyString(ent)}"); + return; + } + + foreach (var user in _uiSystem.GetActors(ent.Owner, ent.Comp.Key)) + { + if (!_container.IsInSameOrParentContainer(user, ent) + && !_interaction.CanAccessViaStorage(user, ent)) + { + _toClose.Add(user); + continue; + + } + + if (!_interaction.InRangeUnobstructed(user, ent)) + _toClose.Add(user); + } + + foreach (var user in _toClose) + { + _uiSystem.CloseUi(ent.Owner, ent.Comp.Key, user); + } + + _toClose.Clear(); + } } diff --git a/Content.Shared/Verbs/SharedVerbSystem.cs b/Content.Shared/Verbs/SharedVerbSystem.cs index e78fe98f4c..60714aea8f 100644 --- a/Content.Shared/Verbs/SharedVerbSystem.cs +++ b/Content.Shared/Verbs/SharedVerbSystem.cs @@ -72,7 +72,19 @@ namespace Content.Shared.Verbs extraCategories = new(); // accessibility checks - var canAccess = force || _interactionSystem.InRangeAndAccessible(user, target); + bool canAccess = false; + if (force || target == user) + canAccess = true; + else if (_interactionSystem.InRangeUnobstructed(user, target)) + { + // Note that being in a container does not count as an obstruction for InRangeUnobstructed + // Therefore, we need extra checks to ensure the item is actually accessible: + if (ContainerSystem.IsInSameOrParentContainer(user, target)) + canAccess = true; + else + // the item might be in a backpack that the user has open + canAccess = _interactionSystem.CanAccessViaStorage(user, target); + } // A large number of verbs need to check action blockers. Instead of repeatedly having each system individually // call ActionBlocker checks, just cache it for the verb request. diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 719a020311..3d9eb67b36 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -1,8 +1,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Numerics; -using Content.Shared._White; -using Content.Shared._White.Implants.NeuroControl; using Content.Shared.ActionBlocker; using Content.Shared.Administration.Logs; using Content.Shared.CombatMode; @@ -15,7 +13,6 @@ using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.Inventory; using Content.Shared.Item.ItemToggle.Components; -using Content.Shared.Movement.Components; using Content.Shared.Physics; using Content.Shared.Popups; using Content.Shared.Weapons.Melee.Components; @@ -23,6 +20,9 @@ using Content.Shared.Weapons.Melee.Events; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Systems; +using Content.Shared._White; +using Content.Shared._White.Implants.NeuroControl; +using Content.Shared.Movement.Components; using Robust.Shared.Configuration; using Robust.Shared.Map; using Robust.Shared.Physics; @@ -205,39 +205,49 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem private void OnLightAttack(LightAttackEvent msg, EntitySessionEventArgs args) { - if (args.SenderSession.AttachedEntity is not {} user) + var user = args.SenderSession.AttachedEntity; + + if (user == null) return; - if (!TryGetWeapon(user, out var weaponUid, out var weapon) || + if (!TryGetWeapon(user.Value, out var weaponUid, out var weapon) || weaponUid != GetEntity(msg.Weapon)) { return; } - AttemptAttack(user, weaponUid, weapon, msg, args.SenderSession); + AttemptAttack(args.SenderSession.AttachedEntity!.Value, weaponUid, weapon, msg, args.SenderSession); } private void OnHeavyAttack(HeavyAttackEvent msg, EntitySessionEventArgs args) { - if (args.SenderSession.AttachedEntity is not {} user) + if (args.SenderSession.AttachedEntity == null) + { return; + } - if (!TryGetWeapon(user, out var weaponUid, out var weapon) || + if (!TryGetWeapon(args.SenderSession.AttachedEntity.Value, out var weaponUid, out var weapon) || weaponUid != GetEntity(msg.Weapon)) { return; } - AttemptAttack(user, weaponUid, weapon, msg, args.SenderSession); + AttemptAttack(args.SenderSession.AttachedEntity.Value, weaponUid, weapon, msg, args.SenderSession); } private void OnDisarmAttack(DisarmAttackEvent msg, EntitySessionEventArgs args) { - if (args.SenderSession.AttachedEntity is not {} user) + if (args.SenderSession.AttachedEntity == null) + { return; + } - if (TryGetWeapon(user, out var weaponUid, out var weapon)) - AttemptAttack(user, weaponUid, weapon, msg, args.SenderSession); + if (!TryGetWeapon(args.SenderSession.AttachedEntity.Value, out var weaponUid, out var weapon)) + { + return; + } + + AttemptAttack(args.SenderSession.AttachedEntity.Value, weaponUid, weapon, msg, args.SenderSession); } /// @@ -371,22 +381,18 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem return false; var update = UpdateNextAttack.Both; // WD - EntityUid? target = null; switch (attack) { case LightAttackEvent light: var lightTarget = GetEntity(light.Target); - if (light.Target != null && !TryGetEntity(light.Target, out target)) - { - // Target was lightly attacked & deleted. - return false; - } + update = lightTarget == null ? UpdateNextAttack.Both : + IsMob(lightTarget.Value) ? UpdateNextAttack.Mob : UpdateNextAttack.NonMob; // WD - if (!Blocker.CanAttack(user, target, (weaponUid, weapon))) + if (!Blocker.CanAttack(user, lightTarget, (weaponUid, weapon))) return false; // Can't self-attack if you're the weapon - if (weaponUid == target) + if (weaponUid == lightTarget) return false; // WD START @@ -415,13 +421,11 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem break; case DisarmAttackEvent disarm: - if (disarm.Target != null && !TryGetEntity(disarm.Target, out target)) - { - // Target was lightly attacked & deleted. - return false; - } + var disarmTarget = GetEntity(disarm.Target); + update = disarmTarget == null ? UpdateNextAttack.Both : + IsMob(disarmTarget.Value) ? UpdateNextAttack.Mob : UpdateNextAttack.NonMob; // WD - if (!Blocker.CanAttack(user, target, (weaponUid, weapon), true)) + if (!Blocker.CanAttack(user, disarmTarget, (weaponUid, weapon), true)) return false; break; default: diff --git a/Content.Shared/Wires/SharedWiresSystem.cs b/Content.Shared/Wires/SharedWiresSystem.cs index d84766a5fc..b4b0768e0f 100644 --- a/Content.Shared/Wires/SharedWiresSystem.cs +++ b/Content.Shared/Wires/SharedWiresSystem.cs @@ -3,7 +3,6 @@ using Content.Shared.Database; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Tools.Systems; -using Content.Shared.UserInterface; using Robust.Shared.Audio.Systems; namespace Content.Shared.Wires; @@ -11,7 +10,6 @@ namespace Content.Shared.Wires; public abstract class SharedWiresSystem : EntitySystem { [Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!; - [Dependency] private readonly ActivatableUISystem _activatableUI = default!; [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!; [Dependency] protected readonly SharedToolSystem Tool = default!; @@ -23,9 +21,6 @@ public abstract class SharedWiresSystem : EntitySystem SubscribeLocalEvent(OnPanelDoAfter); SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnExamine); - - SubscribeLocalEvent(OnAttemptOpenActivatableUI); - SubscribeLocalEvent(OnActivatableUIPanelChanged); } private void OnPanelDoAfter(EntityUid uid, WiresPanelComponent panel, WirePanelDoAfterEvent args) @@ -137,21 +132,4 @@ public abstract class SharedWiresSystem : EntitySystem return entity.Comp.Open; } - - private void OnAttemptOpenActivatableUI(EntityUid uid, ActivatableUIRequiresPanelComponent component, ActivatableUIOpenAttemptEvent args) - { - if (args.Cancelled || !TryComp(uid, out var wires)) - return; - - if (component.RequireOpen != wires.Open) - args.Cancel(); - } - - private void OnActivatableUIPanelChanged(EntityUid uid, ActivatableUIRequiresPanelComponent component, ref PanelChangedEvent args) - { - if (args.Open == component.RequireOpen) - return; - - _activatableUI.CloseAll(uid); - } } diff --git a/Resources/Locale/en-US/objectives/round-end.ftl b/Resources/Locale/en-US/objectives/round-end.ftl index 3da81fc964..b4314b2caf 100644 --- a/Resources/Locale/en-US/objectives/round-end.ftl +++ b/Resources/Locale/en-US/objectives/round-end.ftl @@ -6,6 +6,7 @@ objectives-round-end-result = {$count -> objectives-round-end-result-in-custody = {$custody} out of {$count} {MAKEPLURAL($agent)} were in custody. objectives-player-user-named = [color=White]{$name}[/color] ([color=gray]{$user}[/color]) +objectives-player-user = [color=gray]{$user}[/color] objectives-player-named = [color=White]{$name}[/color] objectives-no-objectives = {$custody}{$title} was a {$agent}. diff --git a/Resources/Prototypes/Catalog/Cargo/cargo_engineering.yml b/Resources/Prototypes/Catalog/Cargo/cargo_engineering.yml index 7ca6af8451..754e30f133 100644 --- a/Resources/Prototypes/Catalog/Cargo/cargo_engineering.yml +++ b/Resources/Prototypes/Catalog/Cargo/cargo_engineering.yml @@ -133,7 +133,7 @@ icon: sprite: Structures/Piping/Atmospherics/Portable/portable_sheater.rsi state: sheaterOff - product: CrateEngineeringSpaceHeater + product: SpaceHeaterAnchored cost: 300 category: cargoproduct-category-name-engineering group: market diff --git a/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml b/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml index 26a8910c73..03c870fa58 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml @@ -194,13 +194,3 @@ contents: - id: WeaponParticleDecelerator amount: 3 - -- type: entity - id: CrateEngineeringSpaceHeater - parent: CrateEngineering - name: space heater crate - description: Contains a space heater for climate control. - components: - - type: StorageFill - contents: - - id: SpaceHeaterFlatpack diff --git a/Resources/Prototypes/Catalog/Fills/Crates/engines.yml b/Resources/Prototypes/Catalog/Fills/Crates/engines.yml index b510b22cca..fcefdf7b1d 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/engines.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/engines.yml @@ -42,7 +42,7 @@ components: - type: StorageFill contents: - - id: EmitterFlatpack + - id: Emitter # TODO change to flatpack - type: entity id: CrateEngineeringSingularityCollector diff --git a/Resources/Prototypes/Entities/Effects/mobspawn.yml b/Resources/Prototypes/Entities/Effects/mobspawn.yml index 2fad68d7f2..fb59aa3fc4 100644 --- a/Resources/Prototypes/Entities/Effects/mobspawn.yml +++ b/Resources/Prototypes/Entities/Effects/mobspawn.yml @@ -1,7 +1,6 @@ - type: entity id: MobSpawnCrabQuartz name: mobspawner quartzcrab - noSpawn: True components: - type: Transform anchored: True @@ -31,7 +30,6 @@ id: MobSpawnCrabIron parent: MobSpawnCrabQuartz name: mobspawner ironcrab - noSpawn: True components: - type: Sprite sprite: /Textures/Effects/mobspawn.rsi @@ -43,7 +41,6 @@ id: MobSpawnCrabSilver parent: MobSpawnCrabQuartz name: mobspawner silvercrab - noSpawn: True components: - type: Sprite sprite: /Textures/Effects/mobspawn.rsi @@ -55,7 +52,6 @@ id: MobSpawnCrabUranium parent: MobSpawnCrabQuartz name: mobspawner uraniumcrab - noSpawn: True components: - type: Sprite sprite: /Textures/Effects/mobspawn.rsi diff --git a/Resources/Prototypes/Entities/Effects/wallspawn.yml b/Resources/Prototypes/Entities/Effects/wallspawn.yml index 7213763c46..f1bd236a8a 100644 --- a/Resources/Prototypes/Entities/Effects/wallspawn.yml +++ b/Resources/Prototypes/Entities/Effects/wallspawn.yml @@ -1,6 +1,5 @@ - type: entity id: WallSpawnAsteroid - noSpawn: True components: - type: Transform anchored: True @@ -29,7 +28,6 @@ - type: entity id: WallSpawnAsteroidUraniumCrab parent: WallSpawnAsteroid - noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockUraniumCrab @@ -37,7 +35,6 @@ - type: entity id: WallSpawnAsteroidUranium parent: WallSpawnAsteroid - noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockUranium @@ -45,7 +42,6 @@ - type: entity id: WallSpawnAsteroidQuartzCrab parent: WallSpawnAsteroid - noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockQuartzCrab @@ -53,7 +49,6 @@ - type: entity id: WallSpawnAsteroidQuartz parent: WallSpawnAsteroid - noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockQuartz @@ -61,7 +56,6 @@ - type: entity id: WallSpawnAsteroidSilverCrab parent: WallSpawnAsteroid - noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockSilverCrab @@ -69,7 +63,6 @@ - type: entity id: WallSpawnAsteroidSilver parent: WallSpawnAsteroid - noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockSilver @@ -77,7 +70,6 @@ - type: entity id: WallSpawnAsteroidIronCrab parent: WallSpawnAsteroid - noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockTinCrab @@ -85,7 +77,6 @@ - type: entity id: WallSpawnAsteroidIron parent: WallSpawnAsteroid - noSpawn: True components: - type: SpawnOnDespawn prototype: AsteroidRockTin \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/cannons.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/cannons.yml deleted file mode 100644 index 6db7f4b385..0000000000 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/cannons.yml +++ /dev/null @@ -1,92 +0,0 @@ -# Most of these have DO NOT MAP, since stations are completely unequipped to deal with ship combat + these are basically placeholder. - -- type: entity - id: ShuttleGunSvalinnMachineGunCircuitboard - parent: BaseMachineCircuitboard - name: LSE-400c "Svalinn machine gun" machine board - description: A machine printed circuit board for an LSE-400c "Svalinn machine gun" - suffix: DO NOT MAP, Machine Board - components: - - type: Sprite - state: security - - type: MachineBoard - prototype: ShuttleGunSvalinnMachineGun - requirements: - MatterBin: 2 - Manipulator: 4 - materialRequirements: - Steel: 5 - CableHV: 5 - -- type: entity - id: ShuttleGunPerforatorCircuitboard - parent: BaseMachineCircuitboard - name: LSE-1200c "Perforator" machine board - description: A machine printed circuit board for an LSE-1200c "Perforator" - suffix: DO NOT MAP, Machine Board - components: - - type: Sprite - state: security - - type: MachineBoard - prototype: ShuttleGunPerforator - requirements: - MatterBin: 4 - Manipulator: 6 - materialRequirements: - Steel: 10 - CableHV: 5 - -- type: entity - id: ShuttleGunFriendshipCircuitboard - parent: BaseMachineCircuitboard - name: EXP-320g "Friendship" machine board - description: A machine printed circuit board for an EXP-320g "Friendship" - suffix: DO NOT MAP, Machine Board - components: - - type: Sprite - state: security - - type: MachineBoard - prototype: ShuttleGunFriendship - requirements: - MatterBin: 3 - Manipulator: 2 - materialRequirements: - Steel: 7 - CableHV: 5 - -- type: entity - id: ShuttleGunDusterCircuitboard - parent: BaseMachineCircuitboard - name: EXP-2100g "Duster" machine board - description: A machine printed circuit board for an EXP-2100g "Duster" - suffix: DO NOT MAP, Machine Board - components: - - type: Sprite - state: security - - type: MachineBoard - prototype: ShuttleGunDuster - requirements: - MatterBin: 6 - Manipulator: 4 - materialRequirements: - Steel: 10 - CableHV: 5 - Uranium: 2 - -- type: entity - id: ShuttleGunKineticCircuitboard - parent: BaseMachineCircuitboard - name: PTK-800 "Matter Dematerializer" machine board - description: A machine printed circuit board for an PTK-800 "Matter Dematerializer" - suffix: DO NOT MAP, Machine Board - components: - - type: Sprite - state: security - - type: MachineBoard - prototype: ShuttleGunKinetic - requirements: - MatterBin: 2 - Manipulator: 3 - materialRequirements: - Steel: 5 - CableHV: 2 \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml index 2fd2c44cf5..3937aac576 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml @@ -1277,6 +1277,92 @@ CableHV: 5 Uranium: 2 +- type: entity + id: ShuttleGunSvalinnMachineGunCircuitboard + parent: BaseMachineCircuitboard + name: LSE-400c "Svalinn machine gun" machine board + description: A machine printed circuit board for an LSE-400c "Svalinn machine gun" + components: + - type: Sprite + state: security + - type: MachineBoard + prototype: ShuttleGunSvalinnMachineGun + requirements: + MatterBin: 2 + Manipulator: 4 + materialRequirements: + Steel: 5 + CableHV: 5 + +- type: entity + id: ShuttleGunPerforatorCircuitboard + parent: BaseMachineCircuitboard + name: LSE-1200c "Perforator" machine board + description: A machine printed circuit board for an LSE-1200c "Perforator" + components: + - type: Sprite + state: security + - type: MachineBoard + prototype: ShuttleGunPerforator + requirements: + MatterBin: 4 + Manipulator: 6 + materialRequirements: + Steel: 10 + CableHV: 5 + +- type: entity + id: ShuttleGunFriendshipCircuitboard + parent: BaseMachineCircuitboard + name: EXP-320g "Friendship" machine board + description: A machine printed circuit board for an EXP-320g "Friendship" + components: + - type: Sprite + state: security + - type: MachineBoard + prototype: ShuttleGunFriendship + requirements: + MatterBin: 3 + Manipulator: 2 + materialRequirements: + Steel: 7 + CableHV: 5 + +- type: entity + id: ShuttleGunDusterCircuitboard + parent: BaseMachineCircuitboard + name: EXP-2100g "Duster" machine board + description: A machine printed circuit board for an EXP-2100g "Duster" + components: + - type: Sprite + state: security + - type: MachineBoard + prototype: ShuttleGunDuster + requirements: + MatterBin: 6 + Manipulator: 4 + materialRequirements: + Steel: 10 + CableHV: 5 + Uranium: 2 + +- type: entity + id: ShuttleGunKineticCircuitboard + parent: BaseMachineCircuitboard + name: PTK-800 "Matter Dematerializer" machine board + description: A machine printed circuit board for an PTK-800 "Matter Dematerializer" + components: + - type: Sprite + state: security + - type: MachineBoard + prototype: ShuttleGunKinetic + requirements: + MatterBin: 2 + Manipulator: 3 + materialRequirements: + Steel: 5 + CableHV: 2 + - type: entity parent: BaseMachineCircuitboard id: ReagentGrinderIndustrialMachineCircuitboard diff --git a/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml b/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml index 50d31d3d39..6aa766bd58 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/flatpack.yml @@ -184,12 +184,3 @@ - state: overlay color: "#cec8ac" - state: icon-default - -- type: entity - parent: BaseFlatpack - id: SpaceHeaterFlatpack - name: space heater flatpack - description: A flatpack used for constructing a space heater. - components: - - type: Flatpack - entity: SpaceHeaterAnchored diff --git a/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml b/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml index 122ff42eb2..684260b737 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml @@ -29,7 +29,7 @@ components: - type: Instrument - type: ActivatableUI - blockSpectators: true # otherwise they can play client-side music + allowSpectator: false # otherwise they can play client-side music inHandsOnly: false singleUser: true requireHands: true diff --git a/Resources/Prototypes/Entities/Objects/Fun/darts.yml b/Resources/Prototypes/Entities/Objects/Fun/darts.yml index 36c841995e..18854f3d12 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/darts.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/darts.yml @@ -126,8 +126,8 @@ maxVol: 7 - type: SolutionInjectOnEmbed transferAmount: 7 - blockSlots: NONE solution: melee + blockSlots: NONE - type: SolutionTransfer maxTransferAmount: 7 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml index 6f694862b5..6fb0e7228f 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml @@ -325,12 +325,11 @@ emptyCase: { state: empty } wiredCase: { state: wired } caseWithTrigger: { state: no-payload } - caseWithPayload: { state: no-trigger } grenade: { state: complete } enum.Trigger.TriggerVisuals.VisualState: enum.ConstructionVisuals.Layer: Primed: { state: primed } - # Unprimed: + Unprimed: { state: complete } - type: StaticPrice price: 25 diff --git a/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml b/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml index 4fc57559a2..0d5df4a8b0 100644 --- a/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml +++ b/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml @@ -1,5 +1,3 @@ -# Most of these have DO NOT MAP, since stations are completely unequipped to deal with ship combat + these are basically placeholder. - - type: entity id: ShuttleGunBase name: shittle gun @@ -60,8 +58,7 @@ id: ShuttleGunSvalinnMachineGun parent: [ ShuttleGunBase, ConstructibleMachine] name: LSE-400c "Svalinn machine gun" - description: Basic stationary laser unit. Effective against live targets and electronics. Uses regular power cells to fire, and has an extremely high rate of fire. - suffix: DO NOT MAP + description: Basic stationary laser unit. Effective against live targets and electronics. Uses regular power cells to fire, and has an extremely high rate of fire components: - type: Sprite sprite: Objects/Weapons/Guns/Shuttles/laser.rsi @@ -116,7 +113,6 @@ parent: [ ShuttleGunBase, ConstructibleMachine] name: LSE-1200c "Perforator" description: Advanced stationary laser unit. Annihilates electronics and is extremely dangerous to health! Uses the power cage to fire. - suffix: DO NOT MAP components: - type: Sprite sprite: Objects/Weapons/Guns/Shuttles/laser.rsi @@ -173,7 +169,6 @@ parent: [ShuttleGunBase, ConstructibleMachine] name: EXP-320g "Friendship" description: A small stationary grenade launcher that holds 2 grenades. - suffix: DO NOT MAP components: - type: Sprite sprite: Objects/Weapons/Guns/Shuttles/launcher.rsi @@ -227,7 +222,6 @@ parent: [ShuttleGunBase, ConstructibleMachine] name: EXP-2100g "Duster" description: A powerful stationary grenade launcher. A cartridge is required for use. - suffix: DO NOT MAP components: - type: Sprite sprite: Objects/Weapons/Guns/Shuttles/launcher.rsi @@ -325,7 +319,6 @@ parent: [ ShuttleGunBase, ConstructibleMachine] name: PTK-800 "Matter Dematerializer" description: Salvage stationary mining turret. Gradually accumulates charges on its own, extremely effective for asteroid excavation. - suffix: DO NOT MAP components: - type: Sprite sprite: Objects/Weapons/Guns/Shuttles/kinetic.rsi diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index d933a786ec..fb52a6e879 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -413,9 +413,9 @@ prototype: InitialInfected - type: entity - noSpawn: true - parent: BaseNukeopsRule id: LoneOpsSpawn + parent: BaseGameRule + noSpawn: true components: - type: StationEvent earliestStart: 35 @@ -447,9 +447,9 @@ prototype: Nukeops - type: entity - noSpawn: true - parent: BaseTraitorRule id: SleeperAgentsRule + parent: BaseGameRule + noSpawn: true components: - type: StationEvent earliestStart: 30 @@ -460,6 +460,7 @@ startAudio: path: /Audio/Announcements/intercept.ogg - type: AlertLevelInterceptionRule + - type: TraitorRule - type: AntagSelection definitions: - prefRoles: [ Traitor ] diff --git a/Resources/Prototypes/GameRules/midround.yml b/Resources/Prototypes/GameRules/midround.yml index 081e37d7f0..4c3a2032e6 100644 --- a/Resources/Prototypes/GameRules/midround.yml +++ b/Resources/Prototypes/GameRules/midround.yml @@ -35,19 +35,7 @@ id: Thief components: - type: ThiefRule - - type: AntagObjectives - objectives: - - EscapeThiefShuttleObjective - - type: AntagRandomObjectives - sets: - - groups: ThiefBigObjectiveGroups - prob: 0.7 - maxPicks: 1 - - groups: ThiefObjectiveGroups - maxPicks: 10 - maxDifficulty: 2.5 - type: AntagSelection - agentName: thief-round-end-agent-name definitions: - prefRoles: [ Thief ] maxRange: diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index 7594962875..c3907e4206 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -64,9 +64,9 @@ roundEndDelay: 10 - type: entity - abstract: true + id: Nukeops parent: BaseGameRule - id: BaseNukeopsRule + noSpawn: true components: - type: GameRule minPlayers: 20 # Honk @@ -75,16 +75,6 @@ - operationPrefix - operationSuffix - type: NukeopsRule - - type: AntagSelection - - type: AntagLoadProfileRule - -- type: entity - noSpawn: true - parent: BaseNukeopsRule - id: Nukeops - components: - - type: GameRule - minPlayers: 20 - type: LoadMapRule gameMap: NukieOutpost - type: AntagSelection @@ -148,30 +138,16 @@ prototype: Nukeops - type: entity - abstract: true - parent: BaseGameRule - id: BaseTraitorRule - components: - - type: TraitorRule - # TODO: codewords in yml - # TODO: uplink in yml - - type: AntagRandomObjectives - sets: - - groups: TraitorObjectiveGroups - maxDifficulty: 5 - - type: AntagSelection - agentName: traitor-round-end-agent-name - -- type: entity - noSpawn: true - parent: BaseTraitorRule id: Traitor + parent: BaseGameRule + noSpawn: true components: - type: GameRule minPlayers: 5 delay: min: 240 max: 420 + - type: TraitorRule - type: AntagSelection definitions: - prefRoles: [ Traitor ] diff --git a/Resources/Prototypes/Objectives/objectiveGroups.yml b/Resources/Prototypes/Objectives/objectiveGroups.yml index bc0d248f6c..00340be276 100644 --- a/Resources/Prototypes/Objectives/objectiveGroups.yml +++ b/Resources/Prototypes/Objectives/objectiveGroups.yml @@ -55,6 +55,13 @@ ThiefObjectiveGroupStructure: 0 #Temporarily disabled until obvious ways to steal structures are added ThiefObjectiveGroupAnimal: 2 +- type: weightedRandom + id: ThiefEscapeObjectiveGroups + weights: + ThiefObjectiveGroupEscape: 1 + + + - type: weightedRandom id: ThiefObjectiveGroupCollection weights: diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/weapons/modular_grenade.yml b/Resources/Prototypes/Recipes/Construction/Graphs/weapons/modular_grenade.yml index 9db8616118..227f5b1cfd 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/weapons/modular_grenade.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/weapons/modular_grenade.yml @@ -50,12 +50,6 @@ store: payloadTrigger name: триггер doAfter: 0.5 - - to: caseWithPayload - steps: - - tag: Payload - store: payload - name: Payload - doAfter: 0.5 - node: caseWithTrigger actions: @@ -77,26 +71,6 @@ name: заряд doAfter: 0.5 - - node: caseWithPayload - actions: - - !type:AppearanceChange - - !type:PlaySound - sound: /Audio/Machines/button.ogg - edges: - - to: wiredCase - steps: - - tool: Prying - doAfter: 0.5 - completed: - - !type:EmptyContainer - container: payload - - to: grenade - steps: - - component: PayloadTrigger - store: payloadTrigger - name: Trigger - doAfter: 0.5 - - node: grenade actions: - !type:AppearanceChange diff --git a/Resources/Textures/Objects/Weapons/Grenades/modular.rsi/meta.json b/Resources/Textures/Objects/Weapons/Grenades/modular.rsi/meta.json index b0b12127c5..f23b6ec168 100644 --- a/Resources/Textures/Objects/Weapons/Grenades/modular.rsi/meta.json +++ b/Resources/Textures/Objects/Weapons/Grenades/modular.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/29c0ed1b000619cb5398ef921000a8d4502ba0b6 and modified by Swept & ElectroSR", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/29c0ed1b000619cb5398ef921000a8d4502ba0b6 and modified by Swept", "size": { "x": 32, "y": 32 @@ -19,10 +19,6 @@ "name": "no-payload", "directions": 1 }, - { - "name": "no-trigger", - "directions": 1 - }, { "name": "complete", "directions": 1 diff --git a/Resources/Textures/Objects/Weapons/Grenades/modular.rsi/no-trigger.png b/Resources/Textures/Objects/Weapons/Grenades/modular.rsi/no-trigger.png deleted file mode 100644 index 1be2cbc421..0000000000 Binary files a/Resources/Textures/Objects/Weapons/Grenades/modular.rsi/no-trigger.png and /dev/null differ diff --git a/RobustToolbox b/RobustToolbox index 721408bb37..ec794ce4e4 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 721408bb37317e7a66cd34655e068369c47912ac +Subproject commit ec794ce4e4693069d3b3ebf7a88ead5ff2f860e0