From a1b588fec4248665953eab2c24fc3241e85e58c6 Mon Sep 17 00:00:00 2001 From: spess-empyrean Date: Wed, 15 Jan 2025 02:39:30 -0600 Subject: [PATCH 01/64] Ports "Fix animation looping bugs" from wizden. --- Content.Client/Jittering/JitteringSystem.cs | 3 +++ Content.Client/Light/EntitySystems/LightBehaviorSystem.cs | 3 +++ Content.Client/Light/EntitySystems/RotatingLightSystem.cs | 3 +++ 3 files changed, 9 insertions(+) diff --git a/Content.Client/Jittering/JitteringSystem.cs b/Content.Client/Jittering/JitteringSystem.cs index aafaf318bb9..cd3e5065ffc 100644 --- a/Content.Client/Jittering/JitteringSystem.cs +++ b/Content.Client/Jittering/JitteringSystem.cs @@ -48,6 +48,9 @@ private void OnAnimationCompleted(EntityUid uid, JitteringComponent jittering, A if (args.Key != _jitterAnimationKey || jittering.LifeStage >= ComponentLifeStage.Stopping) return; + if (!args.Finished) + return; + if (TryComp(uid, out AnimationPlayerComponent? animationPlayer) && TryComp(uid, out SpriteComponent? sprite)) _animationPlayer.Play(uid, animationPlayer, GetAnimation(jittering, sprite), _jitterAnimationKey); diff --git a/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs b/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs index 11f69165cf6..ca19d8522c5 100644 --- a/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs +++ b/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs @@ -19,6 +19,9 @@ public override void Initialize() private void OnBehaviorAnimationCompleted(EntityUid uid, LightBehaviourComponent component, AnimationCompletedEvent args) { + if (!args.Finished) + return; + var container = component.Animations.FirstOrDefault(x => x.FullKey == args.Key); if (container == null) diff --git a/Content.Client/Light/EntitySystems/RotatingLightSystem.cs b/Content.Client/Light/EntitySystems/RotatingLightSystem.cs index 842c13dedfe..5c2c4e4c875 100644 --- a/Content.Client/Light/EntitySystems/RotatingLightSystem.cs +++ b/Content.Client/Light/EntitySystems/RotatingLightSystem.cs @@ -69,6 +69,9 @@ private void OnAfterAutoHandleState(EntityUid uid, RotatingLightComponent comp, private void OnAnimationComplete(EntityUid uid, RotatingLightComponent comp, AnimationCompletedEvent args) { + if (!args.Finished) + return; + PlayAnimation(uid, comp); } From 182820fdb2b1ab09ac72de640242a2230ce821ea Mon Sep 17 00:00:00 2001 From: spess-empyrean Date: Wed, 15 Jan 2025 02:54:47 -0600 Subject: [PATCH 02/64] Ports "Refactor LightBehaviorSystem" from wizden --- .../Components/LightBehaviourComponent.cs | 163 ++---------------- .../EntitySystems/ExpendableLightSystem.cs | 5 +- .../EntitySystems/LightBehaviorSystem.cs | 136 ++++++++++++++- Content.Client/Light/HandheldLightSystem.cs | 12 +- .../Items/VoidTorch/VoidTorchSystem.cs | 7 +- 5 files changed, 154 insertions(+), 169 deletions(-) diff --git a/Content.Client/Light/Components/LightBehaviourComponent.cs b/Content.Client/Light/Components/LightBehaviourComponent.cs index 7e8bf82a29e..bcdb890c013 100644 --- a/Content.Client/Light/Components/LightBehaviourComponent.cs +++ b/Content.Client/Light/Components/LightBehaviourComponent.cs @@ -68,7 +68,7 @@ public void UpdatePlaybackValues(Animation owner) if (MinDuration > 0) { - MaxTime = (float) _random.NextDouble() * (MaxDuration - MinDuration) + MinDuration; + MaxTime = (float)_random.NextDouble() * (MaxDuration - MinDuration) + MinDuration; } else { @@ -192,11 +192,11 @@ public override (int KeyFrameIndex, float FramePlayingTime) AdvancePlayback( { if (interpolateValue < 0.5f) { - ApplyInterpolation(StartValue, EndValue, interpolateValue*2); + ApplyInterpolation(StartValue, EndValue, interpolateValue * 2); } else { - ApplyInterpolation(EndValue, StartValue, (interpolateValue-0.5f)*2); + ApplyInterpolation(EndValue, StartValue, (interpolateValue - 0.5f) * 2); } } else @@ -238,9 +238,9 @@ public sealed partial class RandomizeBehaviour : LightBehaviourAnimationTrack public override void OnInitialize() { - _randomValue1 = (float) InterpolateLinear(StartValue, EndValue, (float) _random.NextDouble()); - _randomValue2 = (float) InterpolateLinear(StartValue, EndValue, (float) _random.NextDouble()); - _randomValue3 = (float) InterpolateLinear(StartValue, EndValue, (float) _random.NextDouble()); + _randomValue1 = (float)InterpolateLinear(StartValue, EndValue, (float) _random.NextDouble()); + _randomValue2 = (float)InterpolateLinear(StartValue, EndValue, (float) _random.NextDouble()); + _randomValue3 = (float)InterpolateLinear(StartValue, EndValue, (float) _random.NextDouble()); } public override void OnStart() @@ -258,7 +258,7 @@ public override void OnStart() } _randomValue3 = _randomValue4; - _randomValue4 = (float) InterpolateLinear(StartValue, EndValue, (float) _random.NextDouble()); + _randomValue4 = (float)InterpolateLinear(StartValue, EndValue, (float) _random.NextDouble()); } public override (int KeyFrameIndex, float FramePlayingTime) AdvancePlayback( @@ -362,7 +362,7 @@ public sealed partial class LightBehaviourComponent : SharedLightBehaviourCompon [Dependency] private readonly IEntityManager _entMan = default!; [Dependency] private readonly IRobustRandom _random = default!; - private const string KeyPrefix = nameof(LightBehaviourComponent); + public const string KeyPrefix = nameof(LightBehaviourComponent); public sealed class AnimationContainer { @@ -387,7 +387,7 @@ public AnimationContainer(int key, Animation animation, LightBehaviourAnimationT public readonly List Animations = new(); [ViewVariables(VVAccess.ReadOnly)] - private Dictionary _originalPropertyValues = new(); + public Dictionary OriginalPropertyValues = new(); void ISerializationHooks.AfterDeserialization() { @@ -397,155 +397,12 @@ void ISerializationHooks.AfterDeserialization() { var animation = new Animation() { - AnimationTracks = {behaviour} + AnimationTracks = { behaviour } }; Animations.Add(new AnimationContainer(key, animation, behaviour)); key++; } } - - /// - /// If we disable all the light behaviours we want to be able to revert the light to its original state. - /// - private void CopyLightSettings(EntityUid uid, string property) - { - if (_entMan.TryGetComponent(uid, out PointLightComponent? light)) - { - var propertyValue = AnimationHelper.GetAnimatableProperty(light, property); - if (propertyValue != null) - { - _originalPropertyValues.Add(property, propertyValue); - } - } - else - { - Logger.Warning($"{_entMan.GetComponent(uid).EntityName} has a {nameof(LightBehaviourComponent)} but it has no {nameof(PointLightComponent)}! Check the prototype!"); - } - } - - /// - /// Start animating a light behaviour with the specified ID. If the specified ID is empty, it will start animating all light behaviour entries. - /// If specified light behaviours are already animating, calling this does nothing. - /// Multiple light behaviours can have the same ID. - /// - public void StartLightBehaviour(string id = "") - { - var uid = Owner; - if (!_entMan.TryGetComponent(uid, out AnimationPlayerComponent? animation)) - { - return; - } - - var animations = _entMan.System(); - - foreach (var container in Animations) - { - if (container.LightBehaviour.ID == id || id == string.Empty) - { - if (!animations.HasRunningAnimation(uid, animation, KeyPrefix + container.Key)) - { - CopyLightSettings(uid, container.LightBehaviour.Property); - container.LightBehaviour.UpdatePlaybackValues(container.Animation); - animations.Play(uid, animation, container.Animation, KeyPrefix + container.Key); - } - } - } - } - - /// - /// If any light behaviour with the specified ID is animating, then stop it. - /// If no ID is specified then all light behaviours will be stopped. - /// Multiple light behaviours can have the same ID. - /// - /// - /// Should the behaviour(s) also be removed permanently? - /// Should the light have its original settings applied? - public void StopLightBehaviour(string id = "", bool removeBehaviour = false, bool resetToOriginalSettings = false) - { - var uid = Owner; - if (!_entMan.TryGetComponent(uid, out AnimationPlayerComponent? animation)) - { - return; - } - - var toRemove = new List(); - var animations = _entMan.System(); - - foreach (var container in Animations) - { - if (container.LightBehaviour.ID == id || id == string.Empty) - { - if (animations.HasRunningAnimation(uid, animation, KeyPrefix + container.Key)) - { - animations.Stop(uid, animation, KeyPrefix + container.Key); - } - - if (removeBehaviour) - { - toRemove.Add(container); - } - } - } - - foreach (var container in toRemove) - { - Animations.Remove(container); - } - - if (resetToOriginalSettings && _entMan.TryGetComponent(uid, out PointLightComponent? light)) - { - foreach (var (property, value) in _originalPropertyValues) - { - AnimationHelper.SetAnimatableProperty(light, property, value); - } - } - - _originalPropertyValues.Clear(); - } - - /// - /// Checks if at least one behaviour is running. - /// - /// Whether at least one behaviour is running, false if none is. - public bool HasRunningBehaviours() - { - var uid = Owner; - if (!_entMan.TryGetComponent(uid, out AnimationPlayerComponent? animation)) - { - return false; - } - - var animations = _entMan.System(); - return Animations.Any(container => animations.HasRunningAnimation(uid, animation, KeyPrefix + container.Key)); - } - - /// - /// Add a new light behaviour to the component and start it immediately unless otherwise specified. - /// - public void AddNewLightBehaviour(LightBehaviourAnimationTrack behaviour, bool playImmediately = true) - { - var key = 0; - - while (Animations.Any(x => x.Key == key)) - { - key++; - } - - var animation = new Animation() - { - AnimationTracks = {behaviour} - }; - - behaviour.Initialize(Owner, _random, _entMan); - - var container = new AnimationContainer(key, animation, behaviour); - Animations.Add(container); - - if (playImmediately) - { - StartLightBehaviour(behaviour.ID); - } - } } } diff --git a/Content.Client/Light/EntitySystems/ExpendableLightSystem.cs b/Content.Client/Light/EntitySystems/ExpendableLightSystem.cs index 80774067301..4d52b0933d4 100644 --- a/Content.Client/Light/EntitySystems/ExpendableLightSystem.cs +++ b/Content.Client/Light/EntitySystems/ExpendableLightSystem.cs @@ -11,6 +11,7 @@ public sealed class ExpendableLightSystem : VisualizerSystem(uid, ExpendableLightVisuals.Behavior, out var lightBehaviourID, args.Component) && TryComp(uid, out var lightBehaviour)) { - lightBehaviour.StopLightBehaviour(); + _lightBehavior.StopLightBehaviour((uid, lightBehaviour)); if (!string.IsNullOrEmpty(lightBehaviourID)) { - lightBehaviour.StartLightBehaviour(lightBehaviourID); + _lightBehavior.StartLightBehaviour((uid, lightBehaviour), lightBehaviourID); } else if (TryComp(uid, out var light)) { diff --git a/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs b/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs index ca19d8522c5..9301c31d3ea 100644 --- a/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs +++ b/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs @@ -1,7 +1,9 @@ using System.Linq; using Content.Client.Light.Components; using Robust.Client.GameObjects; +using Robust.Client.Animations; using Robust.Shared.Random; +using Robust.Shared.Animations; namespace Content.Client.Light.EntitySystems; @@ -36,23 +38,145 @@ private void OnBehaviorAnimationCompleted(EntityUid uid, LightBehaviourComponent } } - private void OnLightStartup(EntityUid uid, LightBehaviourComponent component, ComponentStartup args) + private void OnLightStartup(Entity entity, ref ComponentStartup args) { // TODO: Do NOT ensure component here. And use eventbus events instead... - EnsureComp(uid); + EnsureComp(entity); - foreach (var container in component.Animations) + foreach (var container in entity.Comp.Animations) { - container.LightBehaviour.Initialize(uid, _random, EntityManager); + container.LightBehaviour.Initialize(entity, _random, EntityManager); } // we need to initialize all behaviours before starting any - foreach (var container in component.Animations) + foreach (var container in entity.Comp.Animations) { if (container.LightBehaviour.Enabled) { - component.StartLightBehaviour(container.LightBehaviour.ID); + StartLightBehaviour(entity, container.LightBehaviour.ID); } } } + + /// + /// If we disable all the light behaviours we want to be able to revert the light to its original state. + /// + private void CopyLightSettings(Entity entity, string property) + { + if (EntityManager.TryGetComponent(entity, out PointLightComponent? light)) + { + var propertyValue = AnimationHelper.GetAnimatableProperty(light, property); + if (propertyValue != null) + { + entity.Comp.OriginalPropertyValues.Add(property, propertyValue); + } + } + else + { + Log.Warning($"{EntityManager.GetComponent(entity).EntityName} has a {nameof(LightBehaviourComponent)} but it has no {nameof(PointLightComponent)}! Check the prototype!"); + } + } + /// + /// Start animating a light behaviour with the specified ID. If the specified ID is empty, it will start animating all light behaviour entries. + /// If specified light behaviours are already animating, calling this does nothing. + /// Multiple light behaviours can have the same ID. + /// + public void StartLightBehaviour(Entity entity, string id = "") + { + if (!EntityManager.TryGetComponent(entity, out AnimationPlayerComponent? animation)) + { + return; + } + foreach (var container in entity.Comp.Animations) + { + if (container.LightBehaviour.ID == id || id == string.Empty) + { + if (!_player.HasRunningAnimation(entity, animation, LightBehaviourComponent.KeyPrefix + container.Key)) + { + CopyLightSettings(entity, container.LightBehaviour.Property); + container.LightBehaviour.UpdatePlaybackValues(container.Animation); + _player.Play(entity, container.Animation, LightBehaviourComponent.KeyPrefix + container.Key); + } + } + } + } + /// + /// If any light behaviour with the specified ID is animating, then stop it. + /// If no ID is specified then all light behaviours will be stopped. + /// Multiple light behaviours can have the same ID. + /// + /// + /// Should the behaviour(s) also be removed permanently? + /// Should the light have its original settings applied? + public void StopLightBehaviour(Entity entity, string id = "", bool removeBehaviour = false, bool resetToOriginalSettings = false) + { + if (!EntityManager.TryGetComponent(entity, out AnimationPlayerComponent? animation)) + { + return; + } + var comp = entity.Comp; + var toRemove = new List(); + foreach (var container in comp.Animations) + { + if (container.LightBehaviour.ID == id || id == string.Empty) + { + if (_player.HasRunningAnimation(entity, animation, LightBehaviourComponent.KeyPrefix + container.Key)) + { + _player.Stop(entity, animation, LightBehaviourComponent.KeyPrefix + container.Key); + } + if (removeBehaviour) + { + toRemove.Add(container); + } + } + } + foreach (var container in toRemove) + { + comp.Animations.Remove(container); + } + if (resetToOriginalSettings && EntityManager.TryGetComponent(entity, out PointLightComponent? light)) + { + foreach (var (property, value) in comp.OriginalPropertyValues) + { + AnimationHelper.SetAnimatableProperty(light, property, value); + } + } + comp.OriginalPropertyValues.Clear(); + } + /// + /// Checks if at least one behaviour is running. + /// + /// Whether at least one behaviour is running, false if none is. + public bool HasRunningBehaviours(Entity entity) + { + //var uid = Owner; + if (!EntityManager.TryGetComponent(entity, out AnimationPlayerComponent? animation)) + { + return false; + } + return entity.Comp.Animations.Any(container => _player.HasRunningAnimation(entity, animation, LightBehaviourComponent.KeyPrefix + container.Key)); + } + /// + /// Add a new light behaviour to the component and start it immediately unless otherwise specified. + /// + public void AddNewLightBehaviour(Entity entity, LightBehaviourAnimationTrack behaviour, bool playImmediately = true) + { + var key = 0; + var comp = entity.Comp; + while (comp.Animations.Any(x => x.Key == key)) + { + key++; + } + var animation = new Animation() + { + AnimationTracks = { behaviour } + }; + behaviour.Initialize(entity.Owner, _random, EntityManager); + var container = new LightBehaviourComponent.AnimationContainer(key, animation, behaviour); + comp.Animations.Add(container); + if (playImmediately) + { + StartLightBehaviour(entity, behaviour.ID); + } + } } diff --git a/Content.Client/Light/HandheldLightSystem.cs b/Content.Client/Light/HandheldLightSystem.cs index 7f18223811d..ddd99c7c483 100644 --- a/Content.Client/Light/HandheldLightSystem.cs +++ b/Content.Client/Light/HandheldLightSystem.cs @@ -3,15 +3,15 @@ using Content.Shared.Light; using Content.Shared.Light.Components; using Content.Shared.Toggleable; -using Robust.Client.Animations; using Robust.Client.GameObjects; -using Robust.Shared.Animations; +using Content.Client.Light.EntitySystems; namespace Content.Client.Light; public sealed class HandheldLightSystem : SharedHandheldLightSystem { [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly LightBehaviorSystem _lightBehavior = default!; public override void Initialize() { @@ -41,9 +41,9 @@ private void OnAppearanceChange(EntityUid uid, HandheldLightComponent? component if (TryComp(uid, out var lightBehaviour)) { // Reset any running behaviour to reset the animated properties back to the original value, to avoid conflicts between resets - if (lightBehaviour.HasRunningBehaviours()) + if (_lightBehavior.HasRunningBehaviours((uid, lightBehaviour))) { - lightBehaviour.StopLightBehaviour(resetToOriginalSettings: true); + _lightBehavior.StopLightBehaviour((uid, lightBehaviour), resetToOriginalSettings: true); } if (!enabled) @@ -56,10 +56,10 @@ private void OnAppearanceChange(EntityUid uid, HandheldLightComponent? component case HandheldLightPowerStates.FullPower: break; // We just needed to reset all behaviours case HandheldLightPowerStates.LowPower: - lightBehaviour.StartLightBehaviour(component.RadiatingBehaviourId); + _lightBehavior.StartLightBehaviour((uid, lightBehaviour), component.RadiatingBehaviourId); break; case HandheldLightPowerStates.Dying: - lightBehaviour.StartLightBehaviour(component.BlinkingBehaviourId); + _lightBehavior.StartLightBehaviour((uid, lightBehaviour), component.BlinkingBehaviourId); break; } } diff --git a/Content.Client/WhiteDream/BloodCult/Items/VoidTorch/VoidTorchSystem.cs b/Content.Client/WhiteDream/BloodCult/Items/VoidTorch/VoidTorchSystem.cs index 8b92fb7da89..3e0d1cfa161 100644 --- a/Content.Client/WhiteDream/BloodCult/Items/VoidTorch/VoidTorchSystem.cs +++ b/Content.Client/WhiteDream/BloodCult/Items/VoidTorch/VoidTorchSystem.cs @@ -1,4 +1,5 @@ using Content.Client.Light.Components; +using Content.Client.Light.EntitySystems; using Content.Shared.WhiteDream.BloodCult; using Content.Shared.WhiteDream.BloodCult.Items.VoidTorch; using Robust.Client.GameObjects; @@ -7,6 +8,8 @@ namespace Content.Client.WhiteDream.BloodCult.Items.VoidTorch; public sealed class VoidTorchSystem : VisualizerSystem { + [Dependency] private readonly LightBehaviorSystem _lightBehavior = default!; + protected override void OnAppearanceChange(EntityUid uid, VoidTorchComponent component, ref AppearanceChangeEvent args) @@ -17,7 +20,7 @@ protected override void OnAppearanceChange(EntityUid uid, || !TryComp(uid, out var lightBehaviour)) return; - lightBehaviour.StopLightBehaviour(); - lightBehaviour.StartLightBehaviour(state ? component.TurnOnLightBehaviour : component.TurnOffLightBehaviour); + _lightBehavior.StopLightBehaviour((uid, lightBehaviour)); + _lightBehavior.StartLightBehaviour((uid, lightBehaviour), state ? component.TurnOnLightBehaviour : component.TurnOffLightBehaviour); } } From 1885d33f38919c1fc6c504447affc61dc7b9b693 Mon Sep 17 00:00:00 2001 From: spess-empyrean Date: Wed, 15 Jan 2025 15:41:46 -0600 Subject: [PATCH 03/64] Apply suggestions from code review Co-authored-by: VMSolidus Signed-off-by: spess-empyrean --- .../EntitySystems/LightBehaviorSystem.cs | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs b/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs index 9301c31d3ea..789e6e1d207 100644 --- a/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs +++ b/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs @@ -84,20 +84,17 @@ private void CopyLightSettings(Entity entity, string pr public void StartLightBehaviour(Entity entity, string id = "") { if (!EntityManager.TryGetComponent(entity, out AnimationPlayerComponent? animation)) - { return; - } + foreach (var container in entity.Comp.Animations) { - if (container.LightBehaviour.ID == id || id == string.Empty) - { - if (!_player.HasRunningAnimation(entity, animation, LightBehaviourComponent.KeyPrefix + container.Key)) - { - CopyLightSettings(entity, container.LightBehaviour.Property); - container.LightBehaviour.UpdatePlaybackValues(container.Animation); - _player.Play(entity, container.Animation, LightBehaviourComponent.KeyPrefix + container.Key); - } - } + if (container.LightBehaviour.ID != id || id != string.Empty + || _player.HasRunningAnimation(entity, animation, LightBehaviourComponent.KeyPrefix + container.Key)) + continue; + + CopyLightSettings(entity, container.LightBehaviour.Property); + container.LightBehaviour.UpdatePlaybackValues(container.Animation); + _player.Play(entity, container.Animation, LightBehaviourComponent.KeyPrefix + container.Key); } } /// @@ -118,17 +115,14 @@ public void StopLightBehaviour(Entity entity, string id var toRemove = new List(); foreach (var container in comp.Animations) { - if (container.LightBehaviour.ID == id || id == string.Empty) - { - if (_player.HasRunningAnimation(entity, animation, LightBehaviourComponent.KeyPrefix + container.Key)) - { - _player.Stop(entity, animation, LightBehaviourComponent.KeyPrefix + container.Key); - } - if (removeBehaviour) - { - toRemove.Add(container); - } - } + if (container.LightBehaviour.ID != id || id != string.Empty) + continue; + + if (_player.HasRunningAnimation(entity, animation, LightBehaviourComponent.KeyPrefix + container.Key)) + _player.Stop(entity, animation, LightBehaviourComponent.KeyPrefix + container.Key); + + if (removeBehaviour) + toRemove.Add(container); } foreach (var container in toRemove) { From 558578399f24a1e6c0bb3818d123795e4350662e Mon Sep 17 00:00:00 2001 From: Angelo Fallaria Date: Thu, 16 Jan 2025 18:51:41 +0800 Subject: [PATCH 04/64] increase brain eat delay and show popup when trying to eat brain --- .../Nutrition/Components/FoodComponent.cs | 6 +++++ .../Nutrition/EntitySystems/FoodSystem.cs | 27 +++++++++++++++++-- .../nutrition/components/food-component.ftl | 5 ++++ Resources/Prototypes/Body/Organs/diona.yml | 4 +++ Resources/Prototypes/Body/Organs/human.yml | 6 ++++- .../_Shitmed/Body/Organs/Animal/animal.yml | 4 +++ 6 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 Resources/Locale/en-US/_Shitmed/nutrition/components/food-component.ftl diff --git a/Content.Server/Nutrition/Components/FoodComponent.cs b/Content.Server/Nutrition/Components/FoodComponent.cs index cfb69f53f0c..a8fe7517c4d 100644 --- a/Content.Server/Nutrition/Components/FoodComponent.cs +++ b/Content.Server/Nutrition/Components/FoodComponent.cs @@ -69,6 +69,12 @@ public sealed partial class FoodComponent : Component [DataField] public float ForceFeedDelay = 3; + /// + /// Shitmed Change: Whether to show a popup to everyone in range when attempting to eat this food. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public bool PopupOnEatAttempt = false; + /// /// For mobs that are food, requires killing them before eating. /// diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index f99cecc8e7a..82e4865b040 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -24,6 +24,7 @@ using Content.Shared.Mobs.Systems; using Content.Shared.Nutrition; using Content.Shared.Nutrition.EntitySystems; +using Content.Shared.Popups; // Shitmed Change using Content.Shared.Stacks; using Content.Shared.Storage; using Content.Shared.Verbs; @@ -164,10 +165,14 @@ private void OnFeedFood(Entity entity, ref AfterInteractEvent arg return (false, true); } + // Shitmed Change + EntityUid? userName = null; + var forceFeed = user != target; if (forceFeed) { - var userName = Identity.Entity(user, EntityManager); + // Shitmed Change + userName = Identity.Entity(user, EntityManager); _popup.PopupEntity( Loc.GetString("food-system-force-feed", ("user", userName)), user, @@ -204,7 +209,25 @@ private void OnFeedFood(Entity entity, ref AfterInteractEvent arg NeedHand = forceFeed }; - _doAfter.TryStartDoAfter(doAfterArgs); + // Shitmed Change - track success of doafter to prevent popup on doafter cancel + var doAfterSuccess = _doAfter.TryStartDoAfter(doAfterArgs); + + // Shitmed Change + if (foodComp.PopupOnEatAttempt && doAfterSuccess) + { + userName ??= Identity.Entity(user, EntityManager); + var foodName = Identity.Entity(food, EntityManager); + _popup.PopupPredicted( + !forceFeed + ? Loc.GetString("food-system-eat-popup", ("user", userName), ("food", foodName)) + : Loc.GetString("food-system-force-feed-popup", ("user", userName), ("target", Identity.Entity(target, EntityManager)), ("food", foodName)), + user, target, PopupType.SmallCaution); + + if (!forceFeed) + _popup.PopupEntity(Loc.GetString("food-system-eat-popup-self", ("user", userName), ("food", foodName)), + user, target, PopupType.SmallCaution); + } + return (true, true); } diff --git a/Resources/Locale/en-US/_Shitmed/nutrition/components/food-component.ftl b/Resources/Locale/en-US/_Shitmed/nutrition/components/food-component.ftl new file mode 100644 index 00000000000..91c04c9ee28 --- /dev/null +++ b/Resources/Locale/en-US/_Shitmed/nutrition/components/food-component.ftl @@ -0,0 +1,5 @@ +## System + +food-system-eat-popup = {CAPITALIZE(THE($user))} is trying to eat {THE($food)}! +food-system-eat-popup-self = You start trying to eat {THE($food)}! +food-system-force-feed-popup = {CAPITALIZE(THE($user))} is trying to feed {THE($target)} {THE($food)}! diff --git a/Resources/Prototypes/Body/Organs/diona.yml b/Resources/Prototypes/Body/Organs/diona.yml index fb6c819493b..ce47ff123d9 100644 --- a/Resources/Prototypes/Body/Organs/diona.yml +++ b/Resources/Prototypes/Body/Organs/diona.yml @@ -42,6 +42,10 @@ state: brain - type: Organ # Shitmed slotId: brain + - type: Food # Shitmed Change + delay: 5 + forceFeedDelay: 6 + popupOnEatAttempt: true - type: Brain # Shitmed - type: SolutionContainerManager solutions: diff --git a/Resources/Prototypes/Body/Organs/human.yml b/Resources/Prototypes/Body/Organs/human.yml index 788e582f714..f3532b5ec4a 100644 --- a/Resources/Prototypes/Body/Organs/human.yml +++ b/Resources/Prototypes/Body/Organs/human.yml @@ -45,6 +45,10 @@ state: brain - type: Organ slotId: brain # Shitmed Change + - type: Food # Shitmed Change + delay: 5 + forceFeedDelay: 6 + popupOnEatAttempt: true - type: Input context: "ghost" - type: Brain @@ -73,7 +77,7 @@ entries: Burger: Brain Taco: Brain - + - type: entity id: OrganHumanEyes parent: BaseHumanOrgan diff --git a/Resources/Prototypes/_Shitmed/Body/Organs/Animal/animal.yml b/Resources/Prototypes/_Shitmed/Body/Organs/Animal/animal.yml index e45e2b28bdf..178968841f2 100644 --- a/Resources/Prototypes/_Shitmed/Body/Organs/Animal/animal.yml +++ b/Resources/Prototypes/_Shitmed/Body/Organs/Animal/animal.yml @@ -8,6 +8,10 @@ state: brain - type: Organ slotId: brain + - type: Food + delay: 5 + forceFeedDelay: 6 + popupOnEatAttempt: true - type: Input context: "ghost" - type: Brain From dd5ee5a5be7473f744216903fe1d9fcb83becc45 Mon Sep 17 00:00:00 2001 From: Angelo Fallaria Date: Thu, 16 Jan 2025 19:44:26 +0800 Subject: [PATCH 05/64] add popup for eat success --- .../Nutrition/Components/FoodComponent.cs | 4 ++-- .../Nutrition/EntitySystems/FoodSystem.cs | 20 +++++++++++++++---- .../nutrition/components/food-component.ftl | 8 +++++--- Resources/Prototypes/Body/Organs/diona.yml | 2 +- Resources/Prototypes/Body/Organs/human.yml | 2 +- .../_Shitmed/Body/Organs/Animal/animal.yml | 2 +- 6 files changed, 26 insertions(+), 12 deletions(-) diff --git a/Content.Server/Nutrition/Components/FoodComponent.cs b/Content.Server/Nutrition/Components/FoodComponent.cs index a8fe7517c4d..de325ba35a4 100644 --- a/Content.Server/Nutrition/Components/FoodComponent.cs +++ b/Content.Server/Nutrition/Components/FoodComponent.cs @@ -70,10 +70,10 @@ public sealed partial class FoodComponent : Component public float ForceFeedDelay = 3; /// - /// Shitmed Change: Whether to show a popup to everyone in range when attempting to eat this food. + /// Shitmed Change: Whether to show a popup to everyone in range when attempting to eat this food, and upon successful eating. /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public bool PopupOnEatAttempt = false; + public bool PopupOnEat = false; /// /// For mobs that are food, requires killing them before eating. diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index 82e4865b040..1f80aeb3454 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -30,6 +30,7 @@ using Content.Shared.Verbs; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; +using Robust.Shared.Player; // Shitmed Change using Robust.Shared.Utility; using System.Linq; using Content.Shared.CCVar; @@ -213,18 +214,18 @@ private void OnFeedFood(Entity entity, ref AfterInteractEvent arg var doAfterSuccess = _doAfter.TryStartDoAfter(doAfterArgs); // Shitmed Change - if (foodComp.PopupOnEatAttempt && doAfterSuccess) + if (foodComp.PopupOnEat && doAfterSuccess) { userName ??= Identity.Entity(user, EntityManager); var foodName = Identity.Entity(food, EntityManager); _popup.PopupPredicted( !forceFeed - ? Loc.GetString("food-system-eat-popup", ("user", userName), ("food", foodName)) - : Loc.GetString("food-system-force-feed-popup", ("user", userName), ("target", Identity.Entity(target, EntityManager)), ("food", foodName)), + ? Loc.GetString("food-system-eat-broadcasted", ("user", userName), ("food", foodName)) + : Loc.GetString("food-system-force-feed-broadcasted", ("user", userName), ("target", Identity.Entity(target, EntityManager)), ("food", foodName)), user, target, PopupType.SmallCaution); if (!forceFeed) - _popup.PopupEntity(Loc.GetString("food-system-eat-popup-self", ("user", userName), ("food", foodName)), + _popup.PopupEntity(Loc.GetString("food-system-eat-broadcasted-self", ("user", userName), ("food", foodName)), user, target, PopupType.SmallCaution); } @@ -304,6 +305,12 @@ private void OnDoAfter(Entity entity, ref ConsumeDoAfterEvent arg _popup.PopupEntity(Loc.GetString("food-system-force-feed-success-user", ("target", targetName)), args.User, args.User); + // Shitmed change + if (entity.Comp.PopupOnEat) + _popup.PopupEntity(Loc.GetString("food-system-force-feed-broadcasted-success", ("user", userName), ("target", targetName), ("food", Identity.Entity(entity.Owner, EntityManager))), + args.User, Filter.Pvs(args.User, entityManager: EntityManager).RemovePlayersByAttachedEntity([args.User, args.Target.Value]), + true, PopupType.MediumCaution); + // log successful force feed _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(entity.Owner):user} forced {ToPrettyString(args.User):target} to eat {ToPrettyString(entity.Owner):food}"); } @@ -311,6 +318,11 @@ private void OnDoAfter(Entity entity, ref ConsumeDoAfterEvent arg { _popup.PopupEntity(Loc.GetString(entity.Comp.EatMessage, ("food", entity.Owner), ("flavors", flavors)), args.User, args.User); + // Shitmed change + if (entity.Comp.PopupOnEat) + _popup.PopupPredicted(Loc.GetString("food-system-eat-broadcasted-success", ("user", Identity.Entity(args.User, EntityManager)), ("food", Identity.Entity(entity.Owner, EntityManager))), + args.User, args.User, PopupType.MediumCaution); + // log successful voluntary eating _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(args.User):target} ate {ToPrettyString(entity.Owner):food}"); } diff --git a/Resources/Locale/en-US/_Shitmed/nutrition/components/food-component.ftl b/Resources/Locale/en-US/_Shitmed/nutrition/components/food-component.ftl index 91c04c9ee28..ebd3273b18b 100644 --- a/Resources/Locale/en-US/_Shitmed/nutrition/components/food-component.ftl +++ b/Resources/Locale/en-US/_Shitmed/nutrition/components/food-component.ftl @@ -1,5 +1,7 @@ ## System -food-system-eat-popup = {CAPITALIZE(THE($user))} is trying to eat {THE($food)}! -food-system-eat-popup-self = You start trying to eat {THE($food)}! -food-system-force-feed-popup = {CAPITALIZE(THE($user))} is trying to feed {THE($target)} {THE($food)}! +food-system-eat-broadcasted = {CAPITALIZE(THE($user))} is trying to eat {THE($food)}! +food-system-eat-broadcasted-self = You start trying to eat {THE($food)}! +food-system-force-feed-broadcasted = {CAPITALIZE(THE($user))} is trying to feed {THE($target)} {THE($food)}! +food-system-force-feed-broadcasted-success = {CAPITALIZE(THE($user))} forced {THE($target)} to eat {THE($food)}! +food-system-eat-broadcasted-success = {CAPITALIZE(THE($user))} ate {THE($food)}! diff --git a/Resources/Prototypes/Body/Organs/diona.yml b/Resources/Prototypes/Body/Organs/diona.yml index ce47ff123d9..17b9704fe96 100644 --- a/Resources/Prototypes/Body/Organs/diona.yml +++ b/Resources/Prototypes/Body/Organs/diona.yml @@ -45,7 +45,7 @@ - type: Food # Shitmed Change delay: 5 forceFeedDelay: 6 - popupOnEatAttempt: true + popupOnEat: true - type: Brain # Shitmed - type: SolutionContainerManager solutions: diff --git a/Resources/Prototypes/Body/Organs/human.yml b/Resources/Prototypes/Body/Organs/human.yml index f3532b5ec4a..92dd537d561 100644 --- a/Resources/Prototypes/Body/Organs/human.yml +++ b/Resources/Prototypes/Body/Organs/human.yml @@ -48,7 +48,7 @@ - type: Food # Shitmed Change delay: 5 forceFeedDelay: 6 - popupOnEatAttempt: true + popupOnEat: true - type: Input context: "ghost" - type: Brain diff --git a/Resources/Prototypes/_Shitmed/Body/Organs/Animal/animal.yml b/Resources/Prototypes/_Shitmed/Body/Organs/Animal/animal.yml index 178968841f2..b7a40d18a79 100644 --- a/Resources/Prototypes/_Shitmed/Body/Organs/Animal/animal.yml +++ b/Resources/Prototypes/_Shitmed/Body/Organs/Animal/animal.yml @@ -11,7 +11,7 @@ - type: Food delay: 5 forceFeedDelay: 6 - popupOnEatAttempt: true + popupOnEat: true - type: Input context: "ghost" - type: Brain From c8093166fa45e67898f234c05579ae77f4c76ab6 Mon Sep 17 00:00:00 2001 From: Angelo Fallaria Date: Thu, 16 Jan 2025 19:56:04 +0800 Subject: [PATCH 06/64] Fix a funny bug trying to send popup into food instead of target --- Content.Server/Nutrition/EntitySystems/FoodSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index 1f80aeb3454..7eb3a715c06 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -301,7 +301,7 @@ private void OnDoAfter(Entity entity, ref ConsumeDoAfterEvent arg { var targetName = Identity.Entity(args.Target.Value, EntityManager); var userName = Identity.Entity(args.User, EntityManager); - _popup.PopupEntity(Loc.GetString("food-system-force-feed-success", ("user", userName), ("flavors", flavors)), entity.Owner, entity.Owner); + _popup.PopupEntity(Loc.GetString("food-system-force-feed-success", ("user", userName), ("flavors", flavors)), args.Target.Value, args.Target.Value); _popup.PopupEntity(Loc.GetString("food-system-force-feed-success-user", ("target", targetName)), args.User, args.User); From 36a9789a4dccac1f2c8ce814581f09f57b401b12 Mon Sep 17 00:00:00 2001 From: Angelo Fallaria Date: Thu, 16 Jan 2025 22:29:23 +0800 Subject: [PATCH 07/64] add movement cost to plastic flaps --- Resources/Locale/en-US/traits/traits.ftl | 2 +- .../Prototypes/Entities/Structures/plastic_flaps.yml | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index 92fa6f74a22..03e7e47aad3 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -224,7 +224,7 @@ trait-description-Voracious = Nothing gets between you and your food. Your endless consumption of food and drinks is twice as fast. --terrain-example = [color=gray](e.g. spider web, slime puddle, kudzu, space glue)[/color] +-terrain-example = [color=gray](e.g. plastic flaps, spider web, slime puddle, kudzu)[/color] -slippery-example = [color=gray](e.g. banana peel, water puddle, soap, space lube)[/color] trait-name-ParkourTraining = Parkour Training diff --git a/Resources/Prototypes/Entities/Structures/plastic_flaps.yml b/Resources/Prototypes/Entities/Structures/plastic_flaps.yml index 5d5ff390bac..ea1ed8362dd 100644 --- a/Resources/Prototypes/Entities/Structures/plastic_flaps.yml +++ b/Resources/Prototypes/Entities/Structures/plastic_flaps.yml @@ -42,7 +42,10 @@ - type: StaticPrice price: 83 - type: RequireProjectileTarget - + - type: SpeedModifierContacts + walkSpeedModifier: 0.4 + sprintSpeedModifier: 0.4 + - type: entity id: PlasticFlapsClear parent: PlasticFlapsBase @@ -97,6 +100,9 @@ - type: Airtight - type: StaticPrice price: 100 + - type: SpeedModifierContacts + walkSpeedModifier: 0.3 + sprintSpeedModifier: 0.3 - type: entity id: PlasticFlapsAirtightOpaque @@ -116,3 +122,6 @@ - type: Airtight - type: StaticPrice price: 100 + - type: SpeedModifierContacts + walkSpeedModifier: 0.3 + sprintSpeedModifier: 0.3 From 3fa788c928868eaa70eff7281d1fdca2e2b40e13 Mon Sep 17 00:00:00 2001 From: themias <89101928+themias@users.noreply.github.com> Date: Thu, 25 Jul 2024 01:23:52 -0400 Subject: [PATCH 08/64] Fix Ripley control panel (#30325) --- Content.Client/Mech/Ui/MechMenu.xaml.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Content.Client/Mech/Ui/MechMenu.xaml.cs b/Content.Client/Mech/Ui/MechMenu.xaml.cs index 6f39bc386ee..7ce863b7cd7 100644 --- a/Content.Client/Mech/Ui/MechMenu.xaml.cs +++ b/Content.Client/Mech/Ui/MechMenu.xaml.cs @@ -12,7 +12,7 @@ public sealed partial class MechMenu : FancyWindow { [Dependency] private readonly IEntityManager _ent = default!; - private readonly EntityUid _mech; + private EntityUid _mech; public event Action? OnRemoveButtonPressed; @@ -25,6 +25,7 @@ public MechMenu() public void SetEntity(EntityUid uid) { MechView.SetEntity(uid); + _mech = uid; } public void UpdateMechStats() From 672c537811bf3b11f6b10362a23e6303215eb3ba Mon Sep 17 00:00:00 2001 From: JIPDawg <51352440+JIPDawg@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:09:00 -0500 Subject: [PATCH 09/64] Make small clamp use 2% battery instead of recharging 2% battery. (#32320) Make small clamp user 2% battery instead of recharge 2% battery. Co-authored-by: JIPDawg --- .../Entities/Objects/Specific/Mech/mecha_equipment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Objects/Specific/Mech/mecha_equipment.yml b/Resources/Prototypes/Entities/Objects/Specific/Mech/mecha_equipment.yml index c489dec1c56..a97ca0f017b 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Mech/mecha_equipment.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Mech/mecha_equipment.yml @@ -39,7 +39,7 @@ - type: MechGrabber maxContents: 4 grabDelay: 3 - grabEnergyDelta: 20 + grabEnergyDelta: -20 - type: Tag tags: - SmallMech From 3d6c151e6f8185f3e0599a3f14a90eddcaacec13 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Wed, 2 Oct 2024 05:22:09 +0200 Subject: [PATCH 10/64] fix voice mask chameleon menu (#32546) --- Resources/Prototypes/Entities/Clothing/Masks/specific.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Resources/Prototypes/Entities/Clothing/Masks/specific.yml b/Resources/Prototypes/Entities/Clothing/Masks/specific.yml index 90c648c9d88..d8da80611cf 100644 --- a/Resources/Prototypes/Entities/Clothing/Masks/specific.yml +++ b/Resources/Prototypes/Entities/Clothing/Masks/specific.yml @@ -35,6 +35,8 @@ - Snout - type: UserInterface interfaces: + enum.ChameleonUiKey.Key: + type: ChameleonBoundUserInterface enum.VoiceMaskUIKey.Key: type: VoiceMaskBoundUserInterface From 5cf5f8907935a67f40c537a092bb84e41468195d Mon Sep 17 00:00:00 2001 From: chavonadelal <156101927+chavonadelal@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:10:53 +0300 Subject: [PATCH 11/64] Wires ui tooltip localization (#32284) * Wires ui tooltip localization * Corrections after review --- Content.Client/Wires/UI/WiresMenu.cs | 9 +-------- .../Locale/en-US/wires/components/wires-component.ftl | 6 ++++++ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Content.Client/Wires/UI/WiresMenu.cs b/Content.Client/Wires/UI/WiresMenu.cs index 2eaa9cc1309..01c88bcd0c6 100644 --- a/Content.Client/Wires/UI/WiresMenu.cs +++ b/Content.Client/Wires/UI/WiresMenu.cs @@ -586,17 +586,10 @@ public StatusLight(StatusLightData data, IResourceCache resourceCache) private sealed class HelpPopup : Popup { - private const string Text = "Click on the gold contacts with a multitool in hand to pulse their wire.\n" + - "Click on the wires with a pair of wirecutters in hand to cut/mend them.\n\n" + - "The lights at the top show the state of the machine, " + - "messing with wires will probably do stuff to them.\n" + - "Wire layouts are different each round, " + - "but consistent between machines of the same type."; - public HelpPopup() { var label = new RichTextLabel(); - label.SetMessage(Text); + label.SetMessage(Loc.GetString("wires-menu-help-popup")); AddChild(new PanelContainer { StyleClasses = {ExamineSystem.StyleClassEntityTooltip}, diff --git a/Resources/Locale/en-US/wires/components/wires-component.ftl b/Resources/Locale/en-US/wires/components/wires-component.ftl index be27c270bb4..e98e5c21cab 100644 --- a/Resources/Locale/en-US/wires/components/wires-component.ftl +++ b/Resources/Locale/en-US/wires/components/wires-component.ftl @@ -10,3 +10,9 @@ wires-component-ui-on-receive-message-cannot-mend-uncut-wire = You can't mend a wires-menu-name-label = Wires wires-menu-dead-beef-text = DEAD-BEEF +wires-menu-help-popup = + Click on the gold contacts with a multitool in hand to pulse their wire. + Click on the wires with a pair of wirecutters in hand to cut/mend them. + + The lights at the top show the state of the machine, messing with wires will probably do stuff to them. + Wire layouts are different each round, but consistent between machines of the same type. From aae88528840fdab7a6d50eb1d237b8c69841431f Mon Sep 17 00:00:00 2001 From: themias <89101928+themias@users.noreply.github.com> Date: Mon, 29 Jul 2024 23:04:17 -0400 Subject: [PATCH 12/64] Fix ACC wire light not appearing (#30453) --- Content.Server/Access/LogWireAction.cs | 2 +- Content.Shared/Access/SharedAccessWire.cs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Content.Server/Access/LogWireAction.cs b/Content.Server/Access/LogWireAction.cs index 1e97d5c9d67..837cf420d5e 100644 --- a/Content.Server/Access/LogWireAction.cs +++ b/Content.Server/Access/LogWireAction.cs @@ -25,7 +25,7 @@ public sealed partial class LogWireAction : ComponentWireAction AccessWireActionKey.Status; + public override object StatusKey => LogWireActionKey.Status; public override void Initialize() { diff --git a/Content.Shared/Access/SharedAccessWire.cs b/Content.Shared/Access/SharedAccessWire.cs index d5d1b48c708..a0bb316028f 100644 --- a/Content.Shared/Access/SharedAccessWire.cs +++ b/Content.Shared/Access/SharedAccessWire.cs @@ -10,3 +10,12 @@ public enum AccessWireActionKey : byte Pulsed, PulseCancel } + +[Serializable, NetSerializable] +public enum LogWireActionKey : byte +{ + Key, + Status, + Pulsed, + PulseCancel +} From 2b170b825583d5769bd232b1155e6b6fa567a998 Mon Sep 17 00:00:00 2001 From: chromiumboy <50505512+chromiumboy@users.noreply.github.com> Date: Sat, 28 Dec 2024 17:33:15 -0600 Subject: [PATCH 13/64] UI improvements for holopads (#34055) * Initial commit * Minor update --- Content.Client/Holopad/HolopadWindow.xaml | 31 +++++++++++++------ Content.Client/Holopad/HolopadWindow.xaml.cs | 12 +++++-- Content.Server/Telephone/TelephoneSystem.cs | 5 +-- .../Telephone/SharedTelephoneSystem.cs | 17 ++++++++++ .../Telephone/TelephoneComponent.cs | 3 +- Resources/Locale/en-US/holopad/holopad.ftl | 2 ++ .../Locale/en-US/telephone/telephone.ftl | 4 ++- 7 files changed, 57 insertions(+), 17 deletions(-) diff --git a/Content.Client/Holopad/HolopadWindow.xaml b/Content.Client/Holopad/HolopadWindow.xaml index 9c3dfab1ea6..882f918158a 100644 --- a/Content.Client/Holopad/HolopadWindow.xaml +++ b/Content.Client/Holopad/HolopadWindow.xaml @@ -27,7 +27,11 @@ @@ -68,18 +72,25 @@ - + - + + + + - - - + - - - + + + + + + + + diff --git a/Content.Client/Holopad/HolopadWindow.xaml.cs b/Content.Client/Holopad/HolopadWindow.xaml.cs index bcab0d43df1..25982b901c2 100644 --- a/Content.Client/Holopad/HolopadWindow.xaml.cs +++ b/Content.Client/Holopad/HolopadWindow.xaml.cs @@ -171,8 +171,10 @@ public void UpdateState(Dictionary holopads) // Caller ID text var callerId = _telephoneSystem.GetFormattedCallerIdForEntity(telephone.LastCallerId.Item1, telephone.LastCallerId.Item2, Color.LightGray, "Default", 11); + var holoapdId = _telephoneSystem.GetFormattedDeviceIdForEntity(telephone.LastCallerId.Item3, Color.LightGray, "Default", 11); CallerIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(callerId)); + HolopadIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(holoapdId)); LockOutIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(callerId)); // Sort holopads alphabetically @@ -236,10 +238,13 @@ private void UpdateAppearance() // Make / update required children foreach (var child in ContactsList.Children) { - if (child is not HolopadContactButton) + if (child is not HolopadContactButton contactButton) continue; - var contactButton = (HolopadContactButton)child; + var passesFilter = string.IsNullOrEmpty(SearchLineEdit.Text) || + contactButton.Text?.Contains(SearchLineEdit.Text, StringComparison.CurrentCultureIgnoreCase) == true; + + contactButton.Visible = passesFilter; contactButton.Disabled = (_currentState != TelephoneState.Idle || lockButtons); } @@ -290,7 +295,7 @@ private void UpdateAppearance() FetchingAvailableHolopadsContainer.Visible = (ContactsList.ChildCount == 0); ActiveCallControlsContainer.Visible = (_currentState != TelephoneState.Idle || _currentUiKey == HolopadUiKey.AiRequestWindow); CallPlacementControlsContainer.Visible = !ActiveCallControlsContainer.Visible; - CallerIdText.Visible = (_currentState == TelephoneState.Ringing); + CallerIdContainer.Visible = (_currentState == TelephoneState.Ringing); AnswerCallButton.Visible = (_currentState == TelephoneState.Ringing); } @@ -316,6 +321,7 @@ public HolopadContactButton() HorizontalExpand = true; SetHeight = 32; Margin = new Thickness(0f, 1f, 0f, 1f); + ReservesSpace = false; } public void UpdateValues(NetEntity netEntity, string label) diff --git a/Content.Server/Telephone/TelephoneSystem.cs b/Content.Server/Telephone/TelephoneSystem.cs index b81a72ad230..b8337c766cc 100644 --- a/Content.Server/Telephone/TelephoneSystem.cs +++ b/Content.Server/Telephone/TelephoneSystem.cs @@ -151,7 +151,7 @@ public override void Update(float frameTime) break; - // Try to hang up if their has been no recent in-call activity + // Try to hang up if there has been no recent in-call activity case TelephoneState.InCall: if (_timing.CurTime > telephone.StateStartTime + TimeSpan.FromSeconds(telephone.IdlingTimeout)) EndTelephoneCalls(entity); @@ -214,7 +214,8 @@ private bool TryCallTelephone(Entity source, Entity /// The presumed name and/or job of the last person to call this telephone + /// and the name of the device that they used to do so /// [ViewVariables, AutoNetworkedField] - public (string?, string?) LastCallerId; + public (string?, string?, string?) LastCallerId; } #region: Telephone events diff --git a/Resources/Locale/en-US/holopad/holopad.ftl b/Resources/Locale/en-US/holopad/holopad.ftl index 47c5fbe25cf..7c890ee27ac 100644 --- a/Resources/Locale/en-US/holopad/holopad.ftl +++ b/Resources/Locale/en-US/holopad/holopad.ftl @@ -6,6 +6,7 @@ holopad-window-options = [color=darkgray][font size=10][italic]Please select an # Call status holopad-window-no-calls-in-progress = No holo-calls in progress holopad-window-incoming-call = Incoming holo-call from: +holopad-window-relay-label = Originating at: holopad-window-outgoing-call = Attempting to establish a connection... holopad-window-call-in-progress = Holo-call in progress holopad-window-call-ending = Disconnecting... @@ -28,6 +29,7 @@ holopad-window-access-denied = Access denied holopad-window-select-contact-from-list = Select a contact to initiate a holo-call holopad-window-fetching-contacts-list = No holopads are currently contactable holopad-window-contact-label = {CAPITALIZE($label)} +holopad-window-filter-line-placeholder = Search for a contact # Flavor holopad-window-flavor-left = ⚠ Do not enter while projector is active diff --git a/Resources/Locale/en-US/telephone/telephone.ftl b/Resources/Locale/en-US/telephone/telephone.ftl index 915d54843ff..b1b27768e6e 100644 --- a/Resources/Locale/en-US/telephone/telephone.ftl +++ b/Resources/Locale/en-US/telephone/telephone.ftl @@ -5,4 +5,6 @@ chat-telephone-message-wrap-bold = [color={$color}][bold]{$name}[/bold] {$verb}, # Caller ID chat-telephone-unknown-caller = [color={$color}][font={$fontType} size={$fontSize}][bolditalic]Unknown caller[/bolditalic][/font][/color] chat-telephone-caller-id-with-job = [color={$color}][font={$fontType} size={$fontSize}][bold]{CAPITALIZE($callerName)} ({CAPITALIZE($callerJob)})[/bold][/font][/color] -chat-telephone-caller-id-without-job = [color={$color}][font={$fontType} size={$fontSize}][bold]{CAPITALIZE($callerName)}[/bold][/font][/color] \ No newline at end of file +chat-telephone-caller-id-without-job = [color={$color}][font={$fontType} size={$fontSize}][bold]{CAPITALIZE($callerName)}[/bold][/font][/color] +chat-telephone-unknown-device = [color={$color}][font={$fontType} size={$fontSize}][bolditalic]Unknown device[/bolditalic][/font][/color] +chat-telephone-device-id = [color={$color}][font={$fontType} size={$fontSize}][bold]{CAPITALIZE($deviceName)}[/bold][/font][/color] From ee12c0f9a5c4711d0ef55bd546bd4f1d686e8cb9 Mon Sep 17 00:00:00 2001 From: chromiumboy <50505512+chromiumboy@users.noreply.github.com> Date: Tue, 31 Dec 2024 04:52:08 -0600 Subject: [PATCH 14/64] Holopads no longer log broadcasted speech and emotes in the chat (#34114) Initial commit --- Content.Server/Holopad/HolopadSystem.cs | 7 +++++-- Content.Server/Telephone/TelephoneSystem.cs | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Content.Server/Holopad/HolopadSystem.cs b/Content.Server/Holopad/HolopadSystem.cs index ad531b6d2d7..826a74c48d7 100644 --- a/Content.Server/Holopad/HolopadSystem.cs +++ b/Content.Server/Holopad/HolopadSystem.cs @@ -382,7 +382,10 @@ private void OnEmote(Entity entity, ref EmoteEvent args) if (TryComp(linkedHolopad, out var linkedHolopadTelephone) && linkedHolopadTelephone.Muted) continue; - foreach (var receiver in GetLinkedHolopads(linkedHolopad)) + var receivingHolopads = GetLinkedHolopads(linkedHolopad); + var range = receivingHolopads.Count > 1 ? ChatTransmitRange.HideChat : ChatTransmitRange.GhostRangeLimit; + + foreach (var receiver in receivingHolopads) { if (receiver.Comp.Hologram == null) continue; @@ -392,7 +395,7 @@ private void OnEmote(Entity entity, ref EmoteEvent args) var name = Loc.GetString("holopad-hologram-name", ("name", ent)); // Force the emote, because if the user can do it, the hologram can too - _chatSystem.TryEmoteWithChat(receiver.Comp.Hologram.Value, args.Emote, ChatTransmitRange.Normal, false, name, true, true); + _chatSystem.TryEmoteWithChat(receiver.Comp.Hologram.Value, args.Emote, range, false, name, true, true); } } } diff --git a/Content.Server/Telephone/TelephoneSystem.cs b/Content.Server/Telephone/TelephoneSystem.cs index b8337c766cc..a13ebd16fda 100644 --- a/Content.Server/Telephone/TelephoneSystem.cs +++ b/Content.Server/Telephone/TelephoneSystem.cs @@ -108,8 +108,10 @@ private void OnTelephoneMessageReceived(Entity entity, ref T ("speaker", Name(entity)), ("originalName", nameEv.VoiceName)); + var range = args.TelephoneSource.Comp.LinkedTelephones.Count > 1 ? ChatTransmitRange.HideChat : ChatTransmitRange.GhostRangeLimit; var volume = entity.Comp.SpeakerVolume == TelephoneVolume.Speak ? InGameICChatType.Speak : InGameICChatType.Whisper; - _chat.TrySendInGameICMessage(entity, args.Message, volume, ChatTransmitRange.GhostRangeLimit, nameOverride: name, checkRadioPrefix: false); + + _chat.TrySendInGameICMessage(entity, args.Message, volume, range, nameOverride: name, checkRadioPrefix: false); } #endregion From e2de6443a3274bfd742d6d1b953b34768cc290be Mon Sep 17 00:00:00 2001 From: chromiumboy <50505512+chromiumboy@users.noreply.github.com> Date: Thu, 2 Jan 2025 05:27:18 -0600 Subject: [PATCH 15/64] Fix layout on wires UI (#33714) Layout would break for machines with >6 lights because the column count was hardcoded. Uncap the UI width and fix the rows count instead. Lights with less than 4 characters of text weren't aligned right, now they are. --- Content.Server/Holopad/HolopadSystem.cs | 26 +++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/Content.Server/Holopad/HolopadSystem.cs b/Content.Server/Holopad/HolopadSystem.cs index 826a74c48d7..f3fcc2a9f68 100644 --- a/Content.Server/Holopad/HolopadSystem.cs +++ b/Content.Server/Holopad/HolopadSystem.cs @@ -183,11 +183,15 @@ private void OnHolopadStartBroadcast(Entity source, ref Holopa // AI broadcasting if (TryComp(args.Actor, out var stationAiHeld)) { + // Link the AI to the holopad they are broadcasting from + LinkHolopadToUser(source, args.Actor); + if (!_stationAiSystem.TryGetStationAiCore((args.Actor, stationAiHeld), out var stationAiCore) || stationAiCore.Value.Comp.RemoteEntity == null || !TryComp(stationAiCore, out var stationAiCoreHolopad)) return; + // Execute the broadcast, but have it originate from the AI core ExecuteBroadcast((stationAiCore.Value, stationAiCoreHolopad), args.Actor); // Switch the AI's perspective from free roaming to the target holopad @@ -612,11 +616,25 @@ private void ShutDownHolopad(Entity entity) DeleteHologram(entity.Comp.Hologram.Value, entity); if (entity.Comp.User != null) - UnlinkHolopadFromUser(entity, entity.Comp.User.Value); - - if (TryComp(entity, out var stationAiCore)) { - _stationAiSystem.SwitchRemoteEntityMode((entity.Owner, stationAiCore), true); + // Check if the associated holopad user is an AI + if (TryComp(entity.Comp.User, out var stationAiHeld) && + _stationAiSystem.TryGetStationAiCore((entity.Comp.User.Value, stationAiHeld), out var stationAiCore)) + { + // Return the AI eye to free roaming + _stationAiSystem.SwitchRemoteEntityMode(stationAiCore.Value, true); + + // If the AI core is still broadcasting, end its calls + if (entity.Owner != stationAiCore.Value.Owner && + TryComp(stationAiCore, out var stationAiCoreTelephone) && + _telephoneSystem.IsTelephoneEngaged((stationAiCore.Value.Owner, stationAiCoreTelephone))) + { + _telephoneSystem.EndTelephoneCalls((stationAiCore.Value.Owner, stationAiCoreTelephone)); + } + } + + UnlinkHolopadFromUser(entity, entity.Comp.User.Value); + } if (TryComp(entity, out var stationAiCoreTelphone) && stationAiCoreTelphone.CurrentState != TelephoneState.EndingCall && stationAiCoreTelphone.CurrentState != TelephoneState.Idle) From 2f763f711018c4eab8cdb7babdb9ac93f5689bf9 Mon Sep 17 00:00:00 2001 From: Kanashi-Panda Date: Mon, 16 Sep 2024 17:35:57 -0700 Subject: [PATCH 16/64] Crayons buffed to 45 uses (#32061) * Buffs crayons to 45 uses * Update Resources/Prototypes/Entities/Objects/Fun/crayons.yml Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Resources/Prototypes/Entities/Objects/Fun/crayons.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Objects/Fun/crayons.yml b/Resources/Prototypes/Entities/Objects/Fun/crayons.yml index 4aab2efdb97..9db8e6d2aa1 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/crayons.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/crayons.yml @@ -21,7 +21,7 @@ enum.CrayonUiKey.Key: type: CrayonBoundUserInterface - type: Crayon - capacity: 15 + capacity: 25 - type: Food - type: SolutionContainerManager solutions: From 882ce26862892655078ea2e85d56f90d3777d05c Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Mon, 22 Jul 2024 19:22:59 -0700 Subject: [PATCH 17/64] fix crayon (#30263) Co-authored-by: plykiya --- Content.Client/Crayon/UI/CrayonWindow.xaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Client/Crayon/UI/CrayonWindow.xaml.cs b/Content.Client/Crayon/UI/CrayonWindow.xaml.cs index b97786cd41a..6ef282d219a 100644 --- a/Content.Client/Crayon/UI/CrayonWindow.xaml.cs +++ b/Content.Client/Crayon/UI/CrayonWindow.xaml.cs @@ -89,6 +89,7 @@ private void ButtonOnPressed(ButtonEventArgs obj) if (obj.Button.Name == null) return; _selected = obj.Button.Name; + OnSelected?.Invoke(_selected); RefreshList(); } From 26ffe940b56e98d7d2ebce84da343245bf998aad Mon Sep 17 00:00:00 2001 From: Saphire Lattice Date: Sat, 16 Nov 2024 10:25:06 +0700 Subject: [PATCH 18/64] Improve crayon UI to not be stuck in 1996 (#33101) * Improve crayon UI to not be stuck in 1996 * Make a horrifying crayon spaghetti * Crayon * Undeprecate the crayon, describe the crayon --- .../Crayon/UI/CrayonBoundUserInterface.cs | 12 +- Content.Client/Crayon/UI/CrayonWindow.xaml | 11 +- Content.Client/Crayon/UI/CrayonWindow.xaml.cs | 138 ++++- Content.Server/Crayon/CrayonSystem.cs | 2 + .../Crayon/SharedCrayonComponent.cs | 44 +- .../Locale/en-US/crayon/crayon-component.ftl | 7 + Resources/Prototypes/Decals/crayons.yml | 552 +++++++++--------- 7 files changed, 449 insertions(+), 317 deletions(-) diff --git a/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs b/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs index e5be0b1811f..44501767dd4 100644 --- a/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs +++ b/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs @@ -31,7 +31,7 @@ protected override void Open() private void PopulateCrayons() { var crayonDecals = _protoManager.EnumeratePrototypes().Where(x => x.Tags.Contains("crayon")); - _menu?.Populate(crayonDecals); + _menu?.Populate(crayonDecals.ToList()); } public override void OnProtoReload(PrototypesReloadedEventArgs args) @@ -44,6 +44,16 @@ public override void OnProtoReload(PrototypesReloadedEventArgs args) PopulateCrayons(); } + protected override void ReceiveMessage(BoundUserInterfaceMessage message) + { + base.ReceiveMessage(message); + + if (_menu is null || message is not CrayonUsedMessage crayonMessage) + return; + + _menu.AdvanceState(crayonMessage.DrawnDecal); + } + protected override void UpdateState(BoundUserInterfaceState state) { base.UpdateState(state); diff --git a/Content.Client/Crayon/UI/CrayonWindow.xaml b/Content.Client/Crayon/UI/CrayonWindow.xaml index 7729318ae7f..7acb22551b7 100644 --- a/Content.Client/Crayon/UI/CrayonWindow.xaml +++ b/Content.Client/Crayon/UI/CrayonWindow.xaml @@ -1,14 +1,13 @@  + MinSize="450 500" + SetSize="450 500"> - + - - - + + diff --git a/Content.Client/Crayon/UI/CrayonWindow.xaml.cs b/Content.Client/Crayon/UI/CrayonWindow.xaml.cs index 6ef282d219a..88475562c67 100644 --- a/Content.Client/Crayon/UI/CrayonWindow.xaml.cs +++ b/Content.Client/Crayon/UI/CrayonWindow.xaml.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; +using System.Linq; using Content.Client.Stylesheets; using Content.Shared.Crayon; using Content.Shared.Decals; using Robust.Client.AutoGenerated; +using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; @@ -18,7 +20,12 @@ namespace Content.Client.Crayon.UI [GenerateTypedNameReferences] public sealed partial class CrayonWindow : DefaultWindow { - private Dictionary? _decals; + [Dependency] private readonly IEntitySystemManager _entitySystem = default!; + private readonly SpriteSystem _spriteSystem = default!; + + private Dictionary>? _decals; + private List? _allDecals; + private string? _autoSelected; private string? _selected; private Color _color; @@ -28,8 +35,10 @@ public sealed partial class CrayonWindow : DefaultWindow public CrayonWindow() { RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + _spriteSystem = _entitySystem.GetEntitySystem(); - Search.OnTextChanged += _ => RefreshList(); + Search.OnTextChanged += SearchChanged; ColorSelector.OnColorChanged += SelectColor; } @@ -44,51 +53,94 @@ private void SelectColor(Color color) private void RefreshList() { // Clear - Grid.DisposeAllChildren(); - if (_decals == null) + Grids.DisposeAllChildren(); + + if (_decals == null || _allDecals == null) return; var filter = Search.Text; - foreach (var (decal, tex) in _decals) + var comma = filter.IndexOf(','); + var first = (comma == -1 ? filter : filter[..comma]).Trim(); + + var names = _decals.Keys.ToList(); + names.Sort((a, b) => a == "random" ? 1 : b == "random" ? -1 : a.CompareTo(b)); + + if (_autoSelected != null && first != _autoSelected && _allDecals.Contains(first)) + { + _selected = first; + _autoSelected = _selected; + OnSelected?.Invoke(_selected); + } + + foreach (var categoryName in names) { - if (!decal.Contains(filter)) + var locName = Loc.GetString("crayon-category-" + categoryName); + var category = _decals[categoryName].Where(d => locName.Contains(first) || d.Name.Contains(first)).ToList(); + + if (category.Count == 0) continue; - var button = new TextureButton() + var label = new Label { - TextureNormal = tex, - Name = decal, - ToolTip = decal, - Modulate = _color, + Text = locName }; - button.OnPressed += ButtonOnPressed; - if (_selected == decal) + + var grid = new GridContainer { - var panelContainer = new PanelContainer() + Columns = 6, + Margin = new Thickness(0, 0, 0, 16) + }; + + Grids.AddChild(label); + Grids.AddChild(grid); + + foreach (var (name, texture) in category) + { + var button = new TextureButton() { - PanelOverride = new StyleBoxFlat() - { - BackgroundColor = StyleNano.ButtonColorDefault, - }, - Children = - { - button, - }, + TextureNormal = texture, + Name = name, + ToolTip = name, + Modulate = _color, + Scale = new System.Numerics.Vector2(2, 2) }; - Grid.AddChild(panelContainer); - } - else - { - Grid.AddChild(button); + button.OnPressed += ButtonOnPressed; + + if (_selected == name) + { + var panelContainer = new PanelContainer() + { + PanelOverride = new StyleBoxFlat() + { + BackgroundColor = StyleNano.ButtonColorDefault, + }, + Children = + { + button, + }, + }; + grid.AddChild(panelContainer); + } + else + { + grid.AddChild(button); + } } } } + private void SearchChanged(LineEdit.LineEditEventArgs obj) + { + _autoSelected = ""; // Placeholder to kick off the auto-select in refreshlist() + RefreshList(); + } + private void ButtonOnPressed(ButtonEventArgs obj) { if (obj.Button.Name == null) return; _selected = obj.Button.Name; + _autoSelected = null; OnSelected?.Invoke(_selected); RefreshList(); } @@ -107,12 +159,38 @@ public void UpdateState(CrayonBoundUserInterfaceState state) RefreshList(); } - public void Populate(IEnumerable prototypes) + public void AdvanceState(string drawnDecal) { - _decals = new Dictionary(); + var filter = Search.Text; + if (!filter.Contains(',') || !filter.Contains(drawnDecal)) + return; + + var first = filter[..filter.IndexOf(',')].Trim(); + + if (first.Equals(drawnDecal, StringComparison.InvariantCultureIgnoreCase)) + { + Search.Text = filter[(filter.IndexOf(',') + 1)..].Trim(); + _autoSelected = first; + } + + RefreshList(); + } + + public void Populate(List prototypes) + { + _decals = []; + _allDecals = []; + + prototypes.Sort((a, b) => a.ID.CompareTo(b.ID)); + foreach (var decalPrototype in prototypes) { - _decals.Add(decalPrototype.ID, decalPrototype.Sprite.Frame0()); + var category = "random"; + if (decalPrototype.Tags.Count > 1 && decalPrototype.Tags[1].StartsWith("crayon-")) + category = decalPrototype.Tags[1].Replace("crayon-", ""); + var list = _decals.GetOrNew(category); + list.Add((decalPrototype.ID, _spriteSystem.Frame0(decalPrototype.Sprite))); + _allDecals.Add(decalPrototype.ID); } RefreshList(); diff --git a/Content.Server/Crayon/CrayonSystem.cs b/Content.Server/Crayon/CrayonSystem.cs index 07a13d8a34a..4257c436c23 100644 --- a/Content.Server/Crayon/CrayonSystem.cs +++ b/Content.Server/Crayon/CrayonSystem.cs @@ -82,6 +82,8 @@ private void OnCrayonAfterInteract(EntityUid uid, CrayonComponent component, Aft if (component.DeleteEmpty && component.Charges <= 0) UseUpCrayon(uid, args.User); + else + _uiSystem.ServerSendUiMessage(uid, SharedCrayonComponent.CrayonUiKey.Key, new CrayonUsedMessage(component.SelectedState)); } private void OnCrayonUse(EntityUid uid, CrayonComponent component, UseInHandEvent args) diff --git a/Content.Shared/Crayon/SharedCrayonComponent.cs b/Content.Shared/Crayon/SharedCrayonComponent.cs index f8e88b218de..a9c21988ea6 100644 --- a/Content.Shared/Crayon/SharedCrayonComponent.cs +++ b/Content.Shared/Crayon/SharedCrayonComponent.cs @@ -3,12 +3,23 @@ namespace Content.Shared.Crayon { + + /// + /// Component holding the state of a crayon-like component + /// [NetworkedComponent, ComponentProtoName("Crayon"), Access(typeof(SharedCrayonSystem))] public abstract partial class SharedCrayonComponent : Component { + /// + /// The ID of currently selected decal prototype that will be placed when the crayon is used + /// public string SelectedState { get; set; } = string.Empty; - [DataField("color")] public Color Color; + /// + /// Color with which the crayon will draw + /// + [DataField("color")] + public Color Color; [Serializable, NetSerializable] public enum CrayonUiKey : byte @@ -17,6 +28,9 @@ public enum CrayonUiKey : byte } } + /// + /// Used by the client to notify the server about the selected decal ID + /// [Serializable, NetSerializable] public sealed class CrayonSelectMessage : BoundUserInterfaceMessage { @@ -27,6 +41,9 @@ public CrayonSelectMessage(string selected) } } + /// + /// Sets the color of the crayon, used by Rainbow Crayon + /// [Serializable, NetSerializable] public sealed class CrayonColorMessage : BoundUserInterfaceMessage { @@ -37,13 +54,25 @@ public CrayonColorMessage(Color color) } } + /// + /// Server to CLIENT. Notifies the BUI that a decal with given ID has been drawn. + /// Allows the client UI to advance forward in the client-only ephemeral queue, + /// preventing the crayon from becoming a magic text storage device. + /// [Serializable, NetSerializable] - public enum CrayonVisuals + public sealed class CrayonUsedMessage : BoundUserInterfaceMessage { - State, - Color + public readonly string DrawnDecal; + + public CrayonUsedMessage(string drawn) + { + DrawnDecal = drawn; + } } + /// + /// Component state, describes how many charges are left in the crayon in the near-hand UI + /// [Serializable, NetSerializable] public sealed class CrayonComponentState : ComponentState { @@ -60,10 +89,17 @@ public CrayonComponentState(Color color, string state, int charges, int capacity Capacity = capacity; } } + + /// + /// The state of the crayon UI as sent by the server + /// [Serializable, NetSerializable] public sealed class CrayonBoundUserInterfaceState : BoundUserInterfaceState { public string Selected; + /// + /// Whether or not the color can be selected + /// public bool SelectableColor; public Color Color; diff --git a/Resources/Locale/en-US/crayon/crayon-component.ftl b/Resources/Locale/en-US/crayon/crayon-component.ftl index 444ffa4c45e..e13bf76941c 100644 --- a/Resources/Locale/en-US/crayon/crayon-component.ftl +++ b/Resources/Locale/en-US/crayon/crayon-component.ftl @@ -8,3 +8,10 @@ crayon-interact-invalid-location = Can't reach there! ## UI crayon-window-title = Crayon +crayon-window-placeholder = Search, or queue a comma-separated list of names +crayon-category-1-brushes = Brushes +crayon-category-2-alphanum = Numbers and letters +crayon-category-3-symbols = Symbols +crayon-category-4-info = Signs +crayon-category-5-graffiti = Graffiti +crayon-category-random = Random diff --git a/Resources/Prototypes/Decals/crayons.yml b/Resources/Prototypes/Decals/crayons.yml index 3be2ec24261..a15619d483c 100644 --- a/Resources/Prototypes/Decals/crayons.yml +++ b/Resources/Prototypes/Decals/crayons.yml @@ -1,6 +1,6 @@ - type: decal id: 0 - tags: ["crayon"] + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -10,7 +10,7 @@ - type: decal id: 1 - tags: ["crayon"] + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -20,7 +20,7 @@ - type: decal id: 2 - tags: ["crayon"] + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -30,7 +30,7 @@ - type: decal id: 3 - tags: ["crayon"] + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -40,7 +40,7 @@ - type: decal id: 4 - tags: ["crayon"] + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -50,7 +50,7 @@ - type: decal id: 5 - tags: ["crayon"] + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -60,7 +60,7 @@ - type: decal id: 6 - tags: ["crayon"] + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -70,7 +70,7 @@ - type: decal id: 7 - tags: ["crayon"] + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -80,7 +80,7 @@ - type: decal id: 8 - tags: ["crayon"] + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -90,7 +90,7 @@ - type: decal id: 9 - tags: ["crayon"] + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -100,7 +100,7 @@ - type: decal id: Blasto - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -110,7 +110,7 @@ - type: decal id: Clandestine - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -120,7 +120,7 @@ - type: decal id: Cyber - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -130,7 +130,7 @@ - type: decal id: Diablo - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -140,7 +140,7 @@ - type: decal id: Donk - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -150,7 +150,7 @@ - type: decal id: Gene - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -160,7 +160,7 @@ - type: decal id: Gib - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -170,7 +170,7 @@ - type: decal id: Max - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -180,7 +180,7 @@ - type: decal id: Newton - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -190,7 +190,7 @@ - type: decal id: North - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -200,7 +200,7 @@ - type: decal id: Omni - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -210,7 +210,7 @@ - type: decal id: Osiron - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -220,7 +220,7 @@ - type: decal id: Prima - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -230,7 +230,7 @@ - type: decal id: Psyke - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -240,7 +240,7 @@ - type: decal id: Sirius - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -250,7 +250,7 @@ - type: decal id: Tunnel - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -260,7 +260,7 @@ - type: decal id: Waffle - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -268,19 +268,9 @@ sprite: Effects/crayondecals.rsi state: Waffle -- type: decal - id: a - tags: ["crayon"] - defaultCleanable: true - defaultCustomColor: true - defaultSnap: false - sprite: - sprite: Effects/crayondecals.rsi - state: a - - type: decal id: ampersand - tags: ["crayon"] + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -290,7 +280,7 @@ - type: decal id: amyjon - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -300,7 +290,7 @@ - type: decal id: arrow - tags: ["crayon"] + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -308,16 +298,6 @@ sprite: Effects/crayondecals.rsi state: arrow -- type: decal - id: b - tags: ["crayon"] - defaultCleanable: true - defaultCustomColor: true - defaultSnap: false - sprite: - sprite: Effects/crayondecals.rsi - state: b - - type: decal id: beepsky tags: ["crayon"] @@ -330,7 +310,7 @@ - type: decal id: biohazard - tags: ["crayon"] + tags: ["crayon", "crayon-4-info"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -370,7 +350,7 @@ - type: decal id: brush - tags: ["crayon"] + tags: ["crayon", "crayon-1-brushes"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -378,16 +358,6 @@ sprite: Effects/crayondecals.rsi state: brush -- type: decal - id: c - tags: ["crayon"] - defaultCleanable: true - defaultCustomColor: true - defaultSnap: false - sprite: - sprite: Effects/crayondecals.rsi - state: c - - type: decal id: carp tags: ["crayon"] @@ -410,7 +380,7 @@ - type: decal id: chevron - tags: ["crayon"] + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -440,7 +410,7 @@ - type: decal id: comma - tags: ["crayon"] + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -460,7 +430,7 @@ - type: decal id: credit - tags: ["crayon"] + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -470,7 +440,7 @@ - type: decal id: cyka - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -478,19 +448,9 @@ sprite: Effects/crayondecals.rsi state: cyka -- type: decal - id: d - tags: ["crayon"] - defaultCleanable: true - defaultCustomColor: true - defaultSnap: false - sprite: - sprite: Effects/crayondecals.rsi - state: d - - type: decal id: danger - tags: ["crayon"] + tags: ["crayon", "crayon-4-info"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -510,7 +470,7 @@ - type: decal id: dot - tags: ["crayon"] + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -528,19 +488,9 @@ sprite: Effects/crayondecals.rsi state: dwarf -- type: decal - id: e - tags: ["crayon"] - defaultCleanable: true - defaultCustomColor: true - defaultSnap: false - sprite: - sprite: Effects/crayondecals.rsi - state: e - - type: decal id: electricdanger - tags: ["crayon"] + tags: ["crayon", "crayon-4-info"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -550,7 +500,7 @@ - type: decal id: end - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -560,7 +510,7 @@ - type: decal id: engie - tags: ["crayon"] + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -570,7 +520,7 @@ - type: decal id: equals - tags: ["crayon"] + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -580,7 +530,7 @@ - type: decal id: evac - tags: ["crayon"] + tags: ["crayon", "crayon-4-info"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -590,7 +540,7 @@ - type: decal id: exclamationmark - tags: ["crayon"] + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -598,16 +548,6 @@ sprite: Effects/crayondecals.rsi state: exclamationmark -- type: decal - id: f - tags: ["crayon"] - defaultCleanable: true - defaultCustomColor: true - defaultSnap: false - sprite: - sprite: Effects/crayondecals.rsi - state: f - - type: decal id: face tags: ["crayon"] @@ -630,7 +570,7 @@ - type: decal id: firedanger - tags: ["crayon"] + tags: ["crayon", "crayon-4-info"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -640,7 +580,7 @@ - type: decal id: food - tags: ["crayon"] + tags: ["crayon", "crayon-4-info"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -659,598 +599,658 @@ state: footprint - type: decal - id: g + id: ghost tags: ["crayon"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: g + state: ghost - type: decal - id: ghost + id: guy tags: ["crayon"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: ghost + state: guy - type: decal - id: guy - tags: ["crayon"] + id: heart + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: guy + state: heart - type: decal - id: h - tags: ["crayon"] + id: largebrush + tags: ["crayon", "crayon-1-brushes"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: h + state: largebrush - type: decal - id: heart - tags: ["crayon"] + id: like + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: heart + state: like - type: decal - id: i - tags: ["crayon"] + id: line + tags: ["crayon", "crayon-1-brushes"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: i + state: line - type: decal - id: j - tags: ["crayon"] + id: matt + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: j + state: matt - type: decal - id: k - tags: ["crayon"] + id: med + tags: ["crayon", "crayon-4-info"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: k + state: med - type: decal - id: l - tags: ["crayon"] + id: minus + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: l + state: minus - type: decal - id: largebrush - tags: ["crayon"] + id: nay + tags: ["crayon", "crayon-4-info"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: largebrush + state: nay - type: decal - id: like + id: pawprint tags: ["crayon"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: like + state: pawprint - type: decal - id: line - tags: ["crayon"] + id: peace + tags: ["crayon", "crayon-4-info"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: line + state: peace - type: decal - id: m - tags: ["crayon"] + id: percent + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: m + state: percent - type: decal - id: matt - tags: ["crayon"] + id: plus + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: matt + state: plus - type: decal - id: med - tags: ["crayon"] + id: pound + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: med + state: pound - type: decal - id: minus - tags: ["crayon"] + id: prolizard + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: minus + state: prolizard - type: decal - id: n - tags: ["crayon"] + id: questionmark + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: n + state: questionmark - type: decal - id: nay - tags: ["crayon"] + id: radiation + tags: ["crayon", "crayon-4-info"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: nay + state: radiation - type: decal - id: o - tags: ["crayon"] + id: revolution + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: o + state: revolution - type: decal - id: p - tags: ["crayon"] + id: rune1 + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: p + state: rune1 - type: decal - id: pawprint - tags: ["crayon"] + id: rune2 + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: pawprint + state: rune2 - type: decal - id: peace - tags: ["crayon"] + id: rune3 + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: peace + state: rune3 - type: decal - id: percent - tags: ["crayon"] + id: rune4 + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: percent + state: rune4 - type: decal - id: plus - tags: ["crayon"] + id: rune5 + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: plus + state: rune5 - type: decal - id: pound - tags: ["crayon"] + id: rune6 + tags: ["crayon", "crayon-5-graffiti"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: pound + state: rune6 - type: decal - id: prolizard - tags: ["crayon"] + id: safe + tags: ["crayon", "crayon-4-info"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: prolizard + state: safe - type: decal - id: q + id: scroll tags: ["crayon"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: q + state: scroll - type: decal - id: questionmark - tags: ["crayon"] + id: shop + tags: ["crayon", "crayon-4-info"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: questionmark + state: shop - type: decal - id: r - tags: ["crayon"] + id: shortline + tags: ["crayon", "crayon-1-brushes"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: r + state: shortline - type: decal - id: radiation + id: shotgun tags: ["crayon"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: radiation + state: shotgun - type: decal - id: revolution - tags: ["crayon"] + id: skull + tags: ["crayon", "crayon-4-info"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: revolution + state: skull - type: decal - id: rune1 - tags: ["crayon"] + id: slash + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: rune1 + state: slash - type: decal - id: rune2 - tags: ["crayon"] + id: smallbrush + tags: ["crayon", "crayon-1-brushes"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: rune2 + state: smallbrush - type: decal - id: rune3 + id: snake tags: ["crayon"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: rune3 + state: snake - type: decal - id: rune4 - tags: ["crayon"] + id: space + tags: ["crayon", "crayon-4-info"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: rune4 + state: space - type: decal - id: rune5 + id: splatter tags: ["crayon"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: rune5 + state: splatter - type: decal - id: rune6 - tags: ["crayon"] + id: star + tags: ["crayon", "crayon-3-symbols"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: rune6 + state: star - type: decal - id: s + id: stickman tags: ["crayon"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: s + state: stickman - type: decal - id: safe + id: taser tags: ["crayon"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: safe + state: taser - type: decal - id: scroll - tags: ["crayon"] + id: thinline + tags: ["crayon", "crayon-1-brushes"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: scroll + state: thinline - type: decal - id: shop + id: toilet tags: ["crayon"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: shop + state: toilet - type: decal - id: shortline + id: toolbox tags: ["crayon"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: shortline + state: toolbox - type: decal - id: shotgun - tags: ["crayon"] + id: trade + tags: ["crayon", "crayon-4-info"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: shotgun + state: trade - type: decal - id: skull + id: uboa tags: ["crayon"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: skull + state: uboa - type: decal - id: slash - tags: ["crayon"] + id: a + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: slash + state: a - type: decal - id: smallbrush - tags: ["crayon"] + id: b + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: smallbrush + state: b - type: decal - id: snake - tags: ["crayon"] + id: c + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: snake + state: c - type: decal - id: space - tags: ["crayon"] + id: d + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: space + state: d - type: decal - id: splatter - tags: ["crayon"] + id: e + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: splatter + state: e - type: decal - id: star - tags: ["crayon"] + id: f + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: star + state: f - type: decal - id: stickman - tags: ["crayon"] + id: g + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: stickman + state: g - type: decal - id: t - tags: ["crayon"] + id: h + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: t + state: h - type: decal - id: taser - tags: ["crayon"] + id: i + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: taser + state: i - type: decal - id: thinline - tags: ["crayon"] + id: j + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: thinline + state: j - type: decal - id: toilet - tags: ["crayon"] + id: k + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: toilet + state: k - type: decal - id: toolbox - tags: ["crayon"] + id: l + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: toolbox + state: l - type: decal - id: trade - tags: ["crayon"] + id: m + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: trade + state: m - type: decal - id: u - tags: ["crayon"] + id: n + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: u + state: n - type: decal - id: uboa - tags: ["crayon"] + id: o + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false sprite: sprite: Effects/crayondecals.rsi - state: uboa + state: o + +- type: decal + id: p + tags: ["crayon", "crayon-2-alphanum"] + defaultCleanable: true + defaultCustomColor: true + defaultSnap: false + sprite: + sprite: Effects/crayondecals.rsi + state: p + +- type: decal + id: q + tags: ["crayon", "crayon-2-alphanum"] + defaultCleanable: true + defaultCustomColor: true + defaultSnap: false + sprite: + sprite: Effects/crayondecals.rsi + state: q + +- type: decal + id: r + tags: ["crayon", "crayon-2-alphanum"] + defaultCleanable: true + defaultCustomColor: true + defaultSnap: false + sprite: + sprite: Effects/crayondecals.rsi + state: r + +- type: decal + id: s + tags: ["crayon", "crayon-2-alphanum"] + defaultCleanable: true + defaultCustomColor: true + defaultSnap: false + sprite: + sprite: Effects/crayondecals.rsi + state: s + +- type: decal + id: t + tags: ["crayon", "crayon-2-alphanum"] + defaultCleanable: true + defaultCustomColor: true + defaultSnap: false + sprite: + sprite: Effects/crayondecals.rsi + state: t + +- type: decal + id: u + tags: ["crayon", "crayon-2-alphanum"] + defaultCleanable: true + defaultCustomColor: true + defaultSnap: false + sprite: + sprite: Effects/crayondecals.rsi + state: u - type: decal id: v - tags: ["crayon"] + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -1260,7 +1260,7 @@ - type: decal id: w - tags: ["crayon"] + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -1270,7 +1270,7 @@ - type: decal id: x - tags: ["crayon"] + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -1280,7 +1280,7 @@ - type: decal id: y - tags: ["crayon"] + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false @@ -1290,7 +1290,7 @@ - type: decal id: z - tags: ["crayon"] + tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true defaultSnap: false From 4f863f05e13e89dfbb50b5a20751eff1af0946aa Mon Sep 17 00:00:00 2001 From: Ilya246 <57039557+Ilya246@users.noreply.github.com> Date: Mon, 18 Sep 2023 10:13:12 +0400 Subject: [PATCH 19/64] fix bloodred boots (#20309) --- .../Entities/Clothing/Shoes/magboots.yml | 47 ++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml index bb2b163f080..f2789866745 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml @@ -1,8 +1,9 @@ - type: entity parent: [ClothingShoesBase, BaseToggleClothing] - id: ClothingShoesBootsMag + id: ClothingShoesBootsMagBase name: magboots description: Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle. + abstract: true components: - type: Sprite sprite: Clothing/Shoes/Boots/magboots.rsi @@ -13,8 +14,7 @@ sprite: Clothing/Shoes/Boots/magboots.rsi - type: ToggleClothing action: ActionToggleMagboots - - type: ToggleVerb - text: toggle-magboots-verb-get-data-text + mustEquip: false - type: ComponentToggler components: - type: NoSlip @@ -35,6 +35,10 @@ tags: - WhitelistChameleon +- type: entity + parent: [ClothingShoesBootsMagBase] + id: ClothingShoesBootsMag + - type: entity parent: ClothingShoesBootsMag id: ClothingShoesBootsMagAdv @@ -85,7 +89,7 @@ price: 3000 - type: entity - parent: ClothingShoesBase + parent: [ClothingShoesBootsMagBase, BaseJetpack] id: ClothingShoesBootsMagSyndie name: blood-red magboots description: Reverse-engineered magnetic boots that have a heavy magnetic pull and integrated thrusters. @@ -105,12 +109,43 @@ volume: 0.75 temperature: 293.15 moles: - - 0.153853429 # oxygen - - 0.153853429 # nitrogen + - 0.153853429 # oxygen + - 0.153853429 # nitrogen + - type: ActivatableUI + key: enum.SharedGasTankUiKey.Key + - type: UserInterface + interfaces: + - key: enum.SharedGasTankUiKey.Key + type: GasTankBoundUserInterface + - type: Explosive + explosionType: Default + maxIntensity: 20 + - type: Jetpack + moleUsage: 0.00085 + - type: CanMoveInAir + - type: InputMover + toParent: true + - type: MovementSpeedModifier + weightlessAcceleration: 1 + weightlessFriction: 0.3 + weightlessModifier: 1.2 + - type: Tag + tags: + - WhitelistChameleon - type: Item sprite: null size: Normal +- type: entity + id: ActionBaseToggleMagboots + name: Toggle Magboots + description: Toggles the magboots on and off. + noSpawn: true + components: + - type: InstantAction + itemIconStyle: NoItem + event: !type:ToggleMagbootsEvent + - type: entity id: ActionToggleMagboots name: Toggle Magboots From a8537eb024e374c15e86b52a7733d54c70342976 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Thu, 16 Jan 2025 19:07:15 -0400 Subject: [PATCH 20/64] Fix all prisoner/AI/borg spawnpoint issues --- .../ContainerSpawnPointSystem.cs | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs b/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs index bc37d93dec5..fc96b8170f1 100644 --- a/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs +++ b/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs @@ -12,17 +12,17 @@ namespace Content.Server.Spawners.EntitySystems; public sealed class ContainerSpawnPointSystem : EntitySystem { + [Dependency] private readonly ContainerSystem _container = default!; [Dependency] private readonly GameTicker _gameTicker = default!; - [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly ContainerSystem _container = default!; + [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(HandlePlayerSpawning, before: [ typeof(SpawnPointSystem) ]); + SubscribeLocalEvent(HandlePlayerSpawning, before: new []{ typeof(SpawnPointSystem) }); } public void HandlePlayerSpawning(PlayerSpawningEvent args) @@ -30,14 +30,13 @@ public void HandlePlayerSpawning(PlayerSpawningEvent args) if (args.SpawnResult != null) return; - JobPrototype? jobProto = null; + // DeltaV - Ignore these two desired spawn types + if (args.DesiredSpawnPointType is SpawnPointType.Observer or SpawnPointType.LateJoin) + return; // If it's just a spawn pref check if it's for cryo (silly). if (args.HumanoidCharacterProfile?.SpawnPriority != SpawnPriorityPreference.Cryosleep && - (!_proto.TryIndex(args.Job?.Prototype, out jobProto) || jobProto.JobEntity == null)) - return; - - if (jobProto == null && !_proto.TryIndex(args.Job?.Prototype, out jobProto)) + (!_proto.TryIndex(args.Job?.Prototype, out var jobProto) || jobProto.JobEntity == null)) return; var query = EntityQueryEnumerator(); @@ -49,12 +48,11 @@ public void HandlePlayerSpawning(PlayerSpawningEvent args) continue; // DeltaV - Custom override for override spawnpoints, only used for prisoners currently. This shouldn't run for any other jobs - if (args.DesiredSpawnPointType == SpawnPointType.Job - && spawnPoint.SpawnType == SpawnPointType.Job - && args.Job is not null - && spawnPoint.Job is not "" - && spawnPoint.Job == args.Job.Prototype) + if (args.DesiredSpawnPointType == SpawnPointType.Job) { + if (spawnPoint.SpawnType != SpawnPointType.Job || spawnPoint.Job != args.Job?.Prototype) + continue; + possibleContainers.Add((uid, spawnPoint, container, xform)); continue; } @@ -68,9 +66,7 @@ public void HandlePlayerSpawning(PlayerSpawningEvent args) continue; } - if (_gameTicker.RunLevel == GameRunLevel.InRound - && spawnPoint.SpawnType == SpawnPointType.LateJoin - && jobProto.JobEntity == null) + if (_gameTicker.RunLevel == GameRunLevel.InRound && spawnPoint.SpawnType == SpawnPointType.LateJoin) possibleContainers.Add((uid, spawnPoint, container, xform)); if (_gameTicker.RunLevel != GameRunLevel.InRound && From 3f0f797fa50b346eeea2793b4619a3669ab7b06d Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Thu, 16 Jan 2025 19:14:11 -0400 Subject: [PATCH 21/64] Sort crayons properly. --- Resources/Prototypes/Decals/crayons.yml | 72 ++++++++++++------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/Resources/Prototypes/Decals/crayons.yml b/Resources/Prototypes/Decals/crayons.yml index a15619d483c..91f942e6800 100644 --- a/Resources/Prototypes/Decals/crayons.yml +++ b/Resources/Prototypes/Decals/crayons.yml @@ -1,5 +1,5 @@ - type: decal - id: 0 + id: alphanumeric_0 tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -9,7 +9,7 @@ state: 0 - type: decal - id: 1 + id: alphanumeric_1 tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -19,7 +19,7 @@ state: 1 - type: decal - id: 2 + id: alphanumeric_2 tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -29,7 +29,7 @@ state: 2 - type: decal - id: 3 + id: alphanumeric_3 tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -39,7 +39,7 @@ state: 3 - type: decal - id: 4 + id: alphanumeric_4 tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -49,7 +49,7 @@ state: 4 - type: decal - id: 5 + id: alphanumeric_5 tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -59,7 +59,7 @@ state: 5 - type: decal - id: 6 + id: alphanumeric_6 tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -69,7 +69,7 @@ state: 6 - type: decal - id: 7 + id: alphanumeric_7 tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -79,7 +79,7 @@ state: 7 - type: decal - id: 8 + id: alphanumeric_8 tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -89,7 +89,7 @@ state: 8 - type: decal - id: 9 + id: alphanumeric_9 tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1039,7 +1039,7 @@ state: uboa - type: decal - id: a + id: alphanumeric_a tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1049,7 +1049,7 @@ state: a - type: decal - id: b + id: alphanumeric_b tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1059,7 +1059,7 @@ state: b - type: decal - id: c + id: alphanumeric_c tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1069,7 +1069,7 @@ state: c - type: decal - id: d + id: alphanumeric_d tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1079,7 +1079,7 @@ state: d - type: decal - id: e + id: alphanumeric_e tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1089,7 +1089,7 @@ state: e - type: decal - id: f + id: alphanumeric_f tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1099,7 +1099,7 @@ state: f - type: decal - id: g + id: alphanumeric_g tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1109,7 +1109,7 @@ state: g - type: decal - id: h + id: alphanumeric_h tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1119,7 +1119,7 @@ state: h - type: decal - id: i + id: alphanumeric_i tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1129,7 +1129,7 @@ state: i - type: decal - id: j + id: alphanumeric_j tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1139,7 +1139,7 @@ state: j - type: decal - id: k + id: alphanumeric_k tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1149,7 +1149,7 @@ state: k - type: decal - id: l + id: alphanumeric_l tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1159,7 +1159,7 @@ state: l - type: decal - id: m + id: alphanumeric_m tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1169,7 +1169,7 @@ state: m - type: decal - id: n + id: alphanumeric_n tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1179,7 +1179,7 @@ state: n - type: decal - id: o + id: alphanumeric_o tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1189,7 +1189,7 @@ state: o - type: decal - id: p + id: alphanumeric_p tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1199,7 +1199,7 @@ state: p - type: decal - id: q + id: alphanumeric_q tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1209,7 +1209,7 @@ state: q - type: decal - id: r + id: alphanumeric_r tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1219,7 +1219,7 @@ state: r - type: decal - id: s + id: alphanumeric_s tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1229,7 +1229,7 @@ state: s - type: decal - id: t + id: alphanumeric_t tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1239,7 +1239,7 @@ state: t - type: decal - id: u + id: alphanumeric_u tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1249,7 +1249,7 @@ state: u - type: decal - id: v + id: alphanumeric_v tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1259,7 +1259,7 @@ state: v - type: decal - id: w + id: alphanumeric_w tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1269,7 +1269,7 @@ state: w - type: decal - id: x + id: alphanumeric_x tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1279,7 +1279,7 @@ state: x - type: decal - id: y + id: alphanumeric_y tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true @@ -1289,7 +1289,7 @@ state: y - type: decal - id: z + id: alphanumeric_z tags: ["crayon", "crayon-2-alphanum"] defaultCleanable: true defaultCustomColor: true From e1bf09b36bfa9e6694219d156a111d2cc02b46cf Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Thu, 16 Jan 2025 19:18:30 -0400 Subject: [PATCH 22/64] ! --- Content.Server/Holopad/HolopadSystem.cs | 47 ++++++++++++++----------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/Content.Server/Holopad/HolopadSystem.cs b/Content.Server/Holopad/HolopadSystem.cs index f3fcc2a9f68..c5e6c15b516 100644 --- a/Content.Server/Holopad/HolopadSystem.cs +++ b/Content.Server/Holopad/HolopadSystem.cs @@ -10,6 +10,7 @@ using Content.Shared.IdentityManagement; using Content.Shared.Labels.Components; using Content.Shared.Silicons.StationAi; +using Content.Shared.Speech; using Content.Shared.Telephone; using Content.Shared.UserInterface; using Content.Shared.Verbs; @@ -18,8 +19,6 @@ using Robust.Shared.Timing; using Robust.Shared.Utility; using System.Linq; -using Content.Shared.Chat; - namespace Content.Server.Holopad; @@ -153,8 +152,7 @@ private void OnHolopadEndCall(Entity entity, ref HolopadEndCal if (IsHolopadControlLocked(entity, args.Actor)) return; - if (entityTelephone.CurrentState != TelephoneState.EndingCall && entityTelephone.CurrentState != TelephoneState.Idle) - _telephoneSystem.EndTelephoneCalls((entity, entityTelephone)); + _telephoneSystem.EndTelephoneCalls((entity, entityTelephone)); // If the user is an AI, end all calls originating from its // associated core to ensure that any broadcasts will end @@ -162,8 +160,7 @@ private void OnHolopadEndCall(Entity entity, ref HolopadEndCal !_stationAiSystem.TryGetStationAiCore((args.Actor, stationAiHeld), out var stationAiCore)) return; - if (TryComp(stationAiCore, out var telephone) && - telephone.CurrentState != TelephoneState.EndingCall && telephone.CurrentState != TelephoneState.Idle) + if (TryComp(stationAiCore, out var telephone)) _telephoneSystem.EndTelephoneCalls((stationAiCore.Value, telephone)); } @@ -221,7 +218,8 @@ private void OnHolopadStationAiRequest(Entity entity, ref Holo { var receiver = new Entity(receiverUid, receiverTelephone); - if (!_telephoneSystem.IsSourceAbleToReachReceiver(source, receiver)) + // Check if the core can reach the call source, rather than the other way around + if (!_telephoneSystem.IsSourceAbleToReachReceiver(receiver, source)) continue; if (_telephoneSystem.IsTelephoneEngaged(receiver)) @@ -236,10 +234,9 @@ private void OnHolopadStationAiRequest(Entity entity, ref Holo LinkHolopadToUser(entity, args.Actor); } - if (!reachableAiCores.Any()) - return; - - _telephoneSystem.BroadcastCallToTelephones(source, reachableAiCores, args.Actor); + // Ignore range so that holopads that ignore other devices on the same grid can request the AI + var options = new TelephoneCallOptions { IgnoreRange = true }; + _telephoneSystem.BroadcastCallToTelephones(source, reachableAiCores, args.Actor, options); } #endregion @@ -360,6 +357,9 @@ private void OnHolopadUserInit(Entity entity, ref Componen private void OnHolopadShutdown(Entity entity, ref ComponentShutdown args) { + if (TryComp(entity, out var telphone) && _telephoneSystem.IsTelephoneEngaged((entity.Owner, telphone))) + _telephoneSystem.EndTelephoneCalls((entity, telphone)); + ShutDownHolopad(entity); SetHolopadAmbientState(entity, false); } @@ -529,16 +529,23 @@ private void GenerateHologram(Entity entity) entity.Comp.HologramProtoId == null) return; - var uid = Spawn(entity.Comp.HologramProtoId, Transform(entity).Coordinates); + var hologramUid = Spawn(entity.Comp.HologramProtoId, Transform(entity).Coordinates); // Safeguard - spawned holograms must have this component - if (!TryComp(uid, out var component)) + if (!TryComp(hologramUid, out var holopadHologram)) { - Del(uid); + Del(hologramUid); return; } - entity.Comp.Hologram = new Entity(uid, component); + entity.Comp.Hologram = new Entity(hologramUid, holopadHologram); + + // Relay speech preferentially through the hologram + if (TryComp(hologramUid, out var hologramSpeech) && + TryComp(entity, out var entityTelephone)) + { + _telephoneSystem.SetSpeakerForTelephone((entity, entityTelephone), (hologramUid, hologramSpeech)); + } } private void DeleteHologram(Entity hologram, Entity attachedHolopad) @@ -636,11 +643,6 @@ private void ShutDownHolopad(Entity entity) UnlinkHolopadFromUser(entity, entity.Comp.User.Value); } - if (TryComp(entity, out var stationAiCoreTelphone) && - stationAiCoreTelphone.CurrentState != TelephoneState.EndingCall && stationAiCoreTelphone.CurrentState != TelephoneState.Idle) - _telephoneSystem.EndTelephoneCalls((entity, stationAiCoreTelphone)); - } - Dirty(entity); } @@ -691,8 +693,12 @@ private void ActivateProjector(Entity entity, EntityUid user) var source = new Entity(stationAiCore.Value, stationAiTelephone); + // Check if the AI is unable to activate the projector (unlikely this will ever pass; its just a safeguard) if (!_telephoneSystem.IsSourceInRangeOfReceiver(source, receiver)) + { + _popupSystem.PopupEntity(Loc.GetString("holopad-ai-is-unable-to-activate-projector"), receiver, user); return; + } // Terminate any calls that the core is hosting and immediately connect to the receiver _telephoneSystem.TerminateTelephoneCalls(source); @@ -737,7 +743,6 @@ private void ExecuteBroadcast(Entity source, EntityUid user) var receiverTelephoneEntity = new Entity(receiver, receiverTelephone); if (sourceTelephoneEntity == receiverTelephoneEntity || - receiverTelephone.UnlistedNumber || !_telephoneSystem.IsSourceAbleToReachReceiver(sourceTelephoneEntity, receiverTelephoneEntity)) continue; From 25c1da6e3f5c39e527cafd30e9f0ac47294e42df Mon Sep 17 00:00:00 2001 From: chromiumboy <50505512+chromiumboy@users.noreply.github.com> Date: Thu, 2 Jan 2025 05:41:12 -0600 Subject: [PATCH 23/64] Speech is relayed by holopad holograms (#33978) * Initial commit * Corrected a field attribute --- Content.Server/Telephone/TelephoneSystem.cs | 16 ++++++++++++---- Content.Shared/Telephone/TelephoneComponent.cs | 12 ++++++++++-- Resources/Locale/en-US/telephone/telephone.ftl | 3 +++ .../Entities/Structures/Machines/holopad.yml | 5 +++++ 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Content.Server/Telephone/TelephoneSystem.cs b/Content.Server/Telephone/TelephoneSystem.cs index a13ebd16fda..e90bfbf6c52 100644 --- a/Content.Server/Telephone/TelephoneSystem.cs +++ b/Content.Server/Telephone/TelephoneSystem.cs @@ -104,14 +104,17 @@ private void OnTelephoneMessageReceived(Entity entity, ref T var nameEv = new TransformSpeakerNameEvent(args.MessageSource, Name(args.MessageSource)); RaiseLocalEvent(args.MessageSource, nameEv); - var name = Loc.GetString("speech-name-relay", - ("speaker", Name(entity)), - ("originalName", nameEv.VoiceName)); + // Determine if speech should be relayed via the telephone itself or a designated speaker + var speaker = entity.Comp.Speaker != null ? entity.Comp.Speaker.Value.Owner : entity.Owner; + + var name = Loc.GetString("chat-telephone-name-relay", + ("originalName", nameEv.VoiceName), + ("speaker", Name(speaker))); var range = args.TelephoneSource.Comp.LinkedTelephones.Count > 1 ? ChatTransmitRange.HideChat : ChatTransmitRange.GhostRangeLimit; var volume = entity.Comp.SpeakerVolume == TelephoneVolume.Speak ? InGameICChatType.Speak : InGameICChatType.Whisper; - _chat.TrySendInGameICMessage(entity, args.Message, volume, range, nameOverride: name, checkRadioPrefix: false); + _chat.TrySendInGameICMessage(speaker, args.Message, volume, range, nameOverride: name, checkRadioPrefix: false); } #endregion @@ -404,6 +407,11 @@ private void SetTelephoneMicrophoneState(Entity entity, bool } } + public void SetSpeakerForTelephone(Entity entity, Entity? speaker) + { + entity.Comp.Speaker = speaker; + } + private (string?, string?) GetNameAndJobOfCallingEntity(EntityUid uid) { string? presumedName = null; diff --git a/Content.Shared/Telephone/TelephoneComponent.cs b/Content.Shared/Telephone/TelephoneComponent.cs index c08f8b9af37..c24840ce01a 100644 --- a/Content.Shared/Telephone/TelephoneComponent.cs +++ b/Content.Shared/Telephone/TelephoneComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.Chat; +using Content.Shared.Speech; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Serialization; @@ -92,6 +93,12 @@ public sealed partial class TelephoneComponent : Component [DataField] public bool UnlistedNumber = false; + /// + /// Speech is relayed through this entity instead of the telephone + /// + [ViewVariables(VVAccess.ReadOnly)] + public Entity? Speaker = null; + /// /// Telephone number for this device /// @@ -182,7 +189,8 @@ public record struct TelephoneCallEndedEvent(); [Serializable, NetSerializable] public struct TelephoneCallOptions { - public bool ForceConnect; // The source immediately starts a call with the receiver, potentially interrupting a call that is already in progress + public bool IgnoreRange; // The source can always reach its target + public bool ForceConnect; // The source immediately starts a call with the receiver, potentially interrupting a call that is already in progress public bool ForceJoin; // The source smoothly joins a call in progress, or starts a normal call with the receiver if there is none public bool MuteSource; // Chatter from the source is not transmitted - could be used for eavesdropping when combined with 'ForceJoin' public bool MuteReceiver; // Chatter from the receiver is not transmitted - useful for broadcasting messages to multiple receivers @@ -214,7 +222,7 @@ public enum TelephoneVolume : byte [Serializable, NetSerializable] public enum TelephoneRange : byte { - Grid, // Can only reach telephones that are on the same grid + Grid, // Can only reach telephones that are on the same grid Map, // Can reach any telephone that is on the same map Unlimited, // Can reach any telephone, across any distance } diff --git a/Resources/Locale/en-US/telephone/telephone.ftl b/Resources/Locale/en-US/telephone/telephone.ftl index b1b27768e6e..6685a67c8e2 100644 --- a/Resources/Locale/en-US/telephone/telephone.ftl +++ b/Resources/Locale/en-US/telephone/telephone.ftl @@ -8,3 +8,6 @@ chat-telephone-caller-id-with-job = [color={$color}][font={$fontType} size={$fon chat-telephone-caller-id-without-job = [color={$color}][font={$fontType} size={$fontSize}][bold]{CAPITALIZE($callerName)}[/bold][/font][/color] chat-telephone-unknown-device = [color={$color}][font={$fontType} size={$fontSize}][bolditalic]Unknown device[/bolditalic][/font][/color] chat-telephone-device-id = [color={$color}][font={$fontType} size={$fontSize}][bold]{CAPITALIZE($deviceName)}[/bold][/font][/color] + +# Chat text +chat-telephone-name-relay = {$originalName} ({$speaker}) \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Structures/Machines/holopad.yml b/Resources/Prototypes/Entities/Structures/Machines/holopad.yml index f4327cfd03b..84ecb085530 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/holopad.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/holopad.yml @@ -149,6 +149,7 @@ # These are spawned by holopads - type: entity id: HolopadHologram + name: hologram categories: [ HideSpawnMenu ] suffix: DO NOT MAP components: @@ -163,6 +164,10 @@ - type: Appearance - type: TypingIndicator proto: robot + - type: Speech + speechVerb: Robotic + speechSounds: Borg + speechBubbleOffset: 0.45 - type: HolopadHologram rsiPath: Structures/Machines/holopad.rsi rsiState: icon_in_call From 3efbb58f03d4ace06444407f96027aab3211f4ec Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Thu, 16 Jan 2025 19:19:29 -0400 Subject: [PATCH 24/64] Imports --- Content.Server/Holopad/HolopadSystem.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Content.Server/Holopad/HolopadSystem.cs b/Content.Server/Holopad/HolopadSystem.cs index c5e6c15b516..3ad7875f224 100644 --- a/Content.Server/Holopad/HolopadSystem.cs +++ b/Content.Server/Holopad/HolopadSystem.cs @@ -19,6 +19,8 @@ using Robust.Shared.Timing; using Robust.Shared.Utility; using System.Linq; +using Content.Shared.Chat; + namespace Content.Server.Holopad; From ded7a46c3dcbf944182c205435814a403636596b Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Thu, 16 Jan 2025 19:25:31 -0400 Subject: [PATCH 25/64] No base --- .../Prototypes/Entities/Clothing/Shoes/magboots.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml index f2789866745..45d5a4956ab 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml @@ -136,16 +136,6 @@ sprite: null size: Normal -- type: entity - id: ActionBaseToggleMagboots - name: Toggle Magboots - description: Toggles the magboots on and off. - noSpawn: true - components: - - type: InstantAction - itemIconStyle: NoItem - event: !type:ToggleMagbootsEvent - - type: entity id: ActionToggleMagboots name: Toggle Magboots From 4a6b71df50983c11bfc159d4dd541039629ba087 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Thu, 16 Jan 2025 19:26:10 -0400 Subject: [PATCH 26/64] Remove extra whitespace --- Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml index 45d5a4956ab..83bf99909f3 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml @@ -144,4 +144,4 @@ components: - type: InstantAction itemIconStyle: BigItem - event: !type:ToggleActionEvent + event: !type:ToggleActionEvent \ No newline at end of file From 04d0d4af2dd6ebb539b559922f0334865d03b68d Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Thu, 16 Jan 2025 19:43:05 -0400 Subject: [PATCH 27/64] Fix YML fail due to outdated UserInterfaceComponent --- Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml index 83bf99909f3..f5c2d775ba0 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml @@ -115,7 +115,7 @@ key: enum.SharedGasTankUiKey.Key - type: UserInterface interfaces: - - key: enum.SharedGasTankUiKey.Key + enum.SharedGasTankUiKey.Key: type: GasTankBoundUserInterface - type: Explosive explosionType: Default From e4094b1909feaf31ffe8bd140a9d00289213a69b Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sun, 26 May 2024 16:07:16 -0400 Subject: [PATCH 28/64] fix borg ui mispredict opening (#28305) move borg ui junk to shared --- .../ActivatableUIRequiresLockSystem.cs | 43 ------------------- Content.Server/Silicons/Borgs/BorgSystem.cs | 2 +- Content.Server/Wires/WiresSystem.cs | 23 ---------- .../ActivatableUIRequiresLockComponent.cs | 10 +++-- Content.Shared/Lock/LockSystem.cs | 26 +++++++++++ .../ActivatableUIRequiresPanelComponent.cs | 8 ++-- Content.Shared/Wires/SharedWiresSystem.cs | 22 ++++++++++ 7 files changed, 60 insertions(+), 74 deletions(-) delete mode 100644 Content.Server/Lock/EntitySystems/ActivatableUIRequiresLockSystem.cs rename {Content.Server/Lock/Components => Content.Shared/Lock}/ActivatableUIRequiresLockComponent.cs (66%) rename {Content.Server => Content.Shared}/Wires/ActivatableUIRequiresPanelComponent.cs (71%) diff --git a/Content.Server/Lock/EntitySystems/ActivatableUIRequiresLockSystem.cs b/Content.Server/Lock/EntitySystems/ActivatableUIRequiresLockSystem.cs deleted file mode 100644 index 04f8e2eb54c..00000000000 --- a/Content.Server/Lock/EntitySystems/ActivatableUIRequiresLockSystem.cs +++ /dev/null @@ -1,43 +0,0 @@ -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/Silicons/Borgs/BorgSystem.cs b/Content.Server/Silicons/Borgs/BorgSystem.cs index 25a5f4c96e2..c4f840eed1e 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.cs @@ -5,7 +5,6 @@ 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; @@ -202,6 +201,7 @@ private void OnPowerCellSlotEmpty(EntityUid uid, BorgChassisComponent component, Toggle.TryDeactivate(uid); UpdateUI(uid, component); } + private void OnGetDeadIC(EntityUid uid, BorgChassisComponent component, ref GetCharactedDeadIcEvent args) { args.Dead = true; diff --git a/Content.Server/Wires/WiresSystem.cs b/Content.Server/Wires/WiresSystem.cs index 526a14b1ab4..44348dc5de8 100644 --- a/Content.Server/Wires/WiresSystem.cs +++ b/Content.Server/Wires/WiresSystem.cs @@ -4,7 +4,6 @@ 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; @@ -12,20 +11,17 @@ using Content.Shared.Popups; using Content.Shared.Power; 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!; @@ -53,8 +49,6 @@ public override void Initialize() SubscribeLocalEvent(OnTimedWire); SubscribeLocalEvent(OnWiresPowered); SubscribeLocalEvent(OnDoAfter); - SubscribeLocalEvent(OnAttemptOpenActivatableUI); - SubscribeLocalEvent(OnActivatableUIPanelChanged); SubscribeLocalEvent(SetWiresPanelSecurity); } @@ -474,23 +468,6 @@ private void OnPanelChanged(Entity ent, ref PanelChangedEvent ar _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.Server/Lock/Components/ActivatableUIRequiresLockComponent.cs b/Content.Shared/Lock/ActivatableUIRequiresLockComponent.cs similarity index 66% rename from Content.Server/Lock/Components/ActivatableUIRequiresLockComponent.cs rename to Content.Shared/Lock/ActivatableUIRequiresLockComponent.cs index dac677c1c27..7d701ffd874 100644 --- a/Content.Server/Lock/Components/ActivatableUIRequiresLockComponent.cs +++ b/Content.Shared/Lock/ActivatableUIRequiresLockComponent.cs @@ -1,15 +1,17 @@ -namespace Content.Server.Lock.Components; +using Robust.Shared.GameStates; + +namespace Content.Shared.Lock; /// /// This is used for activatable UIs that require the entity to have a lock in a certain state. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent, Access(typeof(LockSystem))] 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("requireLocked"), ViewVariables(VVAccess.ReadWrite)] - public bool requireLocked = false; + [DataField] + public bool RequireLocked; } diff --git a/Content.Shared/Lock/LockSystem.cs b/Content.Shared/Lock/LockSystem.cs index 9c1d1a1c789..25b49946099 100644 --- a/Content.Shared/Lock/LockSystem.cs +++ b/Content.Shared/Lock/LockSystem.cs @@ -10,6 +10,7 @@ using Content.Shared.Popups; using Content.Shared.Storage; using Content.Shared.Storage.Components; +using Content.Shared.UserInterface; using Content.Shared.Verbs; using Content.Shared.Wires; using JetBrains.Annotations; @@ -25,6 +26,7 @@ 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,6 +50,9 @@ public override void Initialize() SubscribeLocalEvent(OnLockToggleAttempt); SubscribeLocalEvent(OnAttemptChangePanel); SubscribeLocalEvent(OnUnanchorAttempt); + + SubscribeLocalEvent(OnUIOpenAttempt); + SubscribeLocalEvent(LockToggled); } private void OnStartup(EntityUid uid, LockComponent lockComp, ComponentStartup args) @@ -355,5 +360,26 @@ private void OnUnanchorAttempt(Entity ent, ref Unanch 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); + } } diff --git a/Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs b/Content.Shared/Wires/ActivatableUIRequiresPanelComponent.cs similarity index 71% rename from Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs rename to Content.Shared/Wires/ActivatableUIRequiresPanelComponent.cs index f92df3d3d59..6e8fff6e49c 100644 --- a/Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs +++ b/Content.Shared/Wires/ActivatableUIRequiresPanelComponent.cs @@ -1,15 +1,17 @@ -namespace Content.Server.Wires; +using Robust.Shared.GameStates; + +namespace Content.Shared.Wires; /// /// This is used for activatable UIs that require the entity to have a panel in a certain state. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent, Access(typeof(SharedWiresSystem))] 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("requireOpen"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public bool RequireOpen = true; } diff --git a/Content.Shared/Wires/SharedWiresSystem.cs b/Content.Shared/Wires/SharedWiresSystem.cs index 7ef2230e7a0..c4f860e165c 100644 --- a/Content.Shared/Wires/SharedWiresSystem.cs +++ b/Content.Shared/Wires/SharedWiresSystem.cs @@ -3,6 +3,7 @@ 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; @@ -10,6 +11,7 @@ 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!; @@ -22,6 +24,9 @@ public override void Initialize() SubscribeLocalEvent(OnPanelDoAfter); SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnExamine); + + SubscribeLocalEvent(OnAttemptOpenActivatableUI); + SubscribeLocalEvent(OnActivatableUIPanelChanged); } private void OnStartup(Entity ent, ref ComponentStartup args) @@ -147,6 +152,23 @@ public bool IsPanelOpen(Entity entity, EntityUid? tool = n 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); + } } /// From 8a375c63f251af2325da807ebcc59009c8518554 Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Thu, 16 Jan 2025 19:51:21 -0400 Subject: [PATCH 29/64] Fix yml --- Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml index f5c2d775ba0..ceba20644d9 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml @@ -14,7 +14,6 @@ sprite: Clothing/Shoes/Boots/magboots.rsi - type: ToggleClothing action: ActionToggleMagboots - mustEquip: false - type: ComponentToggler components: - type: NoSlip From 303d94da6fa930ba22e7c2eaf1c8749985fe1b1e Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Thu, 16 Jan 2025 19:55:55 -0400 Subject: [PATCH 30/64] Add popup to ActivatableUIRequiresPanel --- Content.Shared/Wires/SharedWiresSystem.cs | 10 ++++++++++ .../en-US/components/base-computer-ui-component.ftl | 1 + 2 files changed, 11 insertions(+) diff --git a/Content.Shared/Wires/SharedWiresSystem.cs b/Content.Shared/Wires/SharedWiresSystem.cs index c4f860e165c..82b048a72fc 100644 --- a/Content.Shared/Wires/SharedWiresSystem.cs +++ b/Content.Shared/Wires/SharedWiresSystem.cs @@ -2,9 +2,12 @@ using Content.Shared.Database; using Content.Shared.Examine; using Content.Shared.Interaction; +using Content.Shared.Popups; using Content.Shared.Tools.Systems; using Content.Shared.UserInterface; using Robust.Shared.Audio.Systems; +using Robust.Shared.Network; + namespace Content.Shared.Wires; @@ -15,6 +18,8 @@ public abstract class SharedWiresSystem : EntitySystem [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!; [Dependency] protected readonly SharedToolSystem Tool = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly INetManager _netManager = default!; public override void Initialize() { @@ -159,7 +164,12 @@ private void OnAttemptOpenActivatableUI(EntityUid uid, ActivatableUIRequiresPane return; if (component.RequireOpen != wires.Open) + { + if (_netManager.IsServer) + _popup.PopupCursor(Loc.GetString("base-computer-ui-component-not-opened", ("machine", uid))); + args.Cancel(); + } } private void OnActivatableUIPanelChanged(EntityUid uid, ActivatableUIRequiresPanelComponent component, ref PanelChangedEvent args) diff --git a/Resources/Locale/en-US/components/base-computer-ui-component.ftl b/Resources/Locale/en-US/components/base-computer-ui-component.ftl index 731609ea0ec..c5a336fbc88 100644 --- a/Resources/Locale/en-US/components/base-computer-ui-component.ftl +++ b/Resources/Locale/en-US/components/base-computer-ui-component.ftl @@ -1 +1,2 @@ base-computer-ui-component-not-powered = {CAPITALIZE(THE($machine))} is not powered. +base-computer-ui-component-not-opened = {CAPITALIZE(THE($machine))} does not have an open panel. From f2c01dc71fd57f56c1524e28a03d2951dcd23365 Mon Sep 17 00:00:00 2001 From: Angelo Fallaria Date: Fri, 17 Jan 2025 08:04:25 +0800 Subject: [PATCH 31/64] increasse flaps slow even more --- Resources/Prototypes/Entities/Structures/plastic_flaps.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/plastic_flaps.yml b/Resources/Prototypes/Entities/Structures/plastic_flaps.yml index ea1ed8362dd..0e81ffbac80 100644 --- a/Resources/Prototypes/Entities/Structures/plastic_flaps.yml +++ b/Resources/Prototypes/Entities/Structures/plastic_flaps.yml @@ -123,5 +123,5 @@ - type: StaticPrice price: 100 - type: SpeedModifierContacts - walkSpeedModifier: 0.3 - sprintSpeedModifier: 0.3 + walkSpeedModifier: 0.25 + sprintSpeedModifier: 0.25 From 662ad8a9523cae198b6816f836503250bda9465e Mon Sep 17 00:00:00 2001 From: sleepyyapril Date: Thu, 16 Jan 2025 20:13:45 -0400 Subject: [PATCH 32/64] Fix popup prediction --- Content.Shared/Wires/SharedWiresSystem.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Content.Shared/Wires/SharedWiresSystem.cs b/Content.Shared/Wires/SharedWiresSystem.cs index 82b048a72fc..19300d3b03d 100644 --- a/Content.Shared/Wires/SharedWiresSystem.cs +++ b/Content.Shared/Wires/SharedWiresSystem.cs @@ -165,9 +165,7 @@ private void OnAttemptOpenActivatableUI(EntityUid uid, ActivatableUIRequiresPane if (component.RequireOpen != wires.Open) { - if (_netManager.IsServer) - _popup.PopupCursor(Loc.GetString("base-computer-ui-component-not-opened", ("machine", uid))); - + _popup.PopupPredicted(Loc.GetString("base-computer-ui-component-not-opened", ("machine", uid)), uid, uid); args.Cancel(); } } From 0c6ff0011ac64d75e42e5374a8fc569f87a5ecf3 Mon Sep 17 00:00:00 2001 From: Skubman Date: Fri, 17 Jan 2025 08:16:27 +0800 Subject: [PATCH 33/64] Arachne Standing Up Fix (#1571) # Description Arachne can now stand up after lying down again. ## Media Arachne with 8 registered legs lying down and standing up https://github.com/user-attachments/assets/b7b5c83b-b741-4d4f-88b6-1133834104de ## Technical Details I don't even really understand what I'm about to say, just trying to explain the issue for documentation purposes. `LayingDownSystem.TryStandUp` checks if the entity's `BodyComponent.LegEntities` element count is less than `BodyComponent.RequiredLegs`, which for Arachne is 8. https://github.com/Simple-Station/Einstein-Engines/blob/78347d76fb50908888c77df5e3b935b773297620/Content.Shared/Standing/SharedLayingDownSystem.cs#L141-L151 However, due to some issues with the part slot naming, `BodyComponent.LegEntities` for Arachne has no elements, because of Shitmed being hardcoded and being built with the expectation that all slot IDs start with "{body part symmetry} {part type}" like "left leg" and "right leg", from `GetSlotFromBodyPart`. This means that Arachne legs which were not following the format were not registered after being inserted into the thorax part's container. https://github.com/Simple-Station/Einstein-Engines/blob/78347d76fb50908888c77df5e3b935b773297620/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs#L1044-L1060 This is where the function is used: https://github.com/Simple-Station/Einstein-Engines/blob/78347d76fb50908888c77df5e3b935b773297620/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs#L216-L236 For the left foreleg for example, the container ID (`slotId`) is `body_part_slot_right foreleg` but the result of `PartSlotContainerIdPrefix + GetSlotFromBodyPart(part)` is `body_part_slot_right leg`, and slotId does not contain `right leg` so the check fails. This is resolved by renaming `left foreleg` to `left leg (fore)`, `right hind leg` to `right leg (hind)` , so on and so forth. For unconventional body parts like a thorax, you need to explicitly set the `BodyPart.SlotId` field to override `GetSlotFromBodyPart` returning `other`, when it should return `thorax` to pass the slotId check. This will make sure the thorax is registered and thus can recursively register the legs attached to it. ## Changelog :cl: Skubman - fix: Arachne can now stand up after lying down. --- .../Entities/Body/Parts/spider.yml | 1 + .../Entities/Body/Prototypes/arachne.yml | 33 ++++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Body/Parts/spider.yml b/Resources/Prototypes/Nyanotrasen/Entities/Body/Parts/spider.yml index fbd45804dd2..b52a91c1182 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Body/Parts/spider.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Body/Parts/spider.yml @@ -36,6 +36,7 @@ sprite: Mobs/Species/Moth/parts.rsi state: "torso_m" - type: BodyPart #"Other" type + slotId: thorax - type: Extractable juiceSolution: reagents: diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Body/Prototypes/arachne.yml b/Resources/Prototypes/Nyanotrasen/Entities/Body/Prototypes/arachne.yml index d8eb35629bf..ea399ea341f 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Body/Prototypes/arachne.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Body/Prototypes/arachne.yml @@ -38,27 +38,28 @@ thorax: part: ThoraxSpider connections: - - left foreleg - - left second leg - - left third leg - - left hind leg - - right foreleg - - right second leg - - right third leg - - right hind leg - left foreleg: + # The slots needs to start with (symmetry) (part) or they're not gonna be registered + - left leg (fore) + - left leg (second) + - left leg (third) + - left leg (hind) + - right leg (fore) + - right leg (second) + - right leg (third) + - right leg (hind) + left leg (fore): part: LeftLegSpider - left second leg: + left leg (second): part: LeftLegSpider - left third leg: + left leg (third): part: LeftLegSpider - left hind leg: + left leg (hind): part: LeftLegSpider - right foreleg: + right leg (fore): part: RightLegSpider - right second leg: + right leg (second): part: RightLegSpider - right third leg: + right leg (third): part: RightLegSpider - right hind leg: + right leg (hind): part: RightLegSpider From b07d6e1d42645b5fe237912ed0b3cb816c788c81 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Fri, 17 Jan 2025 00:16:55 +0000 Subject: [PATCH 34/64] Automatic Changelog Update (#1571) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 9b922a4c36c..c1ce062b274 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -9996,3 +9996,10 @@ Entries: id: 6703 time: '2025-01-16T07:21:22.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1557 +- author: Skubman + changes: + - type: Fix + message: Arachne can now stand up after lying down. + id: 6704 + time: '2025-01-17T00:16:28.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1571 From 7ce802313f9d39176b7f0389cc66ce9bae5c0fb1 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Fri, 17 Jan 2025 00:27:23 +0000 Subject: [PATCH 35/64] Automatic Changelog Update (#1572) --- Resources/Changelog/Changelog.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index c1ce062b274..64730897f4b 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -10003,3 +10003,32 @@ Entries: id: 6704 time: '2025-01-17T00:16:28.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1571 +- author: sleepyyapril + changes: + - type: Add + message: >- + Added a popup for when the UI cannot be open due to the maintenance + panel being closed. + - type: Fix + message: Fixed Ripley control panel. + - type: Fix + message: Fixed Ripley giving battery instead of taking. + - type: Fix + message: Hopefully fixed wire light not appearing. + - type: Fix + message: Fixed crayons not placing the decal you want. + - type: Fix + message: Fixed bloodred boots not acting as a jetpack or magboots. + - type: Fix + message: Fixed voicemask chameleon menu. + - type: Fix + message: Fixed spawnpoint issues. (hopefully) + - type: Tweak + message: >- + The crayons menu now has letters and numbers always next to each other, + sorted. + - type: Tweak + message: Crayon UI has been improved, including a search feature. + id: 6705 + time: '2025-01-17T00:27:00.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1572 From b49b9600022e1311b6f32d7431741f8414357cfd Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Fri, 17 Jan 2025 00:29:41 +0000 Subject: [PATCH 36/64] Automatic Changelog Update (#1552) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 64730897f4b..f2adbb3abcd 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -10032,3 +10032,10 @@ Entries: id: 6705 time: '2025-01-17T00:27:00.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1572 +- author: spess-empyrean + changes: + - type: Fix + message: Fixed some errors with looping behaviors like jitter. + id: 6706 + time: '2025-01-17T00:29:16.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1552 From ed5e26b6ff6f964e1118faf4f10627cd476d6f57 Mon Sep 17 00:00:00 2001 From: Angelo Fallaria Date: Fri, 17 Jan 2025 08:35:51 +0800 Subject: [PATCH 37/64] update trait examples for slips/difficult terrain --- Resources/Locale/en-US/traits/traits.ftl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index 03e7e47aad3..592e8b32bcf 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -224,8 +224,8 @@ trait-description-Voracious = Nothing gets between you and your food. Your endless consumption of food and drinks is twice as fast. --terrain-example = [color=gray](e.g. plastic flaps, spider web, slime puddle, kudzu)[/color] --slippery-example = [color=gray](e.g. banana peel, water puddle, soap, space lube)[/color] +-terrain-example = [color=gray](e.g. plastic flaps, spider webs, slime puddles, kudzu)[/color] +-slippery-example = [color=gray](e.g. banana peels, water puddles, soap, space lube)[/color] trait-name-ParkourTraining = Parkour Training trait-description-ParkourTraining = From 354da0eebeac95f221cbbe164c8535ba396953f2 Mon Sep 17 00:00:00 2001 From: Skubman Date: Fri, 17 Jan 2025 09:25:54 +0800 Subject: [PATCH 38/64] Update Content.Server/Nutrition/Components/FoodComponent.cs Co-authored-by: VMSolidus Signed-off-by: Skubman --- Content.Server/Nutrition/Components/FoodComponent.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/Nutrition/Components/FoodComponent.cs b/Content.Server/Nutrition/Components/FoodComponent.cs index de325ba35a4..db31e30073b 100644 --- a/Content.Server/Nutrition/Components/FoodComponent.cs +++ b/Content.Server/Nutrition/Components/FoodComponent.cs @@ -72,8 +72,8 @@ public sealed partial class FoodComponent : Component /// /// Shitmed Change: Whether to show a popup to everyone in range when attempting to eat this food, and upon successful eating. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public bool PopupOnEat = false; + [DataField] + public bool PopupOnEat; /// /// For mobs that are food, requires killing them before eating. From 89101e3d58e77357894acbab5ab3e53557356f12 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Fri, 17 Jan 2025 01:54:16 +0000 Subject: [PATCH 39/64] Automatic Changelog Update (#1573) --- Resources/Changelog/Changelog.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index f2adbb3abcd..c180f95f24b 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -10039,3 +10039,14 @@ Entries: id: 6706 time: '2025-01-17T00:29:16.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1552 +- author: Skubman + changes: + - type: Add + message: >- + Plastic flaps are now considered Difficult Terrain, and will slow you + down substantially when crawling through them. Note that the slows of + all Difficult Terrain are halved by the Parkour Training trait, and + increased by the Bad Knees trait. + id: 6707 + time: '2025-01-17T01:53:16.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1573 From 3a1c2dd072f387edaa4c36462634b6a0a3ccc820 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Fri, 17 Jan 2025 01:54:47 +0000 Subject: [PATCH 40/64] Automatic Changelog Update (#1563) --- Resources/Changelog/Changelog.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index c180f95f24b..df4db6da927 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -10050,3 +10050,21 @@ Entries: id: 6707 time: '2025-01-17T01:53:16.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1573 +- author: Skubman + changes: + - type: Tweak + message: >- + Eating a brain now takes 5 seconds (from 1 second), and feeding someone + a brain takes 6 seconds. + - type: Add + message: >- + Attempting or succeeding to eat a brain now shows a red popup message to + everyone else in range, e.g. "Urist McHands is trying to eat the brain!" + / "Urist McHands ate the brain!". + - type: Fix + message: >- + Fixed a bug where players after being force-fed were not shown a popup + showing the food's flavors. + id: 6708 + time: '2025-01-17T01:53:42.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1563 From 638071c48fe3ac7c727a1294de3b6d5d8136e79f Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Thu, 16 Jan 2025 21:03:49 -0500 Subject: [PATCH 41/64] Glimmer Rework 1: "Swingy Glimmer" (#1480) # Description This PR brings back a feature that was present in the Psionic Refactor Version 1, which ultimately never made it into the game. I have substantially reworked the underlying math behind Glimmer, such that it operates on a Logistic Curve described by this equation: ![GlimmerEquation](https://github.com/user-attachments/assets/b079c0f6-5944-408f-adf6-170b8472d6c2) Instead of 0 being the "Normal" amount of glimmer, the "Normal" amount is the "seemingly arbitrary" number 502.941. This number is measured first by taking the derivative of the Glimmer Equation, and then solving for the derivative equal to 1. Above this constant, glimmer grows exponentially more difficult to increase. Below this constant, glimmer grows exponentially easier to increase. It will thus constantly attempt to trend towards the "Glimmer Equilibrium". Probers, Drainers, Anomalies, and Glimmer Mites all cause glimmer to "Fluctuate", either up or down the graph. This gives a glimmer that swings constantly to both high and low values, with more violent swings being caused by having more probers/anomalies etc. A great deal of math functions have been implemented that allow for various uses for glimmer measurements, and psionic powers can even have their effects modified by said measurements. The most significant part of this rework is what's facing Epistemics. It's essentially no longer possible for Probers to cause a round-ending chain of events known as "Glimmerloose". You can ALWAYS recover from high glimmer, no matter how high it gets. As a counterpart to this, Probers have had the math behind their research point generation reworked. Research output follows an inverse log base 4 curve. Which can be found here: https://www.desmos.com/calculator/q183tseun8 I wouldn't drop this massive update on people without a way for them to understand what's going on. So this PR also features the return(and expansion of), the much-demanded Psionics Guidebook.

Media

Psionics Guidebook ![image](https://github.com/user-attachments/assets/6449f518-bdce-4ba5-a9f5-9f87b5c424c9) ![image](https://github.com/user-attachments/assets/5cf5d5a1-8567-40ae-a020-af074cf9abe3)

# Changelog :cl: VMSolidus, Gollee - add: Glimmer has been substantially reworked. Please read the new Psionics Guidebook for more information. In short: "500 is the new normal glimmer. Stop panicking if Sophia says the glimmer is 500. Call code white if it gets to 750 and stays above that level." - add: The much-requested Psionics Guidebook has returned, now with all new up-to-date information. - remove: Removed "GLIMMERLOOSE". It's no longer possible for glimmer to start a chain of events that ends the round if epistemics isn't destroyed. You can ALWAYS recover from high glimmer now. - tweak: All glimmer events have had their thresholds tweaked to reflect the fact that 500 is the new "Normal" amount of glimmer. --------- Signed-off-by: VMSolidus --- .../Cartridges/GlimmerMonitorUiFragment.xaml | 1 + .../GlimmerMonitorUiFragment.xaml.cs | 16 +- .../AnomalyPowerSystem.EntitySpawn.cs | 2 +- .../Abilities/HealOtherPowerSystem.cs | 14 +- .../Abilities/Psionics/AnomalyPowerSystem.cs | 4 +- .../PsionicAbilitiesSystem.Functions.cs | 4 +- .../Chapel/SacrificialAltarSystem.cs | 2 +- Content.Server/Chat/TelepathicChatSystem.cs | 8 +- .../ChangeGlimmerReactionEffect.cs | 6 +- .../RaiseGlimmerConditionComponent.cs | 8 +- .../Systems/RaiseGlimmerConditionSystem.cs | 6 +- .../SophicScribe/SophicScribeSystem.cs | 8 +- .../Psionics/Glimmer/GlimmerCommands.cs | 11 +- .../Psionics/Glimmer/GlimmerReactiveSystem.cs | 590 +++++++++--------- .../Glimmer/PassiveGlimmerReductionSystem.cs | 105 ++-- .../Structures/GlimmerSourceComponent.cs | 59 +- .../Structures/GlimmerStructuresSystem.cs | 164 +++-- Content.Server/Psionics/PsionicsSystem.cs | 3 +- .../Research/Oracle/OracleSystem.cs | 2 +- .../Components/NoosphericFryRuleComponent.cs | 5 + .../StationEvents/EventManagerSystem.cs | 4 +- .../StationEvents/Events/FreeProberRule.cs | 2 +- .../Events/GlimmerEventSystem.cs | 42 +- .../StationEvents/Events/NoosphericFryRule.cs | 4 +- .../Events/NoosphericStormRule.cs | 5 +- .../Systems/ArtifactAnalyzerSystem.cs | 2 +- Content.Shared/CCVar/CCVars.cs | 8 +- .../Chapel/SacrificialAltarComponent.cs | 2 +- .../Psionics/Glimmer/GlimmerSystem.cs | 199 ++++-- .../Psionics/SharedPsionicAbilitiesSystem.cs | 4 +- Resources/Locale/en-US/guidebook/psionics.ftl | 4 + .../DeltaV/Entities/Mobs/NPCs/familiars.yml | 1 + .../Entities/Mobs/NPCs/elemental.yml | 2 +- .../Entities/Mobs/NPCs/glimmer_creatures.yml | 1 + .../Entities/Mobs/NPCs/revenant.yml | 1 + .../Prototypes/Entities/Mobs/NPCs/xeno.yml | 1 + .../Entities/Mobs/Player/familiars.yml | 2 + .../Entities/Mobs/Player/guardian.yml | 2 + .../Entities/Objects/Misc/books.yml | 23 + .../Objects/Specific/Medical/healing.yml | 4 + .../Objects/Specific/Research/misc.yml | 4 +- .../Machines/metempsychoticMachine.yml | 2 + .../Entities/Structures/Specific/oracle.yml | 3 + Resources/Prototypes/Guidebook/psionics.yml | 35 ++ Resources/Prototypes/Guidebook/science.yml | 7 - Resources/Prototypes/Guidebook/ss14.yml | 1 + .../Prototypes/Loadouts/Generic/head.yml | 1 + .../Loadouts/Jobs/Epistemics/acolyte.yml | 1 + .../Loadouts/Jobs/Epistemics/cataloger.yml | 1 + .../Loadouts/Jobs/Epistemics/chaplain.yml | 1 + .../Loadouts/Jobs/Epistemics/golemancer.yml | 1 + .../Loadouts/Jobs/Epistemics/mystagogue.yml | 1 + .../Loadouts/Jobs/Epistemics/mystic.yml | 1 + .../Loadouts/Jobs/Epistemics/noviciate.yml | 1 + .../Jobs/Epistemics/psionicMantis.yml | 2 + .../Loadouts/Jobs/Security/uncategorized.yml | 1 + .../Catalog/Fills/Boxes/ammunition.yml | 3 + .../Clothing/Head/hardsuit-helmets.yml | 2 + .../Entities/Clothing/Head/hats.yml | 5 + .../Entities/Clothing/psionic_clothing.yml | 8 + .../Entities/Mobs/Player/special.yml | 2 + .../Objects/Specific/Medical/pills.yml | 6 + .../Guns/Ammunition/Cartridges/shotgun.yml | 4 +- .../Structures/Research/glimmer_prober.yml | 9 + .../Structures/Research/sophicscribe.yml | 3 + .../Nyanotrasen/GameRules/events.yml | 31 +- .../Nyanotrasen/Guidebook/epistemics.yml | 5 - .../Roles/Jobs/Epistemics/forensicmantis.yml | 2 +- .../Roles/Jobs/Civilian/chaplain.yml | 1 + .../Roles/Jobs/Civilian/librarian.yml | 2 +- .../Roles/Jobs/Science/research_assistant.yml | 1 + .../Roles/Jobs/Science/research_director.yml | 3 +- .../Roles/Jobs/Science/roboticist.yml | 1 + .../Roles/Jobs/Science/scientist.yml | 1 + .../Roles/Jobs/Science/senior_researcher.yml | 1 + .../Psionics}/Altar.xml | 0 .../ServerInfo/Guidebook/Psionics/Glimmer.xml | 76 +++ .../GlimmerCreatures.xml | 0 .../Guidebook/Psionics/Mindbreaking.xml | 16 + .../Guidebook/Psionics/PsionicInsulation.xml | 12 + .../Guidebook/Psionics/Psionics.xml | 62 ++ 81 files changed, 1060 insertions(+), 590 deletions(-) create mode 100644 Resources/Locale/en-US/guidebook/psionics.ftl create mode 100644 Resources/Prototypes/Guidebook/psionics.yml rename Resources/ServerInfo/{Nyanotrasen/Guidebook/Epistemics => Guidebook/Psionics}/Altar.xml (100%) create mode 100644 Resources/ServerInfo/Guidebook/Psionics/Glimmer.xml rename Resources/ServerInfo/Guidebook/{DeltaV/Epistemics => Psionics}/GlimmerCreatures.xml (100%) create mode 100644 Resources/ServerInfo/Guidebook/Psionics/Mindbreaking.xml create mode 100644 Resources/ServerInfo/Guidebook/Psionics/PsionicInsulation.xml create mode 100644 Resources/ServerInfo/Guidebook/Psionics/Psionics.xml diff --git a/Content.Client/CartridgeLoader/Cartridges/GlimmerMonitorUiFragment.xaml b/Content.Client/CartridgeLoader/Cartridges/GlimmerMonitorUiFragment.xaml index e09a422ddf7..7dcd993f807 100644 --- a/Content.Client/CartridgeLoader/Cartridges/GlimmerMonitorUiFragment.xaml +++ b/Content.Client/CartridgeLoader/Cartridges/GlimmerMonitorUiFragment.xaml @@ -3,6 +3,7 @@
[DataField("unobstructedRequired")] public bool UnobstructedRequired = false; + + // Nuclear-14 + /// + // The radio frequency on which the message will be transmitted + /// + [DataField] + public int Frequency = 1459; // Common channel frequency } diff --git a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs index 6fd5e46e10a..0260439fbd0 100644 --- a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs +++ b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs @@ -1,6 +1,7 @@ using System.Linq; using Content.Server.Chat.Systems; using Content.Server.Interaction; +using Content.Server.Language; using Content.Server.Popups; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; @@ -13,6 +14,11 @@ using Content.Shared.Radio; using Content.Shared.Chat; using Content.Shared.Radio.Components; +using Content.Shared.UserInterface; // Nuclear-14 +using Content.Shared._NC.Radio; // Nuclear-14 +using Robust.Server.GameObjects; +using Robust.Shared.Network; +using Robust.Shared.Player; // Nuclear-14 using Robust.Shared.Prototypes; namespace Content.Server.Radio.EntitySystems; @@ -28,10 +34,17 @@ public sealed class RadioDeviceSystem : EntitySystem [Dependency] private readonly RadioSystem _radio = default!; [Dependency] private readonly InteractionSystem _interaction = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; + [Dependency] private readonly INetManager _netMan = default!; + [Dependency] private readonly LanguageSystem _language = default!; // Used to prevent a shitter from using a bunch of radios to spam chat. private HashSet<(string, EntityUid)> _recentlySent = new(); + // Frontier: minimum, maximum radio frequencies + private const int MinRadioFrequency = 1000; + private const int MaxRadioFrequency = 3000; + public override void Initialize() { base.Initialize(); @@ -50,6 +63,13 @@ public override void Initialize() SubscribeLocalEvent(OnToggleIntercomMic); SubscribeLocalEvent(OnToggleIntercomSpeaker); SubscribeLocalEvent(OnSelectIntercomChannel); + + SubscribeLocalEvent(OnBeforeHandheldRadioUiOpen); + SubscribeLocalEvent(OnToggleHandheldRadioMic); + SubscribeLocalEvent(OnToggleHandheldRadioSpeaker); + SubscribeLocalEvent(OnChangeHandheldRadioFrequency); + + SubscribeLocalEvent(OnMapInit); // Frontier } public override void Update(float frameTime) @@ -180,9 +200,14 @@ private void OnExamine(EntityUid uid, RadioMicrophoneComponent component, Examin using (args.PushGroup(nameof(RadioMicrophoneComponent))) { - args.PushMarkup(Loc.GetString("handheld-radio-component-on-examine", ("frequency", proto.Frequency))); - args.PushMarkup(Loc.GetString("handheld-radio-component-chennel-examine", - ("channel", proto.LocalizedName))); + args.PushMarkup(Loc.GetString( + "handheld-radio-component-on-examine", + ("frequency", component.Frequency), + ("color", proto.Color.ToHex()))); + args.PushMarkup(Loc.GetString( + "handheld-radio-component-channel-examine", + ("channel", proto.LocalizedName), + ("color", proto.Color.ToHex()))); } } @@ -192,32 +217,28 @@ private void OnListen(EntityUid uid, RadioMicrophoneComponent component, ListenE return; // no feedback loops please. if (_recentlySent.Add((args.Message, args.Source))) - _radio.SendRadioMessage(args.Source, args.Message, _protoMan.Index(component.BroadcastChannel), uid); + _radio.SendRadioMessage(args.Source, args.Message, _protoMan.Index(component.BroadcastChannel), uid, /*Nuclear-14-start*/ frequency: component.Frequency /*Nuclear-14-end*/); } private void OnAttemptListen(EntityUid uid, RadioMicrophoneComponent component, ListenAttemptEvent args) { if (component.PowerRequired && !this.IsPowered(uid, EntityManager) || component.UnobstructedRequired && !_interaction.InRangeUnobstructed(args.Source, uid, 0)) - { args.Cancel(); - } } private void OnReceiveRadio(EntityUid uid, RadioSpeakerComponent component, ref RadioReceiveEvent args) { - if (uid == args.RadioSource) - return; - - var nameEv = new TransformSpeakerNameEvent(args.MessageSource, Name(args.MessageSource)); - RaiseLocalEvent(args.MessageSource, nameEv); - - var name = Loc.GetString("speech-name-relay", - ("speaker", Name(uid)), - ("originalName", nameEv.VoiceName)); - - // log to chat so people can identity the speaker/source, but avoid clogging ghost chat if there are many radios - _chat.TrySendInGameICMessage(uid, args.OriginalChatMsg.Message, InGameICChatType.Whisper, ChatTransmitRange.GhostRangeLimit, nameOverride: name, checkRadioPrefix: false); + var parent = Transform(uid).ParentUid; + if (TryComp(parent, out ActorComponent? actor)) + { + var canUnderstand = _language.CanUnderstand(parent, args.Language.ID); + var msg = new MsgChatMessage + { + Message = canUnderstand ? args.OriginalChatMsg : args.LanguageObfuscatedChatMsg + }; + _netMan.ServerSendMessage(msg, actor.PlayerSession.Channel); + } } private void OnIntercomEncryptionChannelsChanged(Entity ent, ref EncryptionChannelsChangedEvent args) @@ -256,7 +277,7 @@ private void OnSelectIntercomChannel(Entity ent, ref SelectIn if (ent.Comp.RequiresPower && !this.IsPowered(ent, EntityManager)) return; - if (!_protoMan.HasIndex(args.Channel) || !ent.Comp.SupportedChannels.Contains(args.Channel)) + if (!_protoMan.TryIndex(args.Channel, out var channel) || !ent.Comp.SupportedChannels.Contains(args.Channel)) // Nuclear-14: add channel return; SetIntercomChannel(ent, args.Channel); @@ -277,9 +298,79 @@ private void SetIntercomChannel(Entity ent, ProtoId(ent, out var mic)) + { mic.BroadcastChannel = channel; + if(_protoMan.TryIndex(channel, out var channelProto)) // Frontier + mic.Frequency = _radio.GetFrequency(ent, channelProto); // Frontier + } if (TryComp(ent, out var speaker)) speaker.Channels = new(){ channel }; Dirty(ent); } + + // Nuclear-14-Start + #region Handheld Radio + + private void OnBeforeHandheldRadioUiOpen(Entity microphone, ref BeforeActivatableUIOpenEvent args) + { + UpdateHandheldRadioUi(microphone); + } + + private void OnToggleHandheldRadioMic(Entity microphone, ref ToggleHandheldRadioMicMessage args) + { + if (!args.Actor.Valid) + return; + + SetMicrophoneEnabled(microphone, args.Actor, args.Enabled, true); + UpdateHandheldRadioUi(microphone); + } + + private void OnToggleHandheldRadioSpeaker(Entity microphone, ref ToggleHandheldRadioSpeakerMessage args) + { + if (!args.Actor.Valid) + return; + + SetSpeakerEnabled(microphone, args.Actor, args.Enabled, true); + UpdateHandheldRadioUi(microphone); + } + + private void OnChangeHandheldRadioFrequency(Entity microphone, ref SelectHandheldRadioFrequencyMessage args) + { + if (!args.Actor.Valid) + return; + + // Update frequency if valid and within range. + if (args.Frequency >= MinRadioFrequency && args.Frequency <= MaxRadioFrequency) + microphone.Comp.Frequency = args.Frequency; + // Update UI with current frequency. + UpdateHandheldRadioUi(microphone); + } + + private void UpdateHandheldRadioUi(Entity radio) + { + var speakerComp = CompOrNull(radio); + var frequency = radio.Comp.Frequency; + + var micEnabled = radio.Comp.Enabled; + var speakerEnabled = speakerComp?.Enabled ?? false; + var state = new HandheldRadioBoundUIState(micEnabled, speakerEnabled, frequency); + if (TryComp(radio, out var uiComp)) + _ui.SetUiState((radio.Owner, uiComp), HandheldRadioUiKey.Key, state); // Frontier: TrySetUiState + /// Gets the message frequency, if there is no such frequency, returns the standard channel frequency. + ///
+ public int GetFrequency(EntityUid source, RadioChannelPrototype channel) + { + if (TryComp(source, out var radioMicrophone)) + return radioMicrophone.Frequency; + + return channel.Frequency; + } + private void OnIntrinsicReceive(EntityUid uid, IntrinsicRadioReceiverComponent component, ref RadioReceiveEvent args) { if (TryComp(uid, out ActorComponent? actor)) @@ -80,15 +93,23 @@ public void SendRadioMessage( string message, ProtoId channel, EntityUid radioSource, + int? frequency = null, LanguagePrototype? language = null, bool escapeMarkup = true ) => - SendRadioMessage(messageSource, message, _prototype.Index(channel), radioSource, escapeMarkup: escapeMarkup, language: language); + SendRadioMessage(messageSource, message, _prototype.Index(channel), radioSource, escapeMarkup: escapeMarkup, frequency: frequency, language: language); /// /// Send radio message to all active radio listeners /// - public void SendRadioMessage(EntityUid messageSource, string message, RadioChannelPrototype channel, EntityUid radioSource, LanguagePrototype? language = null, bool escapeMarkup = true) + public void SendRadioMessage( + EntityUid messageSource, + string message, + RadioChannelPrototype channel, + EntityUid radioSource, + LanguagePrototype? language = null, + int? frequency = null, + bool escapeMarkup = true) { if (language == null) language = _language.GetLanguage(messageSource); @@ -111,12 +132,12 @@ public void SendRadioMessage(EntityUid messageSource, string message, RadioChann ? FormattedMessage.EscapeText(message) : message; - var wrappedMessage = WrapRadioMessage(messageSource, channel, name, content, language); + var wrappedMessage = WrapRadioMessage(messageSource, channel, name, content, language, frequency); var msg = new ChatMessage(ChatChannel.Radio, content, wrappedMessage, NetEntity.Invalid, null); // ... you guess it var obfuscated = _language.ObfuscateSpeech(content, language); - var obfuscatedWrapped = WrapRadioMessage(messageSource, channel, name, obfuscated, language); + var obfuscatedWrapped = WrapRadioMessage(messageSource, channel, name, obfuscated, language, frequency); var notUdsMsg = new ChatMessage(ChatChannel.Radio, obfuscated, obfuscatedWrapped, NetEntity.Invalid, null); var ev = new RadioReceiveEvent(messageSource, channel, msg, notUdsMsg, language, radioSource); @@ -132,6 +153,10 @@ public void SendRadioMessage(EntityUid messageSource, string message, RadioChann var speakerQuery = GetEntityQuery(); var radioQuery = EntityQueryEnumerator(); + + if (frequency == null) // Nuclear-14 + frequency = GetFrequency(messageSource, channel); // Nuclear-14 + while (canSend && radioQuery.MoveNext(out var receiver, out var radio, out var transform)) { if (!radio.ReceiveAllChannels) @@ -141,6 +166,9 @@ public void SendRadioMessage(EntityUid messageSource, string message, RadioChann continue; } + if (!HasComp(receiver) && GetFrequency(receiver, channel) != frequency) + continue; + if (!channel.LongRange && transform.MapID != sourceMapId && !radio.GlobalReceive) continue; @@ -169,7 +197,13 @@ public void SendRadioMessage(EntityUid messageSource, string message, RadioChann _messages.Remove(message); } - private string WrapRadioMessage(EntityUid source, RadioChannelPrototype channel, string name, string message, LanguagePrototype language) + private string WrapRadioMessage( + EntityUid source, + RadioChannelPrototype channel, + string name, + string message, + LanguagePrototype language, + int? frequency = null) { // TODO: code duplication with ChatSystem.WrapMessage var speech = _chat.GetSpeechVerb(source, message); @@ -181,6 +215,12 @@ private string WrapRadioMessage(EntityUid source, RadioChannelPrototype channel, : ""; var messageColor = language.IsVisibleLanguage ? languageColor : channel.Color; + string channelText; + if (channel.ShowFrequency && frequency.HasValue) + channelText = $"\\[{frequency}\\]"; + else + channelText = $"\\[{channel.LocalizedName}\\]"; + return Loc.GetString(speech.Bold ? "chat-radio-message-wrap-bold" : "chat-radio-message-wrap", ("color", channel.Color), ("languageColor", languageColor), @@ -188,7 +228,7 @@ private string WrapRadioMessage(EntityUid source, RadioChannelPrototype channel, ("fontType", language.SpeechOverride.FontId ?? speech.FontId), ("fontSize", language.SpeechOverride.FontSize ?? speech.FontSize), ("verb", Loc.GetString(_random.Pick(speech.SpeechVerbStrings))), - ("channel", $"\\[{channel.LocalizedName}\\]"), + ("channel", channelText), ("name", name), ("message", message), ("language", languageDisplay)); diff --git a/Content.Shared/Radio/RadioChannelPrototype.cs b/Content.Shared/Radio/RadioChannelPrototype.cs index cc65f885375..878f41138f9 100644 --- a/Content.Shared/Radio/RadioChannelPrototype.cs +++ b/Content.Shared/Radio/RadioChannelPrototype.cs @@ -35,4 +35,12 @@ public sealed partial class RadioChannelPrototype : IPrototype ///
[DataField("longRange"), ViewVariables] public bool LongRange = false; + + // Frontier: radio channel frequencies + /// + /// If true, the frequency of the message being sent will be appended to the chat message + /// + [DataField, ViewVariables] + public bool ShowFrequency = false; + // End Frontier } diff --git a/Content.Shared/_NC/Radio/SharedHandheldRadio.cs b/Content.Shared/_NC/Radio/SharedHandheldRadio.cs new file mode 100644 index 00000000000..43d45da52dc --- /dev/null +++ b/Content.Shared/_NC/Radio/SharedHandheldRadio.cs @@ -0,0 +1,57 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared._NC.Radio; + +[Serializable, NetSerializable] +public enum HandheldRadioUiKey : byte +{ + Key, +} + +[Serializable, NetSerializable] +public sealed class HandheldRadioBoundUIState : BoundUserInterfaceState +{ + public bool MicEnabled; + public bool SpeakerEnabled; + public int Frequency; + + public HandheldRadioBoundUIState(bool micEnabled, bool speakerEnabled, int frequency) + { + MicEnabled = micEnabled; + SpeakerEnabled = speakerEnabled; + Frequency = frequency; + } +} + +[Serializable, NetSerializable] +public sealed class ToggleHandheldRadioMicMessage : BoundUserInterfaceMessage +{ + public bool Enabled; + + public ToggleHandheldRadioMicMessage(bool enabled) + { + Enabled = enabled; + } +} + +[Serializable, NetSerializable] +public sealed class ToggleHandheldRadioSpeakerMessage : BoundUserInterfaceMessage +{ + public bool Enabled; + + public ToggleHandheldRadioSpeakerMessage(bool enabled) + { + Enabled = enabled; + } +} + +[Serializable, NetSerializable] +public sealed class SelectHandheldRadioFrequencyMessage : BoundUserInterfaceMessage +{ + public int Frequency; + + public SelectHandheldRadioFrequencyMessage(int frequency) + { + Frequency = frequency; + } +} diff --git a/Resources/Locale/en-US/radio/components/handheld-radio-component.ftl b/Resources/Locale/en-US/radio/components/handheld-radio-component.ftl index 39bbf8f968c..48fd1c1829b 100644 --- a/Resources/Locale/en-US/radio/components/handheld-radio-component.ftl +++ b/Resources/Locale/en-US/radio/components/handheld-radio-component.ftl @@ -1,6 +1,14 @@ handheld-radio-component-on-use = The radio is now {$radioState}. -handheld-radio-component-on-examine = It's set to broadcast over the {$frequency} frequency. +handheld-radio-component-on-examine = Frequency: [color={$color}]{$frequency}[/color] handheld-radio-component-on-state = on handheld-radio-component-off-state = off handheld-radio-component-channel-set = Channel set to {$channel} -handheld-radio-component-chennel-examine = The current channel is {$channel}. \ No newline at end of file +handheld-radio-component-channel-examine = Channel: [color={$color}]{$channel}[/color] + +# Nuclear-14-Start +handheld-radio-menu-title = Handheld radio +handheld-radio-current-text-frequency = Broadcast frequency +handheld-radio-button-text-mic = Mic. +handheld-radio-button-text-speaker = Spkr. +handheld-radio-flavor-text-left = HandiComms, 1000-3000 kHz +# Nuclear-14-End \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Devices/radio.yml b/Resources/Prototypes/Entities/Objects/Devices/radio.yml index 77b6cac2d37..67e4597f0b6 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/radio.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/radio.yml @@ -7,7 +7,10 @@ - type: TelecomExempt - type: RadioMicrophone broadcastChannel: Handheld + toggleOnInteract: false + frequency: 1330 - type: RadioSpeaker + toggleOnInteract: false channels: - Handheld - type: Speech @@ -24,6 +27,12 @@ - type: Tag tags: - Radio + - type: ActivatableUI + key: enum.HandheldRadioUiKey.Key + - type: UserInterface + interfaces: + enum.HandheldRadioUiKey.Key: + type: HandheldRadioBoundUserInterface - type: entity name: security radio diff --git a/Resources/Prototypes/radio_channels.yml b/Resources/Prototypes/radio_channels.yml index 48cc0872a1a..b6b65045f19 100644 --- a/Resources/Prototypes/radio_channels.yml +++ b/Resources/Prototypes/radio_channels.yml @@ -77,6 +77,7 @@ color: "#d39f01" # long range since otherwise it'd defeat the point of a handheld radio independent of telecomms longRange: true + showFrequency: true # Frontier - type: radioChannel id: Binary From ae63a452b24c16ff4a1c5ba1655a512cec671b07 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Fri, 17 Jan 2025 03:30:39 +0000 Subject: [PATCH 48/64] Automatic Changelog Update (#1574) --- Resources/Changelog/Changelog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 04c1f40665f..861dabd205c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -10138,3 +10138,12 @@ Entries: id: 6711 time: '2025-01-17T03:16:13.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1560 +- author: sleepyyapril + changes: + - type: Add + message: >- + Added handheld radio frequencies, accessible by pressing your "Use Item" + key while holding one. + id: 6712 + time: '2025-01-17T03:30:13.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1574 From 86f9dfd53ea28d382156b2fd2b999f26330d0de4 Mon Sep 17 00:00:00 2001 From: Piras314 Date: Thu, 16 Jan 2025 22:52:27 -0500 Subject: [PATCH 49/64] Lobby Background Credits (#1558) # Description This PR adds credits for lobby screens, the same as lobby songs have. This works by adding `name` and `artist` to the LobbyBackground prototype and storing the prototype rather than just the path to the texture. ---

Media

![image](https://github.com/user-attachments/assets/6a348060-0ef2-4775-9adb-39a576a1c21e)

--- # Changelog :cl: - add: Added credits for lobby screens. --------- Signed-off-by: VMSolidus Co-authored-by: VMSolidus --- .../GameTicking/Managers/ClientGameTicker.cs | 3 +- Content.Client/Lobby/LobbyState.cs | 34 +++++++++++++++---- Content.Client/Lobby/UI/LobbyGui.xaml | 5 ++- Content.Client/Lobby/UI/LobbyGui.xaml.cs | 1 + .../GameTicking/GameTicker.LobbyBackground.cs | 22 +++++++----- .../Prototypes/LobbyBackgroundPrototype.cs | 12 +++++-- .../GameTicking/SharedGameTicker.cs | 5 +-- .../en-US/_Goobstation/lobby/lobby-state.ftl | 4 +++ Resources/Prototypes/lobbyscreens.yml | 24 ++++++++++++- .../Textures/LobbyScreens/attributions.yml | 2 +- 10 files changed, 90 insertions(+), 22 deletions(-) rename {Content.Server => Content.Shared}/GameTicking/Prototypes/LobbyBackgroundPrototype.cs (68%) create mode 100644 Resources/Locale/en-US/_Goobstation/lobby/lobby-state.ftl diff --git a/Content.Client/GameTicking/Managers/ClientGameTicker.cs b/Content.Client/GameTicking/Managers/ClientGameTicker.cs index 309db2eb4e6..a9e109df8af 100644 --- a/Content.Client/GameTicking/Managers/ClientGameTicker.cs +++ b/Content.Client/GameTicking/Managers/ClientGameTicker.cs @@ -3,6 +3,7 @@ using Content.Client.Lobby; using Content.Client.RoundEnd; using Content.Shared.GameTicking; +using Content.Shared.GameTicking.Prototypes; using Content.Shared.GameWindow; using JetBrains.Annotations; using Robust.Client.Graphics; @@ -26,7 +27,7 @@ public sealed class ClientGameTicker : SharedGameTicker [ViewVariables] public bool AreWeReady { get; private set; } [ViewVariables] public bool IsGameStarted { get; private set; } [ViewVariables] public string? RestartSound { get; private set; } - [ViewVariables] public string? LobbyBackground { get; private set; } + [ViewVariables] public LobbyBackgroundPrototype? LobbyBackground { get; private set; } [ViewVariables] public bool DisallowedLateJoin { get; private set; } [ViewVariables] public string? ServerInfoBlob { get; private set; } [ViewVariables] public TimeSpan StartTime { get; private set; } diff --git a/Content.Client/Lobby/LobbyState.cs b/Content.Client/Lobby/LobbyState.cs index 2e728f552a5..f003189007f 100644 --- a/Content.Client/Lobby/LobbyState.cs +++ b/Content.Client/Lobby/LobbyState.cs @@ -12,7 +12,6 @@ using Robust.Client.UserInterface.Controls; using Robust.Shared.Timing; - namespace Content.Client.Lobby { public sealed class LobbyState : Robust.Client.State.State @@ -25,6 +24,7 @@ public sealed class LobbyState : Robust.Client.State.State [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IVoteManager _voteManager = default!; + private ISawmill _sawmill = default!; private ClientGameTicker _gameTicker = default!; private ContentAudioSystem _contentAudioSystem = default!; @@ -42,6 +42,7 @@ protected override void Startup() _gameTicker = _entityManager.System(); _contentAudioSystem = _entityManager.System(); _contentAudioSystem.LobbySoundtrackChanged += UpdateLobbySoundtrackInfo; + _sawmill = Logger.GetSawmill("lobby"); chatController.SetMainChat(true); @@ -113,7 +114,7 @@ public override void FrameUpdate(FrameEventArgs e) return; } - Lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-not-started"); + Lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-not-started"); string text; if (_gameTicker.Paused) @@ -161,7 +162,7 @@ private void UpdateLobbyUi() else { Lobby!.StartTime.Text = string.Empty; - Lobby!.ReadyButton.Text = Loc.GetString(Lobby!.ReadyButton.Pressed ? "lobby-state-player-status-ready": "lobby-state-player-status-not-ready"); + Lobby!.ReadyButton.Text = Loc.GetString(Lobby!.ReadyButton.Pressed ? "lobby-state-player-status-ready" : "lobby-state-player-status-not-ready"); Lobby!.ReadyButton.ToggleMode = true; Lobby!.ReadyButton.Disabled = false; Lobby!.ReadyButton.Pressed = _gameTicker.AreWeReady; @@ -200,10 +201,31 @@ private void UpdateLobbySoundtrackInfo(LobbySoundtrackChangedEvent ev) private void UpdateLobbyBackground() { if (_gameTicker.LobbyBackground != null) - Lobby!.Background.Texture = _resourceCache.GetResource(_gameTicker.LobbyBackground); - else - Lobby!.Background.Texture = null; + { + Lobby!.Background.Texture = _resourceCache.GetResource(_gameTicker.LobbyBackground.Background); + + var lobbyBackground = _gameTicker.LobbyBackground; + + var name = string.IsNullOrEmpty(lobbyBackground.Name) + ? Loc.GetString("lobby-state-background-unknown-title") + : lobbyBackground.Name; + + var artist = string.IsNullOrEmpty(lobbyBackground.Artist) + ? Loc.GetString("lobby-state-background-unknown-artist") + : lobbyBackground.Artist; + + var markup = Loc.GetString("lobby-state-background-text", + ("backgroundName", name), + ("backgroundArtist", artist)); + + Lobby!.LobbyBackground.SetMarkup(markup); + + return; + } + _sawmill.Warning("_gameTicker.LobbyBackground was null! No lobby background selected."); + Lobby!.Background.Texture = null; + Lobby!.LobbyBackground.SetMarkup(Loc.GetString("lobby-state-background-no-background-text")); } private void SetReady(bool newReady) diff --git a/Content.Client/Lobby/UI/LobbyGui.xaml b/Content.Client/Lobby/UI/LobbyGui.xaml index 4a158fd811f..6f3072a29fe 100644 --- a/Content.Client/Lobby/UI/LobbyGui.xaml +++ b/Content.Client/Lobby/UI/LobbyGui.xaml @@ -50,11 +50,14 @@ - + + + + diff --git a/Content.Client/Lobby/UI/LobbyGui.xaml.cs b/Content.Client/Lobby/UI/LobbyGui.xaml.cs index d19643f20a9..8e18db89932 100644 --- a/Content.Client/Lobby/UI/LobbyGui.xaml.cs +++ b/Content.Client/Lobby/UI/LobbyGui.xaml.cs @@ -22,6 +22,7 @@ public LobbyGui() SetAnchorPreset(Background, LayoutPreset.Wide); LobbySong.SetMarkup(Loc.GetString("lobby-state-song-no-song-text")); + LobbyBackground.SetMarkup(Loc.GetString("lobby-state-background-no-background-text")); LeaveButton.OnPressed += _ => _consoleHost.ExecuteCommand("disconnect"); OptionsButton.OnPressed += _ => UserInterfaceManager.GetUIController().ToggleWindow(); diff --git a/Content.Server/GameTicking/GameTicker.LobbyBackground.cs b/Content.Server/GameTicking/GameTicker.LobbyBackground.cs index 2090e3e31f0..26bd119d44d 100644 --- a/Content.Server/GameTicking/GameTicker.LobbyBackground.cs +++ b/Content.Server/GameTicking/GameTicker.LobbyBackground.cs @@ -1,4 +1,4 @@ -using Content.Server.GameTicking.Prototypes; +using Content.Shared.GameTicking.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; using System.Linq; @@ -8,24 +8,30 @@ namespace Content.Server.GameTicking; public sealed partial class GameTicker { [ViewVariables] - public string? LobbyBackground { get; private set; } + public LobbyBackgroundPrototype? LobbyBackground { get; private set; } [ViewVariables] - private List? _lobbyBackgrounds; + private List _lobbyBackgrounds = []; private static readonly string[] WhitelistedBackgroundExtensions = new string[] {"png", "jpg", "jpeg", "webp"}; private void InitializeLobbyBackground() { - _lobbyBackgrounds = _prototypeManager.EnumeratePrototypes() - .Select(x => x.Background) - .Where(x => WhitelistedBackgroundExtensions.Contains(x.Extension)) - .ToList(); + foreach (var prototype in _prototypeManager.EnumeratePrototypes()) + { + if (!WhitelistedBackgroundExtensions.Contains(prototype.Background.Extension)) + { + _sawmill.Warning($"Lobby background '{prototype.ID}' has an invalid extension '{prototype.Background.Extension}' and will be ignored."); + continue; + } + + _lobbyBackgrounds.Add(prototype); + } RandomizeLobbyBackground(); } private void RandomizeLobbyBackground() { - LobbyBackground = _lobbyBackgrounds!.Any() ? _robustRandom.Pick(_lobbyBackgrounds!).ToString() : null; + LobbyBackground = _lobbyBackgrounds!.Any() ? _robustRandom.Pick(_lobbyBackgrounds!) : null; } } diff --git a/Content.Server/GameTicking/Prototypes/LobbyBackgroundPrototype.cs b/Content.Shared/GameTicking/Prototypes/LobbyBackgroundPrototype.cs similarity index 68% rename from Content.Server/GameTicking/Prototypes/LobbyBackgroundPrototype.cs rename to Content.Shared/GameTicking/Prototypes/LobbyBackgroundPrototype.cs index 4370c22b7ff..16cb9a4e6a5 100644 --- a/Content.Server/GameTicking/Prototypes/LobbyBackgroundPrototype.cs +++ b/Content.Shared/GameTicking/Prototypes/LobbyBackgroundPrototype.cs @@ -1,12 +1,14 @@ -using Robust.Shared.Prototypes; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; using Robust.Shared.Utility; -namespace Content.Server.GameTicking.Prototypes; +namespace Content.Shared.GameTicking.Prototypes; /// /// Prototype for a lobby background the game can choose. /// [Prototype("lobbyBackground")] +[NetSerializable, Serializable] public sealed partial class LobbyBackgroundPrototype : IPrototype { /// @@ -18,4 +20,10 @@ public sealed partial class LobbyBackgroundPrototype : IPrototype /// [DataField("background", required: true)] public ResPath Background = default!; + + [DataField] + public string? Name; + + [DataField] + public string? Artist; } diff --git a/Content.Shared/GameTicking/SharedGameTicker.cs b/Content.Shared/GameTicking/SharedGameTicker.cs index 95da4f4c38d..3154b038f2e 100644 --- a/Content.Shared/GameTicking/SharedGameTicker.cs +++ b/Content.Shared/GameTicking/SharedGameTicker.cs @@ -1,4 +1,5 @@ using Content.Shared.Roles; +using Content.Shared.GameTicking.Prototypes; using Robust.Shared.Network; using Robust.Shared.Replays; using Robust.Shared.Serialization; @@ -78,14 +79,14 @@ public TickerConnectionStatusEvent(TimeSpan roundStartTimeSpan) public sealed class TickerLobbyStatusEvent : EntityEventArgs { public bool IsRoundStarted { get; } - public string? LobbyBackground { get; } + public LobbyBackgroundPrototype? LobbyBackground { get; } public bool YouAreReady { get; } // UTC. public TimeSpan StartTime { get; } public TimeSpan RoundStartTimeSpan { get; } public bool Paused { get; } - public TickerLobbyStatusEvent(bool isRoundStarted, string? lobbyBackground, bool youAreReady, TimeSpan startTime, TimeSpan preloadTime, TimeSpan roundStartTimeSpan, bool paused) + public TickerLobbyStatusEvent(bool isRoundStarted, LobbyBackgroundPrototype? lobbyBackground, bool youAreReady, TimeSpan startTime, TimeSpan preloadTime, TimeSpan roundStartTimeSpan, bool paused) { IsRoundStarted = isRoundStarted; LobbyBackground = lobbyBackground; diff --git a/Resources/Locale/en-US/_Goobstation/lobby/lobby-state.ftl b/Resources/Locale/en-US/_Goobstation/lobby/lobby-state.ftl new file mode 100644 index 00000000000..c92b3cb6ab5 --- /dev/null +++ b/Resources/Locale/en-US/_Goobstation/lobby/lobby-state.ftl @@ -0,0 +1,4 @@ +lobby-state-background-text = Background: [color=white]{$backgroundName}[/color] by [color=white]{$backgroundArtist}[/color] +lobby-state-background-no-background-text = No background selected. +lobby-state-background-unknown-title = [color=dimgray]Unknown lobby background[/color] +lobby-state-background-unknown-artist = [color=dimgray]Unknown artist[/color] diff --git a/Resources/Prototypes/lobbyscreens.yml b/Resources/Prototypes/lobbyscreens.yml index d8c5d283794..358a75930df 100644 --- a/Resources/Prototypes/lobbyscreens.yml +++ b/Resources/Prototypes/lobbyscreens.yml @@ -1,43 +1,65 @@ - type: lobbyBackground id: Robotics background: /Textures/LobbyScreens/robotics.webp + name: Robotics + artist: Veritius - type: lobbyBackground id: Supermatter background: /Textures/LobbyScreens/supermatter.webp + name: Supermatter + artist: Vertitius - type: lobbyBackground id: Warden background: /Textures/LobbyScreens/warden.webp + name: Warden + artist: Solbusaur - type: lobbyBackground id: Pharmacy background: /Textures/LobbyScreens/pharmacy.webp + name: Pharmacy + artist: Solbusaur - type: lobbyBackground id: SSXIV background: /Textures/LobbyScreens/ssxiv.webp + name: SSXIV + artist: Abyssal - type: lobbyBackground id: Susstation background: /Textures/LobbyScreens/susstation.webp + name: Sus Station + artist: Alekshhh - type: lobbyBackground id: SkellyVsTheRev background: /Textures/LobbyScreens/skellyvstherev.webp + name: Skelly Vs The Rev + artist: Hannah 'FairlySadPanda' Dawson - type: lobbyBackground id: Doomed background: /Textures/LobbyScreens/doomed.webp + name: Doomed + artist: brainfood1183 - type: lobbyBackground id: Blueprint background: /Textures/LobbyScreens/blueprint.webp + name: Blueprint + artist: data_redacted - type: lobbyBackground id: TerminalStation background: /Textures/LobbyScreens/terminalstation.webp + name: Terminal Station + artist: aserovich - type: lobbyBackground id: JustAWeekAway - background: /Textures/LobbyScreens/justaweekaway.webp \ No newline at end of file + background: /Textures/LobbyScreens/justaweekaway.webp + name: Just A Week Away + artist: plantyfern diff --git a/Resources/Textures/LobbyScreens/attributions.yml b/Resources/Textures/LobbyScreens/attributions.yml index 4dd3dc945bf..0429884ca05 100644 --- a/Resources/Textures/LobbyScreens/attributions.yml +++ b/Resources/Textures/LobbyScreens/attributions.yml @@ -41,4 +41,4 @@ - files: ["justaweekaway.webp"] license: "CC-BY-SA-3.0" copyright: "plantyfern on discord" - source: "https://github.com/space-wizards/space-station-14" \ No newline at end of file + source: "https://github.com/space-wizards/space-station-14" From b1162a325bb84dda50f63ea70f97cbed398fb497 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Fri, 17 Jan 2025 03:52:51 +0000 Subject: [PATCH 50/64] Automatic Changelog Update (#1558) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 861dabd205c..651ea5d7c85 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -10147,3 +10147,10 @@ Entries: id: 6712 time: '2025-01-17T03:30:13.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1574 +- author: Piras314 + changes: + - type: Add + message: Added credits for lobby screens. + id: 6713 + time: '2025-01-17T03:52:28.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1558 From a9f7fe8e9a337d13ef34ec643064432d0cec2b60 Mon Sep 17 00:00:00 2001 From: sleepyyapril <123355664+sleepyyapril@users.noreply.github.com> Date: Thu, 16 Jan 2025 23:53:49 -0400 Subject: [PATCH 51/64] Downstream Port (#1509) # Changelog :cl: sleepyyapril, Stop-Signs, Memeji - add: Changed the Warden suit's capabilities. (Original author: Stop-Signs) - add: Repeater, Argenti, and 45 magnum rubber box to Bartender's loadout. - add: Red cloak to loadout (Port from Floof PR #390) - add: Witch Robes to loadout (Port from Floof PR #390) - add: Sawed-off PKA (Port from Floof PR #390) - add: The ChemMaster now has 20, 75, and 80 unit options available. - tweak: Made the ChemMaster wider. - fix: Ported Frontier fix to mag visuals on specific guns. - fix: Fixed heirlooms flickering negative mood when the moodlet ended. --------- Signed-off-by: Stop-Signs Signed-off-by: sleepyyapril <123355664+sleepyyapril@users.noreply.github.com> Co-authored-by: Stop-Signs Co-authored-by: FoxxoTrystan <45297731+FoxxoTrystan@users.noreply.github.com> Co-authored-by: Radezolid Co-authored-by: ErhardSteinhauer <65374927+ErhardSteinhauer@users.noreply.github.com> Co-authored-by: VMSolidus --- .../Chemistry/UI/ChemMasterWindow.xaml | 16 +++---- .../Chemistry/UI/ChemMasterWindow.xaml.cs | 5 ++- .../EntitySystems/ConditionalSpawnerSystem.cs | 14 +++++- .../Traits/Assorted/HeirloomSystem.cs | 11 +++-- Content.Shared/Chemistry/SharedChemMaster.cs | 3 ++ .../VendingMachines/Inventories/theater.yml | 3 ++ .../Jobs/Service/bartender.yml | 4 ++ .../Clothing/OuterClothing/hardsuits.yml | 8 ++-- .../Entities/Clothing/Head/hoods.yml | 17 +++++++ .../Entities/Clothing/Neck/cloaks.yml | 16 +++++++ .../Entities/Clothing/OuterClothing/suits.yml | 11 +++++ .../Objects/Weapons/Guns/Basic/pka.yml | 7 +++ .../Loadouts/Jobs/Service/bartender.yml | 42 ++++++++++++++++++ .../Mood/genericNegativeEffects.yml | 5 +++ .../Mood/genericPositiveEffects.yml | 2 +- .../Entities/Clothing/Belt/belts.yml | 2 + .../Entities/Clothing/Head/hats.yml | 3 ++ .../Entities/Objects/Weapons/Melee/knives.yml | 3 ++ .../_NF/Entities/Clothing/Neck/cloaks.yml | 10 +++++ .../Objects/Weapons/Guns/Basic/sawn_pka.yml | 27 +++++++++++ .../Objects/Weapons/Guns/Rifles/rifles.yml | 15 ++++--- .../Objects/Weapons/Guns/Snipers/snipers.yml | 31 +++++++++++++ .../Construction/Graphs/weapons/pkasawn.yml | 12 +++++ .../Hoods/redhood.rsi/equipped-HELMET.png | Bin 0 -> 682 bytes .../Clothing/Head/Hoods/redhood.rsi/icon.png | Bin 0 -> 235 bytes .../Clothing/Head/Hoods/redhood.rsi/meta.json | 18 ++++++++ .../Neck/Cloaks/redhood.rsi/equipped-NECK.png | Bin 0 -> 1077 bytes .../Clothing/Neck/Cloaks/redhood.rsi/icon.png | Bin 0 -> 392 bytes .../Neck/Cloaks/redhood.rsi/inhand-left.png | Bin 0 -> 821 bytes .../Neck/Cloaks/redhood.rsi/inhand-right.png | Bin 0 -> 784 bytes .../Neck/Cloaks/redhood.rsi/meta.json | 26 +++++++++++ .../witchrobe.rsi/equipped-OUTERCLOTHING.png | Bin 0 -> 612 bytes .../Suits/witchrobe.rsi/icon.png | Bin 0 -> 330 bytes .../Suits/witchrobe.rsi/inhand-left.png | Bin 0 -> 725 bytes .../Suits/witchrobe.rsi/inhand-right.png | Bin 0 -> 730 bytes .../Suits/witchrobe.rsi/meta.json | 26 +++++++++++ .../Neck/Cloaks/janitor.rsi/equipped-NECK.png | Bin 0 -> 1247 bytes .../Clothing/Neck/Cloaks/janitor.rsi/icon.png | Bin 0 -> 515 bytes .../Neck/Cloaks/janitor.rsi/inhand-left.png | Bin 0 -> 755 bytes .../Neck/Cloaks/janitor.rsi/inhand-right.png | Bin 0 -> 799 bytes .../Neck/Cloaks/janitor.rsi/meta.json | 33 ++++++++++++++ .../janitor.rsi/plunger-equipped-NECK.png | Bin 0 -> 225 bytes .../Neck/Cloaks/janitor.rsi/plunger.png | Bin 0 -> 193 bytes .../equipped-BELT.png | Bin 0 -> 8773 bytes .../sawn_kinetic_accelerator.rsi/icon.png | Bin 0 -> 7007 bytes .../inhand-left.png | Bin 0 -> 9405 bytes .../inhand-right.png | Bin 0 -> 9434 bytes .../sawn_kinetic_accelerator.rsi/meta.json | 26 +++++++++++ .../Guns/Snipers/repeater.rsi/base.png | Bin 0 -> 4994 bytes .../Guns/Snipers/repeater.rsi/bolt-open.png | Bin 0 -> 4766 bytes .../repeater.rsi/equipped-BACKPACK.png | Bin 0 -> 1294 bytes .../repeater.rsi/equipped-SUITSTORAGE.png | Bin 0 -> 1294 bytes .../Guns/Snipers/repeater.rsi/inhand-left.png | Bin 0 -> 4826 bytes .../Snipers/repeater.rsi/inhand-right.png | Bin 0 -> 4800 bytes .../Guns/Snipers/repeater.rsi/meta.json | 41 +++++++++++++++++ .../repeater.rsi/wielded-inhand-left.png | Bin 0 -> 7348 bytes .../repeater.rsi/wielded-inhand-right.png | Bin 0 -> 7293 bytes 57 files changed, 410 insertions(+), 27 deletions(-) create mode 100644 Resources/Prototypes/_NF/Entities/Clothing/Neck/cloaks.yml create mode 100644 Resources/Prototypes/_NF/Entities/Objects/Weapons/Guns/Basic/sawn_pka.yml create mode 100644 Resources/Prototypes/_NF/Entities/Objects/Weapons/Guns/Snipers/snipers.yml create mode 100644 Resources/Prototypes/_NF/Recipes/Construction/Graphs/weapons/pkasawn.yml create mode 100644 Resources/Textures/Clothing/Head/Hoods/redhood.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Clothing/Head/Hoods/redhood.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Head/Hoods/redhood.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Neck/Cloaks/redhood.rsi/equipped-NECK.png create mode 100644 Resources/Textures/Clothing/Neck/Cloaks/redhood.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Neck/Cloaks/redhood.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Neck/Cloaks/redhood.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Neck/Cloaks/redhood.rsi/meta.json create mode 100644 Resources/Textures/Clothing/OuterClothing/Suits/witchrobe.rsi/equipped-OUTERCLOTHING.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Suits/witchrobe.rsi/icon.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Suits/witchrobe.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Suits/witchrobe.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Suits/witchrobe.rsi/meta.json create mode 100644 Resources/Textures/_NF/Clothing/Neck/Cloaks/janitor.rsi/equipped-NECK.png create mode 100644 Resources/Textures/_NF/Clothing/Neck/Cloaks/janitor.rsi/icon.png create mode 100644 Resources/Textures/_NF/Clothing/Neck/Cloaks/janitor.rsi/inhand-left.png create mode 100644 Resources/Textures/_NF/Clothing/Neck/Cloaks/janitor.rsi/inhand-right.png create mode 100644 Resources/Textures/_NF/Clothing/Neck/Cloaks/janitor.rsi/meta.json create mode 100644 Resources/Textures/_NF/Clothing/Neck/Cloaks/janitor.rsi/plunger-equipped-NECK.png create mode 100644 Resources/Textures/_NF/Clothing/Neck/Cloaks/janitor.rsi/plunger.png create mode 100644 Resources/Textures/_NF/Objects/Weapons/Guns/Basic/sawn_kinetic_accelerator.rsi/equipped-BELT.png create mode 100644 Resources/Textures/_NF/Objects/Weapons/Guns/Basic/sawn_kinetic_accelerator.rsi/icon.png create mode 100644 Resources/Textures/_NF/Objects/Weapons/Guns/Basic/sawn_kinetic_accelerator.rsi/inhand-left.png create mode 100644 Resources/Textures/_NF/Objects/Weapons/Guns/Basic/sawn_kinetic_accelerator.rsi/inhand-right.png create mode 100644 Resources/Textures/_NF/Objects/Weapons/Guns/Basic/sawn_kinetic_accelerator.rsi/meta.json create mode 100644 Resources/Textures/_NF/Objects/Weapons/Guns/Snipers/repeater.rsi/base.png create mode 100644 Resources/Textures/_NF/Objects/Weapons/Guns/Snipers/repeater.rsi/bolt-open.png create mode 100644 Resources/Textures/_NF/Objects/Weapons/Guns/Snipers/repeater.rsi/equipped-BACKPACK.png create mode 100644 Resources/Textures/_NF/Objects/Weapons/Guns/Snipers/repeater.rsi/equipped-SUITSTORAGE.png create mode 100644 Resources/Textures/_NF/Objects/Weapons/Guns/Snipers/repeater.rsi/inhand-left.png create mode 100644 Resources/Textures/_NF/Objects/Weapons/Guns/Snipers/repeater.rsi/inhand-right.png create mode 100644 Resources/Textures/_NF/Objects/Weapons/Guns/Snipers/repeater.rsi/meta.json create mode 100644 Resources/Textures/_NF/Objects/Weapons/Guns/Snipers/repeater.rsi/wielded-inhand-left.png create mode 100644 Resources/Textures/_NF/Objects/Weapons/Guns/Snipers/repeater.rsi/wielded-inhand-right.png diff --git a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml index b1f4f5917f4..61bbba3c35f 100644 --- a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml +++ b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml @@ -2,7 +2,7 @@ xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client" - MinSize="620 670" + MinSize="620 770" Title="{Loc 'chem-master-bound-user-interface-title'}"> @@ -13,12 +13,12 @@