diff --git a/Content.Client/Guidebook/GuidebookDataSystem.cs b/Content.Client/Guidebook/GuidebookDataSystem.cs new file mode 100644 index 000000000000..f47ad6ef1bb2 --- /dev/null +++ b/Content.Client/Guidebook/GuidebookDataSystem.cs @@ -0,0 +1,45 @@ +using Content.Shared.Guidebook; + +namespace Content.Client.Guidebook; + +/// +/// Client system for storing and retrieving values extracted from entity prototypes +/// for display in the guidebook (). +/// Requests data from the server on . +/// Can also be pushed new data when the server reloads prototypes. +/// +public sealed class GuidebookDataSystem : EntitySystem +{ + private GuidebookData? _data; + + public override void Initialize() + { + base.Initialize(); + + SubscribeNetworkEvent(OnServerUpdated); + + // Request data from the server + RaiseNetworkEvent(new RequestGuidebookDataEvent()); + } + + private void OnServerUpdated(UpdateGuidebookDataEvent args) + { + // Got new data from the server, either in response to our request, or because prototypes reloaded on the server + _data = args.Data; + _data.Freeze(); + } + + /// + /// Attempts to retrieve a value using the given identifiers. + /// See for more information. + /// + public bool TryGetValue(string prototype, string component, string field, out object? value) + { + if (_data == null) + { + value = null; + return false; + } + return _data.TryGetValue(prototype, component, field, out value); + } +} diff --git a/Content.Client/Guidebook/Richtext/ProtodataTag.cs b/Content.Client/Guidebook/Richtext/ProtodataTag.cs new file mode 100644 index 000000000000..a725fd4e4b59 --- /dev/null +++ b/Content.Client/Guidebook/Richtext/ProtodataTag.cs @@ -0,0 +1,49 @@ +using System.Globalization; +using Robust.Client.UserInterface.RichText; +using Robust.Shared.Utility; + +namespace Content.Client.Guidebook.RichText; + +/// +/// RichText tag that can display values extracted from entity prototypes. +/// In order to be accessed by this tag, the desired field/property must +/// be tagged with . +/// +public sealed class ProtodataTag : IMarkupTag +{ + [Dependency] private readonly ILogManager _logMan = default!; + [Dependency] private readonly IEntityManager _entMan = default!; + + public string Name => "protodata"; + private ISawmill Log => _log ??= _logMan.GetSawmill("protodata_tag"); + private ISawmill? _log; + + public string TextBefore(MarkupNode node) + { + // Do nothing with an empty tag + if (!node.Value.TryGetString(out var prototype)) + return string.Empty; + + if (!node.Attributes.TryGetValue("comp", out var component)) + return string.Empty; + if (!node.Attributes.TryGetValue("member", out var member)) + return string.Empty; + node.Attributes.TryGetValue("format", out var format); + + var guidebookData = _entMan.System(); + + // Try to get the value + if (!guidebookData.TryGetValue(prototype, component.StringValue!, member.StringValue!, out var value)) + { + Log.Error($"Failed to find protodata for {component}.{member} in {prototype}"); + return "???"; + } + + // If we have a format string and a formattable value, format it as requested + if (!string.IsNullOrEmpty(format.StringValue) && value is IFormattable formattable) + return formattable.ToString(format.StringValue, CultureInfo.CurrentCulture); + + // No format string given, so just use default ToString + return value?.ToString() ?? "NULL"; + } +} diff --git a/Content.Client/PDA/PdaBoundUserInterface.cs b/Content.Client/PDA/PdaBoundUserInterface.cs index 37ce9c4280f2..2d4033390c3a 100644 --- a/Content.Client/PDA/PdaBoundUserInterface.cs +++ b/Content.Client/PDA/PdaBoundUserInterface.cs @@ -4,18 +4,20 @@ using Content.Shared.PDA; using JetBrains.Annotations; using Robust.Client.UserInterface; -using Robust.Shared.Configuration; namespace Content.Client.PDA { [UsedImplicitly] public sealed class PdaBoundUserInterface : CartridgeLoaderBoundUserInterface { + private readonly PdaSystem _pdaSystem; + [ViewVariables] private PdaMenu? _menu; public PdaBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { + _pdaSystem = EntMan.System(); } protected override void Open() @@ -92,7 +94,13 @@ protected override void UpdateState(BoundUserInterfaceState state) if (state is not PdaUpdateState updateState) return; - _menu?.UpdateState(updateState); + if (_menu == null) + { + _pdaSystem.Log.Error("PDA state received before menu was created."); + return; + } + + _menu.UpdateState(updateState); } protected override void AttachCartridgeUI(Control cartridgeUIFragment, string? title) diff --git a/Content.Client/PDA/PdaMenu.xaml b/Content.Client/PDA/PdaMenu.xaml index 8b26860332dd..8c9b4ae2ee6b 100644 --- a/Content.Client/PDA/PdaMenu.xaml +++ b/Content.Client/PDA/PdaMenu.xaml @@ -67,14 +67,17 @@ Description="{Loc 'comp-pda-ui-ringtone-button-description'}"/> diff --git a/Content.Client/Physics/Controllers/MoverController.cs b/Content.Client/Physics/Controllers/MoverController.cs index 03df383eebce..c97110b208e5 100644 --- a/Content.Client/Physics/Controllers/MoverController.cs +++ b/Content.Client/Physics/Controllers/MoverController.cs @@ -8,132 +8,131 @@ using Robust.Shared.Player; using Robust.Shared.Timing; -namespace Content.Client.Physics.Controllers +namespace Content.Client.Physics.Controllers; + +public sealed class MoverController : SharedMoverController { - public sealed class MoverController : SharedMoverController - { - [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnRelayPlayerAttached); - SubscribeLocalEvent(OnRelayPlayerDetached); - SubscribeLocalEvent(OnPlayerAttached); - SubscribeLocalEvent(OnPlayerDetached); - - SubscribeLocalEvent(OnUpdatePredicted); - SubscribeLocalEvent(OnUpdateRelayTargetPredicted); - SubscribeLocalEvent(OnUpdatePullablePredicted); - } + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnRelayPlayerAttached); + SubscribeLocalEvent(OnRelayPlayerDetached); + SubscribeLocalEvent(OnPlayerAttached); + SubscribeLocalEvent(OnPlayerDetached); + + SubscribeLocalEvent(OnUpdatePredicted); + SubscribeLocalEvent(OnUpdateRelayTargetPredicted); + SubscribeLocalEvent(OnUpdatePullablePredicted); + } - private void OnUpdatePredicted(Entity entity, ref UpdateIsPredictedEvent args) - { - // Enable prediction if an entity is controlled by the player - if (entity.Owner == _playerManager.LocalEntity) - args.IsPredicted = true; - } + private void OnUpdatePredicted(Entity entity, ref UpdateIsPredictedEvent args) + { + // Enable prediction if an entity is controlled by the player + if (entity.Owner == _playerManager.LocalEntity) + args.IsPredicted = true; + } - private void OnUpdateRelayTargetPredicted(Entity entity, ref UpdateIsPredictedEvent args) - { - if (entity.Comp.Source == _playerManager.LocalEntity) - args.IsPredicted = true; - } + private void OnUpdateRelayTargetPredicted(Entity entity, ref UpdateIsPredictedEvent args) + { + if (entity.Comp.Source == _playerManager.LocalEntity) + args.IsPredicted = true; + } - private void OnUpdatePullablePredicted(Entity entity, ref UpdateIsPredictedEvent args) - { - // Enable prediction if an entity is being pulled by the player. - // Disable prediction if an entity is being pulled by some non-player entity. + private void OnUpdatePullablePredicted(Entity entity, ref UpdateIsPredictedEvent args) + { + // Enable prediction if an entity is being pulled by the player. + // Disable prediction if an entity is being pulled by some non-player entity. - if (entity.Comp.Puller == _playerManager.LocalEntity) - args.IsPredicted = true; - else if (entity.Comp.Puller != null) - args.BlockPrediction = true; + if (entity.Comp.Puller == _playerManager.LocalEntity) + args.IsPredicted = true; + else if (entity.Comp.Puller != null) + args.BlockPrediction = true; - // TODO recursive pulling checks? - // What if the entity is being pulled by a vehicle controlled by the player? - } + // TODO recursive pulling checks? + // What if the entity is being pulled by a vehicle controlled by the player? + } - private void OnRelayPlayerAttached(Entity entity, ref LocalPlayerAttachedEvent args) - { - Physics.UpdateIsPredicted(entity.Owner); - Physics.UpdateIsPredicted(entity.Comp.RelayEntity); - if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover)) - SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None); - } + private void OnRelayPlayerAttached(Entity entity, ref LocalPlayerAttachedEvent args) + { + Physics.UpdateIsPredicted(entity.Owner); + Physics.UpdateIsPredicted(entity.Comp.RelayEntity); + if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover)) + SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None); + } - private void OnRelayPlayerDetached(Entity entity, ref LocalPlayerDetachedEvent args) - { - Physics.UpdateIsPredicted(entity.Owner); - Physics.UpdateIsPredicted(entity.Comp.RelayEntity); - if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover)) - SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None); - } + private void OnRelayPlayerDetached(Entity entity, ref LocalPlayerDetachedEvent args) + { + Physics.UpdateIsPredicted(entity.Owner); + Physics.UpdateIsPredicted(entity.Comp.RelayEntity); + if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover)) + SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None); + } - private void OnPlayerAttached(Entity entity, ref LocalPlayerAttachedEvent args) - { - SetMoveInput(entity, MoveButtons.None); - } + private void OnPlayerAttached(Entity entity, ref LocalPlayerAttachedEvent args) + { + SetMoveInput(entity, MoveButtons.None); + } - private void OnPlayerDetached(Entity entity, ref LocalPlayerDetachedEvent args) - { - SetMoveInput(entity, MoveButtons.None); - } + private void OnPlayerDetached(Entity entity, ref LocalPlayerDetachedEvent args) + { + SetMoveInput(entity, MoveButtons.None); + } - public override void UpdateBeforeSolve(bool prediction, float frameTime) - { - base.UpdateBeforeSolve(prediction, frameTime); + public override void UpdateBeforeSolve(bool prediction, float frameTime) + { + base.UpdateBeforeSolve(prediction, frameTime); - if (_playerManager.LocalEntity is not {Valid: true} player) - return; + if (_playerManager.LocalEntity is not {Valid: true} player) + return; - if (RelayQuery.TryGetComponent(player, out var relayMover)) - HandleClientsideMovement(relayMover.RelayEntity, frameTime); + if (RelayQuery.TryGetComponent(player, out var relayMover)) + HandleClientsideMovement(relayMover.RelayEntity, frameTime); - HandleClientsideMovement(player, frameTime); - } + HandleClientsideMovement(player, frameTime); + } - private void HandleClientsideMovement(EntityUid player, float frameTime) + private void HandleClientsideMovement(EntityUid player, float frameTime) + { + if (!MoverQuery.TryGetComponent(player, out var mover) || + !XformQuery.TryGetComponent(player, out var xform)) { - if (!MoverQuery.TryGetComponent(player, out var mover) || - !XformQuery.TryGetComponent(player, out var xform)) - { - return; - } - - var physicsUid = player; - PhysicsComponent? body; - var xformMover = xform; + return; + } - if (mover.ToParent && RelayQuery.HasComponent(xform.ParentUid)) - { - if (!PhysicsQuery.TryGetComponent(xform.ParentUid, out body) || - !XformQuery.TryGetComponent(xform.ParentUid, out xformMover)) - { - return; - } + var physicsUid = player; + PhysicsComponent? body; + var xformMover = xform; - physicsUid = xform.ParentUid; - } - else if (!PhysicsQuery.TryGetComponent(player, out body)) + if (mover.ToParent && RelayQuery.HasComponent(xform.ParentUid)) + { + if (!PhysicsQuery.TryGetComponent(xform.ParentUid, out body) || + !XformQuery.TryGetComponent(xform.ParentUid, out xformMover)) { return; } - // Server-side should just be handled on its own so we'll just do this shizznit - HandleMobMovement( - player, - mover, - physicsUid, - body, - xformMover, - frameTime); + physicsUid = xform.ParentUid; } - - protected override bool CanSound() + else if (!PhysicsQuery.TryGetComponent(player, out body)) { - return _timing is { IsFirstTimePredicted: true, InSimulation: true }; + return; } + + // Server-side should just be handled on its own so we'll just do this shizznit + HandleMobMovement( + player, + mover, + physicsUid, + body, + xformMover, + frameTime); + } + + protected override bool CanSound() + { + return _timing is { IsFirstTimePredicted: true, InSimulation: true }; } } diff --git a/Content.Client/Power/ActivatableUIRequiresPowerSystem.cs b/Content.Client/Power/ActivatableUIRequiresPowerSystem.cs index 5a082485a5ac..a6a20958f536 100644 --- a/Content.Client/Power/ActivatableUIRequiresPowerSystem.cs +++ b/Content.Client/Power/ActivatableUIRequiresPowerSystem.cs @@ -18,9 +18,6 @@ protected override void OnActivate(Entity e return; } - if (TryComp(ent.Owner, out var panel) && panel.Open) - return; - _popup.PopupClient(Loc.GetString("base-computer-ui-component-not-powered", ("machine", ent.Owner)), args.User, args.User); args.Cancel(); } diff --git a/Content.Client/Replay/ContentReplayPlaybackManager.cs b/Content.Client/Replay/ContentReplayPlaybackManager.cs index f90731bfa753..b96eae44e9d5 100644 --- a/Content.Client/Replay/ContentReplayPlaybackManager.cs +++ b/Content.Client/Replay/ContentReplayPlaybackManager.cs @@ -1,10 +1,8 @@ -using System.IO.Compression; using Content.Client.Administration.Managers; using Content.Client.Launcher; using Content.Client.MainMenu; using Content.Client.Replay.Spectator; using Content.Client.Replay.UI.Loading; -using Content.Client.Stylesheets; using Content.Client.UserInterface.Systems.Chat; using Content.Shared.Chat; using Content.Shared.Effects; @@ -26,8 +24,6 @@ using Robust.Client.State; using Robust.Client.Timing; using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controls; -using Robust.Client.UserInterface.CustomControls; using Robust.Shared; using Robust.Shared.Configuration; using Robust.Shared.ContentPack; @@ -60,7 +56,7 @@ public sealed class ContentReplayPlaybackManager public bool IsScreenshotMode = false; private bool _initialized; - + /// /// Most recently loaded file, for re-attempting the load with error tolerance. /// Required because the zip reader auto-disposes and I'm too lazy to change it so that @@ -96,32 +92,17 @@ private void OnFinishedLoading(Exception? exception) return; } - ReturnToDefaultState(); - - // Show a popup window with the error message - var text = Loc.GetString("replay-loading-failed", ("reason", exception)); - var box = new BoxContainer - { - Orientation = BoxContainer.LayoutOrientation.Vertical, - Children = {new Label {Text = text}} - }; + if (_client.RunLevel == ClientRunLevel.SinglePlayerGame) + _client.StopSinglePlayer(); - var popup = new DefaultWindow { Title = "Error!" }; - popup.Contents.AddChild(box); + Action? retryAction = null; + Action? cancelAction = null; - // Add button for attempting to re-load the replay while ignoring some errors. - if (!_cfg.GetCVar(CVars.ReplayIgnoreErrors) && LastLoad is {} last) + if (!_cfg.GetCVar(CVars.ReplayIgnoreErrors) && LastLoad is { } last) { - var button = new Button - { - Text = Loc.GetString("replay-loading-retry"), - StyleClasses = { StyleBase.ButtonCaution } - }; - - button.OnPressed += _ => + retryAction = () => { _cfg.SetCVar(CVars.ReplayIgnoreErrors, true); - popup.Dispose(); IReplayFileReader reader = last.Zip == null ? new ReplayFileReaderResources(_resMan, last.Folder) @@ -129,11 +110,20 @@ private void OnFinishedLoading(Exception? exception) _loadMan.LoadAndStartReplay(reader); }; - - box.AddChild(button); } - popup.OpenCentered(); + // If we have an explicit menu to get back to (e.g. replay browser UI), show a cancel button. + if (DefaultState != null) + { + cancelAction = () => + { + _stateMan.RequestStateChange(DefaultState); + }; + } + + // Switch to a new game state to present the error and cancel/retry options. + var state = _stateMan.RequestStateChange(); + state.SetData(exception, cancelAction, retryAction); } public void ReturnToDefaultState() diff --git a/Content.Client/Replay/UI/Loading/ReplayLoadingFailed.cs b/Content.Client/Replay/UI/Loading/ReplayLoadingFailed.cs new file mode 100644 index 000000000000..223895eb29cb --- /dev/null +++ b/Content.Client/Replay/UI/Loading/ReplayLoadingFailed.cs @@ -0,0 +1,36 @@ +using Content.Client.Stylesheets; +using Robust.Client.State; +using Robust.Client.UserInterface; +using Robust.Shared.Utility; + +namespace Content.Client.Replay.UI.Loading; + +/// +/// State used to display an error message if a replay failed to load. +/// +/// +/// +public sealed class ReplayLoadingFailed : State +{ + [Dependency] private readonly IStylesheetManager _stylesheetManager = default!; + [Dependency] private readonly IUserInterfaceManager _userInterface = default!; + + private ReplayLoadingFailedControl? _control; + + public void SetData(Exception exception, Action? cancelPressed, Action? retryPressed) + { + DebugTools.Assert(_control != null); + _control.SetData(exception, cancelPressed, retryPressed); + } + + protected override void Startup() + { + _control = new ReplayLoadingFailedControl(_stylesheetManager); + _userInterface.StateRoot.AddChild(_control); + } + + protected override void Shutdown() + { + _control?.Orphan(); + } +} diff --git a/Content.Client/Replay/UI/Loading/ReplayLoadingFailedControl.xaml b/Content.Client/Replay/UI/Loading/ReplayLoadingFailedControl.xaml new file mode 100644 index 000000000000..5f77a66e535e --- /dev/null +++ b/Content.Client/Replay/UI/Loading/ReplayLoadingFailedControl.xaml @@ -0,0 +1,14 @@ + + + + + + + + + +public sealed partial class Glow : EntityEffect +{ + [DataField] + public float Radius = 2f; + + [DataField] + public Color Color = Color.Black; + + private static readonly List Colors = new() + { + Color.White, + Color.Red, + Color.Yellow, + Color.Green, + Color.Blue, + Color.Purple, + Color.Pink + }; + + public override void Effect(EntityEffectBaseArgs args) + { + if (Color == Color.Black) + { + var random = IoCManager.Resolve(); + Color = random.Pick(Colors); + } + + var lightSystem = args.EntityManager.System(); + var light = lightSystem.EnsureLight(args.TargetEntity); + lightSystem.SetRadius(args.TargetEntity, Radius, light); + lightSystem.SetColor(args.TargetEntity, Color, light); + lightSystem.SetCastShadows(args.TargetEntity, false, light); // this is expensive, and botanists make lots of plants + } + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return "TODO"; + } +} diff --git a/Content.Server/EntityEffects/Effects/PlantChangeStat.cs b/Content.Server/EntityEffects/Effects/PlantChangeStat.cs new file mode 100644 index 000000000000..9592ff779daa --- /dev/null +++ b/Content.Server/EntityEffects/Effects/PlantChangeStat.cs @@ -0,0 +1,142 @@ +using Content.Server.Botany; +using Content.Server.Botany.Components; +using Content.Shared.EntityEffects; +using JetBrains.Annotations; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server.EntityEffects.Effects.PlantMetabolism; + +[UsedImplicitly] +public sealed partial class PlantChangeStat : EntityEffect +{ + [DataField] + public string TargetValue; + + [DataField] + public float MinValue; + + [DataField] + public float MaxValue; + + [DataField] + public int Steps; + + public override void Effect(EntityEffectBaseArgs args) + { + var plantHolder = args.EntityManager.GetComponent(args.TargetEntity); + if (plantHolder == null || plantHolder.Seed == null) + return; + + var member = plantHolder.Seed.GetType().GetField(TargetValue); + var mutationSys = args.EntityManager.System(); + + if (member == null) + { + mutationSys.Log.Error(this.GetType().Name + " Error: Member " + TargetValue + " not found on " + plantHolder.GetType().Name + ". Did you misspell it?"); + return; + } + + var currentValObj = member.GetValue(plantHolder.Seed); + if (currentValObj == null) + return; + + if (member.FieldType == typeof(float)) + { + var floatVal = (float)currentValObj; + MutateFloat(ref floatVal, MinValue, MaxValue, Steps); + member.SetValue(plantHolder.Seed, floatVal); + } + else if (member.FieldType == typeof(int)) + { + var intVal = (int)currentValObj; + MutateInt(ref intVal, (int)MinValue, (int)MaxValue, Steps); + member.SetValue(plantHolder.Seed, intVal); + } + else if (member.FieldType == typeof(bool)) + { + var boolVal = (bool)currentValObj; + boolVal = !boolVal; + member.SetValue(plantHolder.Seed, boolVal); + } + } + + // Mutate reference 'val' between 'min' and 'max' by pretending the value + // is representable by a thermometer code with 'bits' number of bits and + // randomly flipping some of them. + private void MutateFloat(ref float val, float min, float max, int bits) + { + if (min == max) + { + val = min; + return; + } + + // Starting number of bits that are high, between 0 and bits. + // In other words, it's val mapped linearly from range [min, max] to range [0, bits], and then rounded. + int valInt = (int)MathF.Round((val - min) / (max - min) * bits); + // val may be outside the range of min/max due to starting prototype values, so clamp. + valInt = Math.Clamp(valInt, 0, bits); + + // Probability that the bit flip increases n. + // The higher the current value is, the lower the probability of increasing value is, and the higher the probability of decreasive it it. + // In other words, it tends to go to the middle. + float probIncrease = 1 - (float)valInt / bits; + int valIntMutated; + if (Random(probIncrease)) + { + valIntMutated = valInt + 1; + } + else + { + valIntMutated = valInt - 1; + } + + // Set value based on mutated thermometer code. + float valMutated = Math.Clamp((float)valIntMutated / bits * (max - min) + min, min, max); + val = valMutated; + } + + private void MutateInt(ref int val, int min, int max, int bits) + { + if (min == max) + { + val = min; + return; + } + + // Starting number of bits that are high, between 0 and bits. + // In other words, it's val mapped linearly from range [min, max] to range [0, bits], and then rounded. + int valInt = (int)MathF.Round((val - min) / (max - min) * bits); + // val may be outside the range of min/max due to starting prototype values, so clamp. + valInt = Math.Clamp(valInt, 0, bits); + + // Probability that the bit flip increases n. + // The higher the current value is, the lower the probability of increasing value is, and the higher the probability of decreasing it. + // In other words, it tends to go to the middle. + float probIncrease = 1 - (float)valInt / bits; + int valMutated; + if (Random(probIncrease)) + { + valMutated = val + 1; + } + else + { + valMutated = val - 1; + } + + valMutated = Math.Clamp(valMutated, min, max); + val = valMutated; + } + + private bool Random(float odds) + { + var random = IoCManager.Resolve(); + return random.Prob(odds); + } + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + throw new NotImplementedException(); + } +} diff --git a/Content.Server/EntityEffects/Effects/PlantMutateChemicals.cs b/Content.Server/EntityEffects/Effects/PlantMutateChemicals.cs new file mode 100644 index 000000000000..7ee6cd13d758 --- /dev/null +++ b/Content.Server/EntityEffects/Effects/PlantMutateChemicals.cs @@ -0,0 +1,55 @@ +using Content.Server.Botany; +using Content.Server.Botany.Components; +using Content.Shared.EntityEffects; +using Content.Shared.Random; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server.EntityEffects.Effects; + +/// +/// changes the chemicals available in a plant's produce +/// +public sealed partial class PlantMutateChemicals : EntityEffect +{ + public override void Effect(EntityEffectBaseArgs args) + { + var plantholder = args.EntityManager.GetComponent(args.TargetEntity); + + if (plantholder.Seed == null) + return; + + var random = IoCManager.Resolve(); + var prototypeManager = IoCManager.Resolve(); + var chemicals = plantholder.Seed.Chemicals; + var randomChems = prototypeManager.Index("RandomPickBotanyReagent").Fills; + + // Add a random amount of a random chemical to this set of chemicals + if (randomChems != null) + { + var pick = random.Pick(randomChems); + var chemicalId = random.Pick(pick.Reagents); + var amount = random.Next(1, (int)pick.Quantity); + var seedChemQuantity = new SeedChemQuantity(); + if (chemicals.ContainsKey(chemicalId)) + { + seedChemQuantity.Min = chemicals[chemicalId].Min; + seedChemQuantity.Max = chemicals[chemicalId].Max + amount; + } + else + { + seedChemQuantity.Min = 1; + seedChemQuantity.Max = 1 + amount; + seedChemQuantity.Inherent = false; + } + var potencyDivisor = (int)Math.Ceiling(100.0f / seedChemQuantity.Max); + seedChemQuantity.PotencyDivisor = potencyDivisor; + chemicals[chemicalId] = seedChemQuantity; + } + } + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return "TODO"; + } +} diff --git a/Content.Server/EntityEffects/Effects/PlantMutateGases.cs b/Content.Server/EntityEffects/Effects/PlantMutateGases.cs new file mode 100644 index 000000000000..52b9da3a8515 --- /dev/null +++ b/Content.Server/EntityEffects/Effects/PlantMutateGases.cs @@ -0,0 +1,87 @@ +using Content.Server.Botany.Components; +using Content.Shared.Atmos; +using Content.Shared.EntityEffects; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using System.Linq; + +namespace Content.Server.EntityEffects.Effects; + +/// +/// changes the gases that a plant or produce create. +/// +public sealed partial class PlantMutateExudeGasses : EntityEffect +{ + [DataField] + public float MinValue = 0.01f; + + [DataField] + public float MaxValue = 0.5f; + + public override void Effect(EntityEffectBaseArgs args) + { + var plantholder = args.EntityManager.GetComponent(args.TargetEntity); + + if (plantholder.Seed == null) + return; + + var random = IoCManager.Resolve(); + var gasses = plantholder.Seed.ExudeGasses; + + // Add a random amount of a random gas to this gas dictionary + float amount = random.NextFloat(MinValue, MaxValue); + Gas gas = random.Pick(Enum.GetValues(typeof(Gas)).Cast().ToList()); + if (gasses.ContainsKey(gas)) + { + gasses[gas] += amount; + } + else + { + gasses.Add(gas, amount); + } + } + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return "TODO"; + } +} + +/// +/// changes the gases that a plant or produce consumes. +/// +public sealed partial class PlantMutateConsumeGasses : EntityEffect +{ + [DataField] + public float MinValue = 0.01f; + + [DataField] + public float MaxValue = 0.5f; + public override void Effect(EntityEffectBaseArgs args) + { + var plantholder = args.EntityManager.GetComponent(args.TargetEntity); + + if (plantholder.Seed == null) + return; + + var random = IoCManager.Resolve(); + var gasses = plantholder.Seed.ConsumeGasses; + + // Add a random amount of a random gas to this gas dictionary + float amount = random.NextFloat(MinValue, MaxValue); + Gas gas = random.Pick(Enum.GetValues(typeof(Gas)).Cast().ToList()); + if (gasses.ContainsKey(gas)) + { + gasses[gas] += amount; + } + else + { + gasses.Add(gas, amount); + } + } + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return "TODO"; + } +} diff --git a/Content.Server/EntityEffects/Effects/PlantMutateHarvest.cs b/Content.Server/EntityEffects/Effects/PlantMutateHarvest.cs new file mode 100644 index 000000000000..e67176ee16db --- /dev/null +++ b/Content.Server/EntityEffects/Effects/PlantMutateHarvest.cs @@ -0,0 +1,30 @@ +using Content.Server.Botany; +using Content.Server.Botany.Components; +using Content.Shared.EntityEffects; +using Robust.Shared.Prototypes; + +namespace Content.Server.EntityEffects.Effects; + +/// +/// Upgrades a plant's harvest type. +/// +public sealed partial class PlantMutateHarvest : EntityEffect +{ + public override void Effect(EntityEffectBaseArgs args) + { + var plantholder = args.EntityManager.GetComponent(args.TargetEntity); + + if (plantholder.Seed == null) + return; + + if (plantholder.Seed.HarvestRepeat == HarvestType.NoRepeat) + plantholder.Seed.HarvestRepeat = HarvestType.Repeat; + else if (plantholder.Seed.HarvestRepeat == HarvestType.Repeat) + plantholder.Seed.HarvestRepeat = HarvestType.SelfHarvest; + } + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return "TODO"; + } +} diff --git a/Content.Server/EntityEffects/Effects/PlantSpeciesChange.cs b/Content.Server/EntityEffects/Effects/PlantSpeciesChange.cs new file mode 100644 index 000000000000..65bd59daa370 --- /dev/null +++ b/Content.Server/EntityEffects/Effects/PlantSpeciesChange.cs @@ -0,0 +1,43 @@ +using Content.Server.Botany; +using Content.Server.Botany.Components; +using Content.Shared.EntityEffects; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Serilog; + +namespace Content.Server.EntityEffects.Effects; + +/// +/// Changes a plant into one of the species its able to mutate into. +/// +public sealed partial class PlantSpeciesChange : EntityEffect +{ + public override void Effect(EntityEffectBaseArgs args) + { + var prototypeManager = IoCManager.Resolve(); + var plantholder = args.EntityManager.GetComponent(args.TargetEntity); + + if (plantholder.Seed == null) + return; + + if (plantholder.Seed.MutationPrototypes.Count == 0) + return; + + var random = IoCManager.Resolve(); + var targetProto = random.Pick(plantholder.Seed.MutationPrototypes); + prototypeManager.TryIndex(targetProto, out SeedPrototype? protoSeed); + + if (protoSeed == null) + { + Log.Error($"Seed prototype could not be found: {targetProto}!"); + return; + } + + plantholder.Seed = plantholder.Seed.SpeciesChange(protoSeed); + } + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return "TODO"; + } +} diff --git a/Content.Server/EntityEffects/Effects/Slipify.cs b/Content.Server/EntityEffects/Effects/Slipify.cs new file mode 100644 index 000000000000..bc1cc062a387 --- /dev/null +++ b/Content.Server/EntityEffects/Effects/Slipify.cs @@ -0,0 +1,38 @@ +using Content.Shared.EntityEffects; +using Content.Shared.Physics; +using Content.Shared.Slippery; +using Content.Shared.StepTrigger.Components; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Prototypes; + +namespace Content.Server.EntityEffects.Effects; + +/// +/// Makes a mob slippery. +/// +public sealed partial class Slipify : EntityEffect +{ + public override void Effect(EntityEffectBaseArgs args) + { + var fixtureSystem = args.EntityManager.System(); + var colWakeSystem = args.EntityManager.System(); + var slippery = args.EntityManager.EnsureComponent(args.TargetEntity); + args.EntityManager.Dirty(args.TargetEntity, slippery); + args.EntityManager.EnsureComponent(args.TargetEntity); + // Need a fixture with a slip layer in order to actually do the slipping + var fixtures = args.EntityManager.EnsureComponent(args.TargetEntity); + var body = args.EntityManager.EnsureComponent(args.TargetEntity); + var shape = fixtures.Fixtures["fix1"].Shape; + fixtureSystem.TryCreateFixture(args.TargetEntity, shape, "slips", 1, false, (int)CollisionGroup.SlipLayer, manager: fixtures, body: body); + // Need to disable collision wake so that mobs can collide with and slip on it + var collisionWake = args.EntityManager.EnsureComponent(args.TargetEntity); + colWakeSystem.SetEnabled(args.TargetEntity, false, collisionWake); + } + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + throw new NotImplementedException(); + } +} diff --git a/Content.Server/Forensics/Systems/ForensicPadSystem.cs b/Content.Server/Forensics/Systems/ForensicPadSystem.cs index 42512cb1fdc4..a3f5627cdba2 100644 --- a/Content.Server/Forensics/Systems/ForensicPadSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicPadSystem.cs @@ -1,10 +1,11 @@ -using Content.Shared.Examine; -using Content.Shared.Interaction; -using Content.Shared.Inventory; +using Content.Server.Labels; using Content.Server.Popups; using Content.Shared.DoAfter; +using Content.Shared.Examine; using Content.Shared.Forensics; using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Inventory; namespace Content.Server.Forensics { @@ -17,6 +18,7 @@ public sealed class ForensicPadSystem : EntitySystem [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly LabelSystem _label = default!; public override void Initialize() { @@ -99,10 +101,8 @@ private void OnDoAfter(EntityUid uid, ForensicPadComponent padComponent, Forensi if (args.Args.Target != null) { - var name = HasComp(args.Args.Target) - ? "forensic-pad-fingerprint-name" - : "forensic-pad-gloves-name"; - _metaData.SetEntityName(uid, Loc.GetString(name, ("entity", args.Args.Target))); + string label = Identity.Name(args.Args.Target.Value, EntityManager); + _label.Label(uid, label); } padComponent.Sample = args.Sample; diff --git a/Content.Server/Gravity/GravityGeneratorSystem.cs b/Content.Server/Gravity/GravityGeneratorSystem.cs index 5ab2dc893108..9d58b82d690c 100644 --- a/Content.Server/Gravity/GravityGeneratorSystem.cs +++ b/Content.Server/Gravity/GravityGeneratorSystem.cs @@ -36,8 +36,10 @@ public override void Update(float frameTime) private void OnActivated(Entity ent, ref ChargedMachineActivatedEvent args) { ent.Comp.GravityActive = true; - if (TryComp(ent, out var xform) && - TryComp(xform.ParentUid, out GravityComponent? gravity)) + + var xform = Transform(ent); + + if (TryComp(xform.ParentUid, out GravityComponent? gravity)) { _gravitySystem.EnableGravity(xform.ParentUid, gravity); } @@ -46,8 +48,10 @@ private void OnActivated(Entity ent, ref ChargedMachi private void OnDeactivated(Entity ent, ref ChargedMachineDeactivatedEvent args) { ent.Comp.GravityActive = false; - if (TryComp(ent, out var xform) && - TryComp(xform.ParentUid, out GravityComponent? gravity)) + + var xform = Transform(ent); + + if (TryComp(xform.ParentUid, out GravityComponent? gravity)) { _gravitySystem.RefreshGravity(xform.ParentUid, gravity); } diff --git a/Content.Server/Guardian/GuardianSystem.cs b/Content.Server/Guardian/GuardianSystem.cs index ae4d0ca2b8ca..7a1b875756e7 100644 --- a/Content.Server/Guardian/GuardianSystem.cs +++ b/Content.Server/Guardian/GuardianSystem.cs @@ -80,6 +80,12 @@ private void OnPerformAction(EntityUid uid, GuardianHostComponent component, Gua if (args.Handled) return; + if (_container.IsEntityInContainer(uid)) + { + _popupSystem.PopupEntity(Loc.GetString("guardian-inside-container"), uid, uid); + return; + } + if (component.HostedGuardian != null) ToggleGuardian(uid, component); diff --git a/Content.Server/Guidebook/GuidebookDataSystem.cs b/Content.Server/Guidebook/GuidebookDataSystem.cs new file mode 100644 index 000000000000..86a6344156d5 --- /dev/null +++ b/Content.Server/Guidebook/GuidebookDataSystem.cs @@ -0,0 +1,111 @@ +using System.Reflection; +using Content.Shared.Guidebook; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Server.Guidebook; + +/// +/// Server system for identifying component fields/properties to extract values from entity prototypes. +/// Extracted data is sent to clients when they connect or when prototypes are reloaded. +/// +public sealed class GuidebookDataSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _protoMan = default!; + + private readonly Dictionary> _tagged = []; + private GuidebookData _cachedData = new(); + + public override void Initialize() + { + base.Initialize(); + + SubscribeNetworkEvent(OnRequestRules); + SubscribeLocalEvent(OnPrototypesReloaded); + + // Build initial cache + GatherData(ref _cachedData); + } + + private void OnRequestRules(RequestGuidebookDataEvent ev, EntitySessionEventArgs args) + { + // Send cached data to requesting client + var sendEv = new UpdateGuidebookDataEvent(_cachedData); + RaiseNetworkEvent(sendEv, args.SenderSession); + } + + private void OnPrototypesReloaded(PrototypesReloadedEventArgs args) + { + // We only care about entity prototypes + if (!args.WasModified()) + return; + + // The entity prototypes changed! Clear our cache and regather data + RebuildDataCache(); + + // Send new data to all clients + var ev = new UpdateGuidebookDataEvent(_cachedData); + RaiseNetworkEvent(ev); + } + + private void GatherData(ref GuidebookData cache) + { + // Just for debug metrics + var memberCount = 0; + var prototypeCount = 0; + + if (_tagged.Count == 0) + { + // Scan component registrations to find members tagged for extraction + foreach (var registration in EntityManager.ComponentFactory.GetAllRegistrations()) + { + foreach (var member in registration.Type.GetMembers()) + { + if (member.HasCustomAttribute()) + { + // Note this component-member pair for later + _tagged.GetOrNew(registration.Name).Add(member); + memberCount++; + } + } + } + } + + // Scan entity prototypes for the component-member pairs we noted + var entityPrototypes = _protoMan.EnumeratePrototypes(); + foreach (var prototype in entityPrototypes) + { + foreach (var (component, entry) in prototype.Components) + { + if (!_tagged.TryGetValue(component, out var members)) + continue; + + prototypeCount++; + + foreach (var member in members) + { + // It's dumb that we can't just do member.GetValue, but we can't, so + var value = member switch + { + FieldInfo field => field.GetValue(entry.Component), + PropertyInfo property => property.GetValue(entry.Component), + _ => throw new NotImplementedException("Unsupported member type") + }; + // Add it into the data cache + cache.AddData(prototype.ID, component, member.Name, value); + } + } + } + + Log.Debug($"Collected {cache.Count} Guidebook Protodata value(s) - {prototypeCount} matched prototype(s), {_tagged.Count} component(s), {memberCount} member(s)"); + } + + /// + /// Clears the cached data, then regathers it. + /// + private void RebuildDataCache() + { + _cachedData.Clear(); + GatherData(ref _cachedData); + } +} diff --git a/Content.Server/IdentityManagement/IdentitySystem.cs b/Content.Server/IdentityManagement/IdentitySystem.cs index 4766b89172fa..e110a4248345 100644 --- a/Content.Server/IdentityManagement/IdentitySystem.cs +++ b/Content.Server/IdentityManagement/IdentitySystem.cs @@ -39,6 +39,7 @@ public override void Initialize() SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); + SubscribeLocalEvent((uid, _, _) => QueueIdentityUpdate(uid)); SubscribeLocalEvent(OnMapInit); } diff --git a/Content.Server/Mech/Systems/MechSystem.cs b/Content.Server/Mech/Systems/MechSystem.cs index b738d28b4674..9da96a76f8e9 100644 --- a/Content.Server/Mech/Systems/MechSystem.cs +++ b/Content.Server/Mech/Systems/MechSystem.cs @@ -211,8 +211,11 @@ private void OnAlternativeVerb(EntityUid uid, MechComponent component, GetVerbsE return; } - var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.ExitDelay, - new MechExitEvent(), uid, target: uid); + var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.ExitDelay, new MechExitEvent(), uid, target: uid) + { + BreakOnMove = true, + }; + _popup.PopupEntity(Loc.GetString("mech-eject-pilot-alert", ("item", uid), ("user", args.User)), uid, PopupType.Large); _doAfter.TryStartDoAfter(doAfterEventArgs); } diff --git a/Content.Server/Mind/Commands/RenameCommand.cs b/Content.Server/Mind/Commands/RenameCommand.cs index 834453fb1985..f283fe5d19ce 100644 --- a/Content.Server/Mind/Commands/RenameCommand.cs +++ b/Content.Server/Mind/Commands/RenameCommand.cs @@ -1,31 +1,22 @@ using System.Diagnostics.CodeAnalysis; -using Content.Server.Access.Systems; using Content.Server.Administration; -using Content.Server.Administration.Systems; -using Content.Server.PDA; -using Content.Server.StationRecords.Systems; using Content.Shared.Access.Components; using Content.Shared.Administration; -using Content.Shared.Mind; -using Content.Shared.PDA; -using Content.Shared.StationRecords; using Robust.Server.Player; using Robust.Shared.Console; -using Robust.Shared.Player; namespace Content.Server.Mind.Commands; [AdminCommand(AdminFlags.VarEdit)] -public sealed class RenameCommand : IConsoleCommand +public sealed class RenameCommand : LocalizedEntityCommands { [Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly MetaDataSystem _metaSystem = default!; - public string Command => "rename"; - public string Description => "Renames an entity and its cloner entries, ID cards, and PDAs."; - public string Help => "rename "; + public override string Command => "rename"; - public void Execute(IConsoleShell shell, string argStr, string[] args) + public override void Execute(IConsoleShell shell, string argStr, string[] args) { if (args.Length != 2) { @@ -36,69 +27,14 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) var name = args[1]; if (name.Length > IdCardConsoleComponent.MaxFullNameLength) { - shell.WriteLine("Name is too long."); + shell.WriteLine(Loc.GetString("cmd-rename-too-long")); return; } if (!TryParseUid(args[0], shell, _entManager, out var entityUid)) return; - // Metadata - var metadata = _entManager.GetComponent(entityUid.Value); - var oldName = metadata.EntityName; - _entManager.System().SetEntityName(entityUid.Value, name, metadata); - - var minds = _entManager.System(); - - if (minds.TryGetMind(entityUid.Value, out var mindId, out var mind)) - { - // Mind - mind.CharacterName = name; - _entManager.Dirty(mindId, mind); - } - - // Id Cards - if (_entManager.TrySystem(out var idCardSystem)) - { - if (idCardSystem.TryFindIdCard(entityUid.Value, out var idCard)) - { - idCardSystem.TryChangeFullName(idCard, name, idCard); - - // Records - // This is done here because ID cards are linked to station records - if (_entManager.TrySystem(out var recordsSystem) - && _entManager.TryGetComponent(idCard, out StationRecordKeyStorageComponent? keyStorage) - && keyStorage.Key is {} key) - { - if (recordsSystem.TryGetRecord(key, out var generalRecord)) - { - generalRecord.Name = name; - } - - recordsSystem.Synchronize(key); - } - } - } - - // PDAs - if (_entManager.TrySystem(out var pdaSystem)) - { - var query = _entManager.EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var pda)) - { - if (pda.OwnerName == oldName) - { - pdaSystem.SetOwner(uid, pda, name); - } - } - } - - // Admin Overlay - if (_entManager.TrySystem(out var adminSystem) - && _entManager.TryGetComponent(entityUid, out var actorComp)) - { - adminSystem.UpdatePlayerList(actorComp.PlayerSession); - } + _metaSystem.SetEntityName(entityUid.Value, name); } private bool TryParseUid(string str, IConsoleShell shell, @@ -114,9 +50,9 @@ private bool TryParseUid(string str, IConsoleShell shell, } if (session == null) - shell.WriteError("Can't find username/uid: " + str); + shell.WriteError(Loc.GetString("cmd-rename-not-found", ("target", str))); else - shell.WriteError(str + " does not have an entity."); + shell.WriteError(Loc.GetString("cmd-rename-no-entity", ("target", str))); entityUid = EntityUid.Invalid; return false; diff --git a/Content.Server/Nutrition/EntitySystems/FoodSequenceSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSequenceSystem.cs index 367b16a26821..ae8215ac6aee 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSequenceSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSequenceSystem.cs @@ -133,7 +133,7 @@ private bool TryAddFoodElement(Entity start, En var flip = start.Comp.AllowHorizontalFlip && _random.Prob(0.5f); var layer = new FoodSequenceVisualLayer(elementIndexed, _random.Pick(elementIndexed.Sprites), - new Vector2(flip ? -1 : 1, 1), + new Vector2(flip ? -elementIndexed.Scale.X : elementIndexed.Scale.X, elementIndexed.Scale.Y), new Vector2( _random.NextFloat(start.Comp.MinLayerOffset.X, start.Comp.MaxLayerOffset.X), _random.NextFloat(start.Comp.MinLayerOffset.Y, start.Comp.MaxLayerOffset.Y)) diff --git a/Content.Server/PDA/PdaSystem.cs b/Content.Server/PDA/PdaSystem.cs index 691d024ecd74..cdcdbc02e5fb 100644 --- a/Content.Server/PDA/PdaSystem.cs +++ b/Content.Server/PDA/PdaSystem.cs @@ -55,9 +55,23 @@ public override void Initialize() SubscribeLocalEvent(OnNotification); SubscribeLocalEvent(OnStationRenamed); + SubscribeLocalEvent(OnEntityRenamed); SubscribeLocalEvent(OnAlertLevelChanged); } + private void OnEntityRenamed(ref EntityRenamedEvent ev) + { + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.PdaOwner == ev.Uid) + { + SetOwner(uid, comp, ev.Uid, ev.NewName); + } + } + } + protected override void OnComponentInit(EntityUid uid, PdaComponent pda, ComponentInit args) { base.OnComponentInit(uid, pda, args); @@ -94,9 +108,10 @@ private void OnLightToggle(EntityUid uid, PdaComponent pda, LightToggleEvent arg UpdatePdaUi(uid, pda); } - public void SetOwner(EntityUid uid, PdaComponent pda, string ownerName) + public void SetOwner(EntityUid uid, PdaComponent pda, EntityUid owner, string ownerName) { pda.OwnerName = ownerName; + pda.PdaOwner = owner; UpdatePdaUi(uid, pda); } @@ -112,7 +127,7 @@ private void OnAlertLevelChanged(AlertLevelChangedEvent args) private void UpdateAllPdaUisOnStation() { - var query = EntityQueryEnumerator(); + var query = AllEntityQuery(); while (query.MoveNext(out var ent, out var comp)) { UpdatePdaUi(ent, comp); diff --git a/Content.Server/Physics/Controllers/MoverController.cs b/Content.Server/Physics/Controllers/MoverController.cs index 19d58438b35b..f927e717a9d8 100644 --- a/Content.Server/Physics/Controllers/MoverController.cs +++ b/Content.Server/Physics/Controllers/MoverController.cs @@ -12,560 +12,559 @@ using DependencyAttribute = Robust.Shared.IoC.DependencyAttribute; using Robust.Shared.Map.Components; -namespace Content.Server.Physics.Controllers +namespace Content.Server.Physics.Controllers; + +public sealed class MoverController : SharedMoverController { - public sealed class MoverController : SharedMoverController + [Dependency] private readonly ThrusterSystem _thruster = default!; + [Dependency] private readonly SharedTransformSystem _xformSystem = default!; + + private Dictionary)> _shuttlePilots = new(); + + public override void Initialize() { - [Dependency] private readonly ThrusterSystem _thruster = default!; - [Dependency] private readonly SharedTransformSystem _xformSystem = default!; + base.Initialize(); + SubscribeLocalEvent(OnRelayPlayerAttached); + SubscribeLocalEvent(OnRelayPlayerDetached); + SubscribeLocalEvent(OnPlayerAttached); + SubscribeLocalEvent(OnPlayerDetached); + } - private Dictionary)> _shuttlePilots = new(); + private void OnRelayPlayerAttached(Entity entity, ref PlayerAttachedEvent args) + { + if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover)) + SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None); + } - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnRelayPlayerAttached); - SubscribeLocalEvent(OnRelayPlayerDetached); - SubscribeLocalEvent(OnPlayerAttached); - SubscribeLocalEvent(OnPlayerDetached); - } + private void OnRelayPlayerDetached(Entity entity, ref PlayerDetachedEvent args) + { + if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover)) + SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None); + } - private void OnRelayPlayerAttached(Entity entity, ref PlayerAttachedEvent args) - { - if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover)) - SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None); - } + private void OnPlayerAttached(Entity entity, ref PlayerAttachedEvent args) + { + SetMoveInput(entity, MoveButtons.None); + } - private void OnRelayPlayerDetached(Entity entity, ref PlayerDetachedEvent args) - { - if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover)) - SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None); - } + private void OnPlayerDetached(Entity entity, ref PlayerDetachedEvent args) + { + SetMoveInput(entity, MoveButtons.None); + } - private void OnPlayerAttached(Entity entity, ref PlayerAttachedEvent args) - { - SetMoveInput(entity, MoveButtons.None); - } + protected override bool CanSound() + { + return true; + } - private void OnPlayerDetached(Entity entity, ref PlayerDetachedEvent args) - { - SetMoveInput(entity, MoveButtons.None); - } + public override void UpdateBeforeSolve(bool prediction, float frameTime) + { + base.UpdateBeforeSolve(prediction, frameTime); - protected override bool CanSound() - { - return true; - } + var inputQueryEnumerator = AllEntityQuery(); - public override void UpdateBeforeSolve(bool prediction, float frameTime) + while (inputQueryEnumerator.MoveNext(out var uid, out var mover)) { - base.UpdateBeforeSolve(prediction, frameTime); + var physicsUid = uid; - var inputQueryEnumerator = AllEntityQuery(); + if (RelayQuery.HasComponent(uid)) + continue; - while (inputQueryEnumerator.MoveNext(out var uid, out var mover)) + if (!XformQuery.TryGetComponent(uid, out var xform)) { - var physicsUid = uid; - - if (RelayQuery.HasComponent(uid)) - continue; - - if (!XformQuery.TryGetComponent(uid, out var xform)) - { - continue; - } - - PhysicsComponent? body; - var xformMover = xform; + continue; + } - if (mover.ToParent && RelayQuery.HasComponent(xform.ParentUid)) - { - if (!PhysicsQuery.TryGetComponent(xform.ParentUid, out body) || - !XformQuery.TryGetComponent(xform.ParentUid, out xformMover)) - { - continue; - } + PhysicsComponent? body; + var xformMover = xform; - physicsUid = xform.ParentUid; - } - else if (!PhysicsQuery.TryGetComponent(uid, out body)) + if (mover.ToParent && RelayQuery.HasComponent(xform.ParentUid)) + { + if (!PhysicsQuery.TryGetComponent(xform.ParentUid, out body) || + !XformQuery.TryGetComponent(xform.ParentUid, out xformMover)) { continue; } - HandleMobMovement(uid, - mover, - physicsUid, - body, - xformMover, - frameTime); + physicsUid = xform.ParentUid; } - - HandleShuttleMovement(frameTime); - } - - public (Vector2 Strafe, float Rotation, float Brakes) GetPilotVelocityInput(PilotComponent component) - { - if (!Timing.InSimulation) + else if (!PhysicsQuery.TryGetComponent(uid, out body)) { - // Outside of simulation we'll be running client predicted movement per-frame. - // So return a full-length vector as if it's a full tick. - // Physics system will have the correct time step anyways. - ResetSubtick(component); - ApplyTick(component, 1f); - return (component.CurTickStrafeMovement, component.CurTickRotationMovement, component.CurTickBraking); + continue; } - float remainingFraction; - - if (Timing.CurTick > component.LastInputTick) - { - component.CurTickStrafeMovement = Vector2.Zero; - component.CurTickRotationMovement = 0f; - component.CurTickBraking = 0f; - remainingFraction = 1; - } - else - { - remainingFraction = (ushort.MaxValue - component.LastInputSubTick) / (float) ushort.MaxValue; - } + HandleMobMovement(uid, + mover, + physicsUid, + body, + xformMover, + frameTime); + } - ApplyTick(component, remainingFraction); + HandleShuttleMovement(frameTime); + } - // Logger.Info($"{curDir}{walk}{sprint}"); + public (Vector2 Strafe, float Rotation, float Brakes) GetPilotVelocityInput(PilotComponent component) + { + if (!Timing.InSimulation) + { + // Outside of simulation we'll be running client predicted movement per-frame. + // So return a full-length vector as if it's a full tick. + // Physics system will have the correct time step anyways. + ResetSubtick(component); + ApplyTick(component, 1f); return (component.CurTickStrafeMovement, component.CurTickRotationMovement, component.CurTickBraking); } - private void ResetSubtick(PilotComponent component) - { - if (Timing.CurTick <= component.LastInputTick) return; + float remainingFraction; + if (Timing.CurTick > component.LastInputTick) + { component.CurTickStrafeMovement = Vector2.Zero; component.CurTickRotationMovement = 0f; component.CurTickBraking = 0f; - component.LastInputTick = Timing.CurTick; - component.LastInputSubTick = 0; + remainingFraction = 1; + } + else + { + remainingFraction = (ushort.MaxValue - component.LastInputSubTick) / (float) ushort.MaxValue; } - protected override void HandleShuttleInput(EntityUid uid, ShuttleButtons button, ushort subTick, bool state) + ApplyTick(component, remainingFraction); + + // Logger.Info($"{curDir}{walk}{sprint}"); + return (component.CurTickStrafeMovement, component.CurTickRotationMovement, component.CurTickBraking); + } + + private void ResetSubtick(PilotComponent component) + { + if (Timing.CurTick <= component.LastInputTick) return; + + component.CurTickStrafeMovement = Vector2.Zero; + component.CurTickRotationMovement = 0f; + component.CurTickBraking = 0f; + component.LastInputTick = Timing.CurTick; + component.LastInputSubTick = 0; + } + + protected override void HandleShuttleInput(EntityUid uid, ShuttleButtons button, ushort subTick, bool state) + { + if (!TryComp(uid, out var pilot) || pilot.Console == null) + return; + + ResetSubtick(pilot); + + if (subTick >= pilot.LastInputSubTick) { - if (!TryComp(uid, out var pilot) || pilot.Console == null) - return; + var fraction = (subTick - pilot.LastInputSubTick) / (float) ushort.MaxValue; - ResetSubtick(pilot); + ApplyTick(pilot, fraction); + pilot.LastInputSubTick = subTick; + } - if (subTick >= pilot.LastInputSubTick) - { - var fraction = (subTick - pilot.LastInputSubTick) / (float) ushort.MaxValue; + var buttons = pilot.HeldButtons; - ApplyTick(pilot, fraction); - pilot.LastInputSubTick = subTick; - } + if (state) + { + buttons |= button; + } + else + { + buttons &= ~button; + } - var buttons = pilot.HeldButtons; + pilot.HeldButtons = buttons; + } - if (state) - { - buttons |= button; - } - else - { - buttons &= ~button; - } + private static void ApplyTick(PilotComponent component, float fraction) + { + var x = 0; + var y = 0; + var rot = 0; + int brake; - pilot.HeldButtons = buttons; + if ((component.HeldButtons & ShuttleButtons.StrafeLeft) != 0x0) + { + x -= 1; } - private static void ApplyTick(PilotComponent component, float fraction) + if ((component.HeldButtons & ShuttleButtons.StrafeRight) != 0x0) { - var x = 0; - var y = 0; - var rot = 0; - int brake; + x += 1; + } - if ((component.HeldButtons & ShuttleButtons.StrafeLeft) != 0x0) - { - x -= 1; - } + component.CurTickStrafeMovement.X += x * fraction; - if ((component.HeldButtons & ShuttleButtons.StrafeRight) != 0x0) - { - x += 1; - } + if ((component.HeldButtons & ShuttleButtons.StrafeUp) != 0x0) + { + y += 1; + } - component.CurTickStrafeMovement.X += x * fraction; + if ((component.HeldButtons & ShuttleButtons.StrafeDown) != 0x0) + { + y -= 1; + } - if ((component.HeldButtons & ShuttleButtons.StrafeUp) != 0x0) - { - y += 1; - } + component.CurTickStrafeMovement.Y += y * fraction; - if ((component.HeldButtons & ShuttleButtons.StrafeDown) != 0x0) - { - y -= 1; - } + if ((component.HeldButtons & ShuttleButtons.RotateLeft) != 0x0) + { + rot -= 1; + } - component.CurTickStrafeMovement.Y += y * fraction; + if ((component.HeldButtons & ShuttleButtons.RotateRight) != 0x0) + { + rot += 1; + } - if ((component.HeldButtons & ShuttleButtons.RotateLeft) != 0x0) - { - rot -= 1; - } + component.CurTickRotationMovement += rot * fraction; - if ((component.HeldButtons & ShuttleButtons.RotateRight) != 0x0) - { - rot += 1; - } + if ((component.HeldButtons & ShuttleButtons.Brake) != 0x0) + { + brake = 1; + } + else + { + brake = 0; + } - component.CurTickRotationMovement += rot * fraction; + component.CurTickBraking += brake * fraction; + } + + /// + /// Helper function to extrapolate max velocity for a given Vector2 (really, its angle) and shuttle. + /// + private Vector2 ObtainMaxVel(Vector2 vel, ShuttleComponent shuttle) + { + if (vel.Length() == 0f) + return Vector2.Zero; + + // this math could PROBABLY be simplified for performance + // probably + // __________________________________ + // / / __ __ \2 / __ __ \2 + // O = I : _ / |I * | 1/H | | + |I * | 0 | | + // V \ |_ 0 _| / \ |_1/V_| / + + var horizIndex = vel.X > 0 ? 1 : 3; // east else west + var vertIndex = vel.Y > 0 ? 2 : 0; // north else south + var horizComp = vel.X != 0 ? MathF.Pow(Vector2.Dot(vel, new (shuttle.LinearThrust[horizIndex] / shuttle.LinearThrust[horizIndex], 0f)), 2) : 0; + var vertComp = vel.Y != 0 ? MathF.Pow(Vector2.Dot(vel, new (0f, shuttle.LinearThrust[vertIndex] / shuttle.LinearThrust[vertIndex])), 2) : 0; + + return shuttle.BaseMaxLinearVelocity * vel * MathF.ReciprocalSqrtEstimate(horizComp + vertComp); + } + + private void HandleShuttleMovement(float frameTime) + { + var newPilots = new Dictionary)>(); + + // We just mark off their movement and the shuttle itself does its own movement + var activePilotQuery = EntityQueryEnumerator(); + var shuttleQuery = GetEntityQuery(); + while (activePilotQuery.MoveNext(out var uid, out var pilot, out var mover)) + { + var consoleEnt = pilot.Console; - if ((component.HeldButtons & ShuttleButtons.Brake) != 0x0) + // TODO: This is terrible. Just make a new mover and also make it remote piloting + device networks + if (TryComp(consoleEnt, out var cargoConsole)) { - brake = 1; + consoleEnt = cargoConsole.Entity; } - else + + if (!TryComp(consoleEnt, out TransformComponent? xform)) continue; + + var gridId = xform.GridUid; + // This tries to see if the grid is a shuttle and if the console should work. + if (!TryComp(gridId, out var _) || + !shuttleQuery.TryGetComponent(gridId, out var shuttleComponent) || + !shuttleComponent.Enabled) + continue; + + if (!newPilots.TryGetValue(gridId!.Value, out var pilots)) { - brake = 0; + pilots = (shuttleComponent, new List<(EntityUid, PilotComponent, InputMoverComponent, TransformComponent)>()); + newPilots[gridId.Value] = pilots; } - component.CurTickBraking += brake * fraction; + pilots.Item2.Add((uid, pilot, mover, xform)); } - /// - /// Helper function to extrapolate max velocity for a given Vector2 (really, its angle) and shuttle. - /// - private Vector2 ObtainMaxVel(Vector2 vel, ShuttleComponent shuttle) + // Reset inputs for non-piloted shuttles. + foreach (var (shuttleUid, (shuttle, _)) in _shuttlePilots) { - if (vel.Length() == 0f) - return Vector2.Zero; - - // this math could PROBABLY be simplified for performance - // probably - // __________________________________ - // / / __ __ \2 / __ __ \2 - // O = I : _ / |I * | 1/H | | + |I * | 0 | | - // V \ |_ 0 _| / \ |_1/V_| / - - var horizIndex = vel.X > 0 ? 1 : 3; // east else west - var vertIndex = vel.Y > 0 ? 2 : 0; // north else south - var horizComp = vel.X != 0 ? MathF.Pow(Vector2.Dot(vel, new (shuttle.LinearThrust[horizIndex] / shuttle.LinearThrust[horizIndex], 0f)), 2) : 0; - var vertComp = vel.Y != 0 ? MathF.Pow(Vector2.Dot(vel, new (0f, shuttle.LinearThrust[vertIndex] / shuttle.LinearThrust[vertIndex])), 2) : 0; - - return shuttle.BaseMaxLinearVelocity * vel * MathF.ReciprocalSqrtEstimate(horizComp + vertComp); + if (newPilots.ContainsKey(shuttleUid) || CanPilot(shuttleUid)) + continue; + + _thruster.DisableLinearThrusters(shuttle); } - private void HandleShuttleMovement(float frameTime) + _shuttlePilots = newPilots; + + // Collate all of the linear / angular velocites for a shuttle + // then do the movement input once for it. + var xformQuery = GetEntityQuery(); + foreach (var (shuttleUid, (shuttle, pilots)) in _shuttlePilots) { - var newPilots = new Dictionary)>(); + if (Paused(shuttleUid) || CanPilot(shuttleUid) || !TryComp(shuttleUid, out var body)) + continue; - // We just mark off their movement and the shuttle itself does its own movement - var activePilotQuery = EntityQueryEnumerator(); - var shuttleQuery = GetEntityQuery(); - while (activePilotQuery.MoveNext(out var uid, out var pilot, out var mover)) + var shuttleNorthAngle = _xformSystem.GetWorldRotation(shuttleUid, xformQuery); + + // Collate movement linear and angular inputs together + var linearInput = Vector2.Zero; + var brakeInput = 0f; + var angularInput = 0f; + + foreach (var (pilotUid, pilot, _, consoleXform) in pilots) { - var consoleEnt = pilot.Console; + var (strafe, rotation, brakes) = GetPilotVelocityInput(pilot); - // TODO: This is terrible. Just make a new mover and also make it remote piloting + device networks - if (TryComp(consoleEnt, out var cargoConsole)) + if (brakes > 0f) { - consoleEnt = cargoConsole.Entity; + brakeInput += brakes; } - if (!TryComp(consoleEnt, out TransformComponent? xform)) continue; - - var gridId = xform.GridUid; - // This tries to see if the grid is a shuttle and if the console should work. - if (!TryComp(gridId, out var _) || - !shuttleQuery.TryGetComponent(gridId, out var shuttleComponent) || - !shuttleComponent.Enabled) - continue; - - if (!newPilots.TryGetValue(gridId!.Value, out var pilots)) + if (strafe.Length() > 0f) { - pilots = (shuttleComponent, new List<(EntityUid, PilotComponent, InputMoverComponent, TransformComponent)>()); - newPilots[gridId.Value] = pilots; + var offsetRotation = consoleXform.LocalRotation; + linearInput += offsetRotation.RotateVec(strafe); } - pilots.Item2.Add((uid, pilot, mover, xform)); - } - - // Reset inputs for non-piloted shuttles. - foreach (var (shuttleUid, (shuttle, _)) in _shuttlePilots) - { - if (newPilots.ContainsKey(shuttleUid) || CanPilot(shuttleUid)) - continue; - - _thruster.DisableLinearThrusters(shuttle); + if (rotation != 0f) + { + angularInput += rotation; + } } - _shuttlePilots = newPilots; + var count = pilots.Count; + linearInput /= count; + angularInput /= count; + brakeInput /= count; - // Collate all of the linear / angular velocites for a shuttle - // then do the movement input once for it. - var xformQuery = GetEntityQuery(); - foreach (var (shuttleUid, (shuttle, pilots)) in _shuttlePilots) + // Handle shuttle movement + if (brakeInput > 0f) { - if (Paused(shuttleUid) || CanPilot(shuttleUid) || !TryComp(shuttleUid, out var body)) - continue; - - var shuttleNorthAngle = _xformSystem.GetWorldRotation(shuttleUid, xformQuery); - - // Collate movement linear and angular inputs together - var linearInput = Vector2.Zero; - var brakeInput = 0f; - var angularInput = 0f; - - foreach (var (pilotUid, pilot, _, consoleXform) in pilots) + if (body.LinearVelocity.Length() > 0f) { - var (strafe, rotation, brakes) = GetPilotVelocityInput(pilot); + // Minimum brake velocity for a direction to show its thrust appearance. + const float appearanceThreshold = 0.1f; - if (brakes > 0f) - { - brakeInput += brakes; - } - - if (strafe.Length() > 0f) - { - var offsetRotation = consoleXform.LocalRotation; - linearInput += offsetRotation.RotateVec(strafe); - } + // Get velocity relative to the shuttle so we know which thrusters to fire + var shuttleVelocity = (-shuttleNorthAngle).RotateVec(body.LinearVelocity); + var force = Vector2.Zero; - if (rotation != 0f) + if (shuttleVelocity.X < 0f) { - angularInput += rotation; - } - } + _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.West); - var count = pilots.Count; - linearInput /= count; - angularInput /= count; - brakeInput /= count; + if (shuttleVelocity.X < -appearanceThreshold) + _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.East); - // Handle shuttle movement - if (brakeInput > 0f) - { - if (body.LinearVelocity.Length() > 0f) + var index = (int) Math.Log2((int) DirectionFlag.East); + force.X += shuttle.LinearThrust[index]; + } + else if (shuttleVelocity.X > 0f) { - // Minimum brake velocity for a direction to show its thrust appearance. - const float appearanceThreshold = 0.1f; + _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.East); - // Get velocity relative to the shuttle so we know which thrusters to fire - var shuttleVelocity = (-shuttleNorthAngle).RotateVec(body.LinearVelocity); - var force = Vector2.Zero; + if (shuttleVelocity.X > appearanceThreshold) + _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.West); - if (shuttleVelocity.X < 0f) - { - _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.West); - - if (shuttleVelocity.X < -appearanceThreshold) - _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.East); - - var index = (int) Math.Log2((int) DirectionFlag.East); - force.X += shuttle.LinearThrust[index]; - } - else if (shuttleVelocity.X > 0f) - { - _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.East); + var index = (int) Math.Log2((int) DirectionFlag.West); + force.X -= shuttle.LinearThrust[index]; + } - if (shuttleVelocity.X > appearanceThreshold) - _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.West); + if (shuttleVelocity.Y < 0f) + { + _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.South); - var index = (int) Math.Log2((int) DirectionFlag.West); - force.X -= shuttle.LinearThrust[index]; - } + if (shuttleVelocity.Y < -appearanceThreshold) + _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.North); - if (shuttleVelocity.Y < 0f) - { - _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.South); + var index = (int) Math.Log2((int) DirectionFlag.North); + force.Y += shuttle.LinearThrust[index]; + } + else if (shuttleVelocity.Y > 0f) + { + _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.North); - if (shuttleVelocity.Y < -appearanceThreshold) - _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.North); + if (shuttleVelocity.Y > appearanceThreshold) + _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.South); - var index = (int) Math.Log2((int) DirectionFlag.North); - force.Y += shuttle.LinearThrust[index]; - } - else if (shuttleVelocity.Y > 0f) - { - _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.North); + var index = (int) Math.Log2((int) DirectionFlag.South); + force.Y -= shuttle.LinearThrust[index]; + } - if (shuttleVelocity.Y > appearanceThreshold) - _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.South); + var impulse = force * brakeInput * ShuttleComponent.BrakeCoefficient; + impulse = shuttleNorthAngle.RotateVec(impulse); + var forceMul = frameTime * body.InvMass; + var maxVelocity = (-body.LinearVelocity).Length() / forceMul; - var index = (int) Math.Log2((int) DirectionFlag.South); - force.Y -= shuttle.LinearThrust[index]; - } + // Don't overshoot + if (impulse.Length() > maxVelocity) + impulse = impulse.Normalized() * maxVelocity; - var impulse = force * brakeInput * ShuttleComponent.BrakeCoefficient; - impulse = shuttleNorthAngle.RotateVec(impulse); - var forceMul = frameTime * body.InvMass; - var maxVelocity = (-body.LinearVelocity).Length() / forceMul; + PhysicsSystem.ApplyForce(shuttleUid, impulse, body: body); + } + else + { + _thruster.DisableLinearThrusters(shuttle); + } - // Don't overshoot - if (impulse.Length() > maxVelocity) - impulse = impulse.Normalized() * maxVelocity; + if (body.AngularVelocity != 0f) + { + var torque = shuttle.AngularThrust * brakeInput * (body.AngularVelocity > 0f ? -1f : 1f) * ShuttleComponent.BrakeCoefficient; + var torqueMul = body.InvI * frameTime; - PhysicsSystem.ApplyForce(shuttleUid, impulse, body: body); + if (body.AngularVelocity > 0f) + { + torque = MathF.Max(-body.AngularVelocity / torqueMul, torque); } else { - _thruster.DisableLinearThrusters(shuttle); + torque = MathF.Min(-body.AngularVelocity / torqueMul, torque); } - if (body.AngularVelocity != 0f) - { - var torque = shuttle.AngularThrust * brakeInput * (body.AngularVelocity > 0f ? -1f : 1f) * ShuttleComponent.BrakeCoefficient; - var torqueMul = body.InvI * frameTime; - - if (body.AngularVelocity > 0f) - { - torque = MathF.Max(-body.AngularVelocity / torqueMul, torque); - } - else - { - torque = MathF.Min(-body.AngularVelocity / torqueMul, torque); - } - - if (!torque.Equals(0f)) - { - PhysicsSystem.ApplyTorque(shuttleUid, torque, body: body); - _thruster.SetAngularThrust(shuttle, true); - } - } - else + if (!torque.Equals(0f)) { - _thruster.SetAngularThrust(shuttle, false); + PhysicsSystem.ApplyTorque(shuttleUid, torque, body: body); + _thruster.SetAngularThrust(shuttle, true); } } - - if (linearInput.Length().Equals(0f)) + else { - PhysicsSystem.SetSleepingAllowed(shuttleUid, body, true); - - if (brakeInput.Equals(0f)) - _thruster.DisableLinearThrusters(shuttle); + _thruster.SetAngularThrust(shuttle, false); } - else + } + + if (linearInput.Length().Equals(0f)) + { + PhysicsSystem.SetSleepingAllowed(shuttleUid, body, true); + + if (brakeInput.Equals(0f)) + _thruster.DisableLinearThrusters(shuttle); + } + else + { + PhysicsSystem.SetSleepingAllowed(shuttleUid, body, false); + var angle = linearInput.ToWorldAngle(); + var linearDir = angle.GetDir(); + var dockFlag = linearDir.AsFlag(); + var totalForce = Vector2.Zero; + + // Won't just do cardinal directions. + foreach (DirectionFlag dir in Enum.GetValues(typeof(DirectionFlag))) { - PhysicsSystem.SetSleepingAllowed(shuttleUid, body, false); - var angle = linearInput.ToWorldAngle(); - var linearDir = angle.GetDir(); - var dockFlag = linearDir.AsFlag(); - var totalForce = Vector2.Zero; - - // Won't just do cardinal directions. - foreach (DirectionFlag dir in Enum.GetValues(typeof(DirectionFlag))) + // Brain no worky but I just want cardinals + switch (dir) { - // Brain no worky but I just want cardinals - switch (dir) - { - case DirectionFlag.South: - case DirectionFlag.East: - case DirectionFlag.North: - case DirectionFlag.West: - break; - default: - continue; - } - - if ((dir & dockFlag) == 0x0) - { - _thruster.DisableLinearThrustDirection(shuttle, dir); + case DirectionFlag.South: + case DirectionFlag.East: + case DirectionFlag.North: + case DirectionFlag.West: + break; + default: continue; - } - - var force = Vector2.Zero; - var index = (int) Math.Log2((int) dir); - var thrust = shuttle.LinearThrust[index]; - - switch (dir) - { - case DirectionFlag.North: - force.Y += thrust; - break; - case DirectionFlag.South: - force.Y -= thrust; - break; - case DirectionFlag.East: - force.X += thrust; - break; - case DirectionFlag.West: - force.X -= thrust; - break; - default: - throw new ArgumentOutOfRangeException($"Attempted to apply thrust to shuttle {shuttleUid} along invalid dir {dir}."); - } - - _thruster.EnableLinearThrustDirection(shuttle, dir); - var impulse = force * linearInput.Length(); - totalForce += impulse; } - var forceMul = frameTime * body.InvMass; + if ((dir & dockFlag) == 0x0) + { + _thruster.DisableLinearThrustDirection(shuttle, dir); + continue; + } - var localVel = (-shuttleNorthAngle).RotateVec(body.LinearVelocity); - var maxVelocity = ObtainMaxVel(localVel, shuttle); // max for current travel dir - var maxWishVelocity = ObtainMaxVel(totalForce, shuttle); - var properAccel = (maxWishVelocity - localVel) / forceMul; + var force = Vector2.Zero; + var index = (int) Math.Log2((int) dir); + var thrust = shuttle.LinearThrust[index]; - var finalForce = Vector2Dot(totalForce, properAccel.Normalized()) * properAccel.Normalized(); + switch (dir) + { + case DirectionFlag.North: + force.Y += thrust; + break; + case DirectionFlag.South: + force.Y -= thrust; + break; + case DirectionFlag.East: + force.X += thrust; + break; + case DirectionFlag.West: + force.X -= thrust; + break; + default: + throw new ArgumentOutOfRangeException($"Attempted to apply thrust to shuttle {shuttleUid} along invalid dir {dir}."); + } - if (localVel.Length() >= maxVelocity.Length() && Vector2.Dot(totalForce, localVel) > 0f) - finalForce = Vector2.Zero; // burn would be faster if used as such + _thruster.EnableLinearThrustDirection(shuttle, dir); + var impulse = force * linearInput.Length(); + totalForce += impulse; + } - if (finalForce.Length() > properAccel.Length()) - finalForce = properAccel; // don't overshoot + var forceMul = frameTime * body.InvMass; - //Log.Info($"shuttle: maxVelocity {maxVelocity} totalForce {totalForce} finalForce {finalForce} forceMul {forceMul} properAccel {properAccel}"); + var localVel = (-shuttleNorthAngle).RotateVec(body.LinearVelocity); + var maxVelocity = ObtainMaxVel(localVel, shuttle); // max for current travel dir + var maxWishVelocity = ObtainMaxVel(totalForce, shuttle); + var properAccel = (maxWishVelocity - localVel) / forceMul; - finalForce = shuttleNorthAngle.RotateVec(finalForce); + var finalForce = Vector2Dot(totalForce, properAccel.Normalized()) * properAccel.Normalized(); - if (finalForce.Length() > 0f) - PhysicsSystem.ApplyForce(shuttleUid, finalForce, body: body); - } + if (localVel.Length() >= maxVelocity.Length() && Vector2.Dot(totalForce, localVel) > 0f) + finalForce = Vector2.Zero; // burn would be faster if used as such - if (MathHelper.CloseTo(angularInput, 0f)) - { - PhysicsSystem.SetSleepingAllowed(shuttleUid, body, true); + if (finalForce.Length() > properAccel.Length()) + finalForce = properAccel; // don't overshoot - if (brakeInput <= 0f) - _thruster.SetAngularThrust(shuttle, false); - } - else - { - PhysicsSystem.SetSleepingAllowed(shuttleUid, body, false); - var torque = shuttle.AngularThrust * -angularInput; + //Log.Info($"shuttle: maxVelocity {maxVelocity} totalForce {totalForce} finalForce {finalForce} forceMul {forceMul} properAccel {properAccel}"); - // Need to cap the velocity if 1 tick of input brings us over cap so we don't continuously - // edge onto the cap over and over. - var torqueMul = body.InvI * frameTime; + finalForce = shuttleNorthAngle.RotateVec(finalForce); - torque = Math.Clamp(torque, - (-ShuttleComponent.MaxAngularVelocity - body.AngularVelocity) / torqueMul, - (ShuttleComponent.MaxAngularVelocity - body.AngularVelocity) / torqueMul); + if (finalForce.Length() > 0f) + PhysicsSystem.ApplyForce(shuttleUid, finalForce, body: body); + } - if (!torque.Equals(0f)) - { - PhysicsSystem.ApplyTorque(shuttleUid, torque, body: body); - _thruster.SetAngularThrust(shuttle, true); - } - } + if (MathHelper.CloseTo(angularInput, 0f)) + { + PhysicsSystem.SetSleepingAllowed(shuttleUid, body, true); + + if (brakeInput <= 0f) + _thruster.SetAngularThrust(shuttle, false); } - } + else + { + PhysicsSystem.SetSleepingAllowed(shuttleUid, body, false); + var torque = shuttle.AngularThrust * -angularInput; - // .NET 8 seem to miscompile usage of Vector2.Dot above. This manual outline fixes it pending an upstream fix. - // See PR #24008 - [MethodImpl(MethodImplOptions.NoInlining)] - public static float Vector2Dot(Vector2 value1, Vector2 value2) - { - return Vector2.Dot(value1, value2); - } + // Need to cap the velocity if 1 tick of input brings us over cap so we don't continuously + // edge onto the cap over and over. + var torqueMul = body.InvI * frameTime; - private bool CanPilot(EntityUid shuttleUid) - { - return TryComp(shuttleUid, out var ftl) - && (ftl.State & (FTLState.Starting | FTLState.Travelling | FTLState.Arriving)) != 0x0 - || HasComp(shuttleUid); + torque = Math.Clamp(torque, + (-ShuttleComponent.MaxAngularVelocity - body.AngularVelocity) / torqueMul, + (ShuttleComponent.MaxAngularVelocity - body.AngularVelocity) / torqueMul); + + if (!torque.Equals(0f)) + { + PhysicsSystem.ApplyTorque(shuttleUid, torque, body: body); + _thruster.SetAngularThrust(shuttle, true); + } + } } + } + + // .NET 8 seem to miscompile usage of Vector2.Dot above. This manual outline fixes it pending an upstream fix. + // See PR #24008 + [MethodImpl(MethodImplOptions.NoInlining)] + public static float Vector2Dot(Vector2 value1, Vector2 value2) + { + return Vector2.Dot(value1, value2); + } + private bool CanPilot(EntityUid shuttleUid) + { + return TryComp(shuttleUid, out var ftl) + && (ftl.State & (FTLState.Starting | FTLState.Travelling | FTLState.Arriving)) != 0x0 + || HasComp(shuttleUid); } + } diff --git a/Content.Server/Power/EntitySystems/ActivatableUIRequiresPowerSystem.cs b/Content.Server/Power/EntitySystems/ActivatableUIRequiresPowerSystem.cs index 9fd824a3c495..a33bddcaa34f 100644 --- a/Content.Server/Power/EntitySystems/ActivatableUIRequiresPowerSystem.cs +++ b/Content.Server/Power/EntitySystems/ActivatableUIRequiresPowerSystem.cs @@ -1,9 +1,7 @@ -using Content.Server.Power.Components; using Content.Shared.Power; using Content.Shared.Power.Components; using Content.Shared.Power.EntitySystems; using Content.Shared.UserInterface; -using Content.Shared.Wires; using ActivatableUISystem = Content.Shared.UserInterface.ActivatableUISystem; namespace Content.Server.Power.EntitySystems; @@ -26,9 +24,6 @@ protected override void OnActivate(Entity e return; } - if (TryComp(ent.Owner, out var panel) && panel.Open) - return; - args.Cancel(); } diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index 82a38f5f00fe..46d2cd69b918 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -92,7 +92,7 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(HandlePlayerSpawning, before: new []{ typeof(ContainerSpawnPointSystem), typeof(SpawnPointSystem)}); + SubscribeLocalEvent(HandlePlayerSpawning, before: new []{ typeof(SpawnPointSystem)}, after: new [] { typeof(ContainerSpawnPointSystem)}); SubscribeLocalEvent(OnStationPostInit); @@ -335,8 +335,7 @@ public void HandlePlayerSpawning(PlayerSpawningEvent ev) if (ev.SpawnResult != null) return; - if (ev.HumanoidCharacterProfile?.SpawnPriority != SpawnPriorityPreference.Arrivals) - return; + // We use arrivals as the default spawn so don't check for job prio. // Only works on latejoin even if enabled. if (!Enabled || _ticker.RunLevel != GameRunLevel.InRound) diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs index f30cab253a0e..e544c1538d15 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs @@ -70,11 +70,11 @@ public sealed partial class ShuttleSystem private readonly HashSet _lookupEnts = new(); private readonly HashSet _immuneEnts = new(); + private readonly HashSet> _noFtls = new(); private EntityQuery _bodyQuery; private EntityQuery _buckleQuery; - private EntityQuery _beaconQuery; - private EntityQuery _ghostQuery; + private EntityQuery _immuneQuery; private EntityQuery _physicsQuery; private EntityQuery _statusQuery; private EntityQuery _xformQuery; @@ -86,8 +86,7 @@ private void InitializeFTL() _bodyQuery = GetEntityQuery(); _buckleQuery = GetEntityQuery(); - _beaconQuery = GetEntityQuery(); - _ghostQuery = GetEntityQuery(); + _immuneQuery = GetEntityQuery(); _physicsQuery = GetEntityQuery(); _statusQuery = GetEntityQuery(); _xformQuery = GetEntityQuery(); @@ -102,7 +101,7 @@ private void InitializeFTL() private void OnFtlShutdown(Entity ent, ref ComponentShutdown args) { - Del(ent.Comp.VisualizerEntity); + QueueDel(ent.Comp.VisualizerEntity); ent.Comp.VisualizerEntity = null; } @@ -404,7 +403,12 @@ private void UpdateFTLStarting(Entity entity) // Offset the start by buffer range just to avoid overlap. var ftlStart = new EntityCoordinates(ftlMap, new Vector2(_index + width / 2f, 0f) - shuttleCenter); + // Store the matrix for the grid prior to movement. This means any entities we need to leave behind we can make sure their positions are updated. + // Setting the entity to map directly may run grid traversal (at least at time of writing this). + var oldMapUid = xform.MapUid; + var oldGridMatrix = _transform.GetWorldMatrix(xform); _transform.SetCoordinates(entity.Owner, ftlStart); + LeaveNoFTLBehind((entity.Owner, xform), oldGridMatrix, oldMapUid); // Reset rotation so they always face the same direction. xform.LocalRotation = Angle.Zero; @@ -476,6 +480,9 @@ private void UpdateFTLArriving(Entity entity) MapId mapId; + QueueDel(entity.Comp1.VisualizerEntity); + entity.Comp1.VisualizerEntity = null; + if (!Exists(entity.Comp1.TargetCoordinates.EntityId)) { // Uhh good luck @@ -628,6 +635,31 @@ private void DoTheDinosaur(TransformComponent xform) } } + private void LeaveNoFTLBehind(Entity grid, Matrix3x2 oldGridMatrix, EntityUid? oldMapUid) + { + if (oldMapUid == null) + return; + + _noFtls.Clear(); + var oldGridRotation = oldGridMatrix.Rotation(); + _lookup.GetGridEntities(grid.Owner, _noFtls); + + foreach (var childUid in _noFtls) + { + if (!_xformQuery.TryComp(childUid, out var childXform)) + continue; + + // If we're not parented directly to the grid the matrix may be wrong. + var relative = _physics.GetRelativePhysicsTransform(childUid.Owner, (grid.Owner, grid.Comp)); + + _transform.SetCoordinates( + childUid, + childXform, + new EntityCoordinates(oldMapUid.Value, + Vector2.Transform(relative.Position, oldGridMatrix)), rotation: relative.Quaternion2D.Angle + oldGridRotation); + } + } + private void KnockOverKids(TransformComponent xform, ref ValueList toKnock) { // Not recursive because probably not necessary? If we need it to be that's why this method is separate. @@ -924,8 +956,11 @@ private void Smimsh(EntityUid uid, FixturesComponent? manager = null, MapGridCom if (!Resolve(uid, ref manager, ref grid, ref xform) || xform.MapUid == null) return; + if (!TryComp(xform.MapUid, out BroadphaseComponent? lookup)) + return; + // Flatten anything not parented to a grid. - var transform = _physics.GetPhysicsTransform(uid, xform); + var transform = _physics.GetRelativePhysicsTransform((uid, xform), xform.MapUid.Value); var aabbs = new List(manager.Fixtures.Count); var tileSet = new List<(Vector2i, Tile)>(); @@ -946,7 +981,8 @@ private void Smimsh(EntityUid uid, FixturesComponent? manager = null, MapGridCom _biomes.ReserveTiles(xform.MapUid.Value, aabb, tileSet); _lookupEnts.Clear(); _immuneEnts.Clear(); - _lookup.GetEntitiesIntersecting(xform.MapUid.Value, aabb, _lookupEnts, LookupFlags.Uncontained); + // TODO: Ideally we'd query first BEFORE moving grid but needs adjustments above. + _lookup.GetLocalEntitiesIntersecting(xform.MapUid.Value, fixture.Shape, transform, _lookupEnts, flags: LookupFlags.Uncontained, lookup: lookup); foreach (var ent in _lookupEnts) { @@ -955,7 +991,13 @@ private void Smimsh(EntityUid uid, FixturesComponent? manager = null, MapGridCom continue; } - if (_ghostQuery.HasComponent(ent) || _beaconQuery.HasComponent(ent)) + // If it's on our grid ignore it. + if (!_xformQuery.TryComp(ent, out var childXform) || childXform.GridUid == uid) + { + continue; + } + + if (_immuneQuery.HasComponent(ent)) { continue; } @@ -969,9 +1011,6 @@ private void Smimsh(EntityUid uid, FixturesComponent? manager = null, MapGridCom continue; } - if (HasComp(ent)) - continue; - QueueDel(ent); } } diff --git a/Content.Server/Speech/EntitySystems/SkeletonAccentSystem.cs b/Content.Server/Speech/EntitySystems/SkeletonAccentSystem.cs index d143c25fdbab..1b773f1a5ac1 100644 --- a/Content.Server/Speech/EntitySystems/SkeletonAccentSystem.cs +++ b/Content.Server/Speech/EntitySystems/SkeletonAccentSystem.cs @@ -7,29 +7,11 @@ namespace Content.Server.Speech.EntitySystems; public sealed partial class SkeletonAccentSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly ReplacementAccentSystem _replacement = default!; [GeneratedRegex(@"(? DirectReplacements = new() - { - { "fuck you", "I've got a BONE to pick with you" }, - { "fucked", "boned"}, - { "fuck", "RATTLE RATTLE" }, - { "fck", "RATTLE RATTLE" }, - { "shit", "RATTLE RATTLE" }, // Capitalize RATTLE RATTLE regardless of original message case. - { "definitely", "make no bones about it" }, - { "absolutely", "make no bones about it" }, - { "afraid", "rattled"}, - { "scared", "rattled"}, - { "spooked", "rattled"}, - { "shocked", "rattled"}, - { "killed", "skeletonized"}, - { "humorous", "humerus"}, - { "to be a", "tibia"}, - { "under", "ulna"} - }; - public override void Initialize() { base.Initialize(); @@ -50,11 +32,8 @@ public string Accentuate(string message, SkeletonAccentComponent component) // At the start of words, any non-vowel + "one" becomes "bone", e.g. tone -> bone ; lonely -> bonely; clone -> clone (remains unchanged). msg = BoneRegex().Replace(msg, "bone"); - // Direct word/phrase replacements: - foreach (var (first, replace) in DirectReplacements) - { - msg = Regex.Replace(msg, $@"(? Loc.GetString("ion-storm-law-crew-must-go", ("who", crewAll), ("area", area)), 27 => Loc.GetString("ion-storm-law-crew-only-1", ("who", crew1), ("part", part)), 28 => Loc.GetString("ion-storm-law-crew-only-2", ("who", crew1), ("other", crew2), ("part", part)), - 29 => Loc.GetString("ion-storm-law-crew-only-subjects", ("adjective", adjective), ("subjects", RobustRandom.Prob(0.5f) ? objectsThreats : "PEOPLE"), ("part", part)), + 29 => Loc.GetString("ion-storm-law-crew-only-subjects", ("adjective", adjective), ("subjects", subjects), ("part", part)), 30 => Loc.GetString("ion-storm-law-crew-must-do", ("must", must), ("part", part)), 31 => Loc.GetString("ion-storm-law-crew-must-have", ("adjective", adjective), ("objects", objects), ("part", part)), 32 => Loc.GetString("ion-storm-law-crew-must-eat", ("who", who), ("adjective", adjective), ("food", food), ("part", part)), diff --git a/Content.Server/StationRecords/Systems/StationRecordsSystem.cs b/Content.Server/StationRecords/Systems/StationRecordsSystem.cs index c7d566546467..e941e65c4157 100644 --- a/Content.Server/StationRecords/Systems/StationRecordsSystem.cs +++ b/Content.Server/StationRecords/Systems/StationRecordsSystem.cs @@ -1,6 +1,9 @@ using System.Diagnostics.CodeAnalysis; +using System.IO; +using Content.Server.Access.Systems; using Content.Server.Forensics; using Content.Server.GameTicking; +using Content.Shared.Access.Components; using Content.Shared.Inventory; using Content.Shared.PDA; using Content.Shared.Preferences; @@ -35,12 +38,14 @@ public sealed class StationRecordsSystem : SharedStationRecordsSystem [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly StationRecordKeyStorageSystem _keyStorage = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IdCardSystem _idCard = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnPlayerSpawn); + SubscribeLocalEvent(OnRename); } private void OnPlayerSpawn(PlayerSpawnCompleteEvent args) @@ -51,6 +56,30 @@ private void OnPlayerSpawn(PlayerSpawnCompleteEvent args) CreateGeneralRecord(args.Station, args.Mob, args.Profile, args.JobId, stationRecords); } + private void OnRename(ref EntityRenamedEvent ev) + { + // When a player gets renamed their card gets changed to match. + // Unfortunately this means that an event is called for it as well, and since TryFindIdCard will succeed if the + // given entity is a card and the card itself is the key the record will be mistakenly renamed to the card's name + // if we don't return early. + if (HasComp(ev.Uid)) + return; + + if (_idCard.TryFindIdCard(ev.Uid, out var idCard)) + { + if (TryComp(idCard, out StationRecordKeyStorageComponent? keyStorage) + && keyStorage.Key is {} key) + { + if (TryGetRecord(key, out var generalRecord)) + { + generalRecord.Name = ev.NewName; + } + + Synchronize(key); + } + } + } + private void CreateGeneralRecord(EntityUid station, EntityUid player, HumanoidCharacterProfile profile, string? jobId, StationRecordsComponent records) { diff --git a/Content.Server/Traits/TraitSystem.cs b/Content.Server/Traits/TraitSystem.cs index 3bd540a3049a..e19f736f0676 100644 --- a/Content.Server/Traits/TraitSystem.cs +++ b/Content.Server/Traits/TraitSystem.cs @@ -1,6 +1,7 @@ using Content.Server.GameTicking; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; +using Content.Shared.Roles; using Content.Shared.Traits; using Content.Shared.Whitelist; using Robust.Shared.Prototypes; @@ -24,6 +25,14 @@ public override void Initialize() // When the player is spawned in, add all trait components selected during character creation private void OnPlayerSpawnComplete(PlayerSpawnCompleteEvent args) { + // Check if player's job allows to apply traits + if (args.JobId == null || + !_prototypeManager.TryIndex(args.JobId ?? string.Empty, out var protoJob) || + !protoJob.ApplyTraits) + { + return; + } + foreach (var traitId in args.Profile.TraitPreferences) { if (!_prototypeManager.TryIndex(traitId, out var traitPrototype)) diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ChemicalPuddleArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ChemicalPuddleArtifactSystem.cs index cd312797ce73..542d8bb84cc2 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ChemicalPuddleArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ChemicalPuddleArtifactSystem.cs @@ -18,7 +18,7 @@ public sealed class ChemicalPuddleArtifactSystem : EntitySystem /// The key for the node data entry containing /// the chemicals that the puddle is made of. /// - public const string NodeDataChemicalList = "nodeDataSpawnAmount"; + public const string NodeDataChemicalList = "nodeDataChemicalList"; /// public override void Initialize() diff --git a/Content.Shared/Access/Systems/SharedIdCardSystem.cs b/Content.Shared/Access/Systems/SharedIdCardSystem.cs index 5a90d4ea3551..8bdc548e353a 100644 --- a/Content.Shared/Access/Systems/SharedIdCardSystem.cs +++ b/Content.Shared/Access/Systems/SharedIdCardSystem.cs @@ -25,6 +25,19 @@ public override void Initialize() SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnTryGetIdentityShortInfo); + SubscribeLocalEvent(OnRename); + } + + private void OnRename(ref EntityRenamedEvent ev) + { + // When a player gets renamed their id card is renamed as well to match. + // Unfortunately since TryFindIdCard will succeed if the entity is also a card this means that the card will + // keep renaming itself unless we return early. + if (HasComp(ev.Uid)) + return; + + if (TryFindIdCard(ev.Uid, out var idCard)) + TryChangeFullName(idCard, ev.NewName, idCard); } private void OnMapInit(EntityUid uid, IdCardComponent id, MapInitEvent args) diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs index 83c24016ceb1..7f6c39eafc06 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs @@ -242,8 +242,9 @@ private bool CanBuckle(EntityUid buckleUid, if (_whitelistSystem.IsWhitelistFail(strapComp.Whitelist, buckleUid) || _whitelistSystem.IsBlacklistPass(strapComp.Blacklist, buckleUid)) { - if (_netManager.IsServer && popup && user != null) - _popup.PopupEntity(Loc.GetString("buckle-component-cannot-fit-message"), user.Value, user.Value, PopupType.Medium); + if (popup) + _popup.PopupClient(Loc.GetString("buckle-component-cannot-fit-message"), user, PopupType.Medium); + return false; } @@ -261,23 +262,24 @@ private bool CanBuckle(EntityUid buckleUid, if (user != null && !HasComp(user)) { - // PopupPredicted when - if (_netManager.IsServer && popup) - _popup.PopupEntity(Loc.GetString("buckle-component-no-hands-message"), user.Value, user.Value); + if (popup) + _popup.PopupClient(Loc.GetString("buckle-component-no-hands-message"), user); + return false; } if (buckleComp.Buckled) { - if (_netManager.IsClient || popup || user == null) - return false; - - var message = Loc.GetString(buckleUid == user + if (popup) + { + var message = Loc.GetString(buckleUid == user ? "buckle-component-already-buckled-message" : "buckle-component-other-already-buckled-message", ("owner", Identity.Entity(buckleUid, EntityManager))); - _popup.PopupEntity(message, user.Value, user.Value); + _popup.PopupClient(message, user); + } + return false; } @@ -291,29 +293,30 @@ private bool CanBuckle(EntityUid buckleUid, continue; } - if (_netManager.IsClient || popup || user == null) - return false; - - var message = Loc.GetString(buckleUid == user + if (popup) + { + var message = Loc.GetString(buckleUid == user ? "buckle-component-cannot-buckle-message" : "buckle-component-other-cannot-buckle-message", ("owner", Identity.Entity(buckleUid, EntityManager))); - _popup.PopupEntity(message, user.Value, user.Value); + _popup.PopupClient(message, user); + } + return false; } if (!StrapHasSpace(strapUid, buckleComp, strapComp)) { - if (_netManager.IsClient || popup || user == null) - return false; - - var message = Loc.GetString(buckleUid == user - ? "buckle-component-cannot-fit-message" - : "buckle-component-other-cannot-fit-message", + if (popup) + { + var message = Loc.GetString(buckleUid == user + ? "buckle-component-cannot-buckle-message" + : "buckle-component-other-cannot-buckle-message", ("owner", Identity.Entity(buckleUid, EntityManager))); - _popup.PopupEntity(message, user.Value, user.Value); + _popup.PopupClient(message, user); + } return false; } diff --git a/Content.Shared/Buckle/SharedBuckleSystem.cs b/Content.Shared/Buckle/SharedBuckleSystem.cs index d190f685ed0e..da1d111f977f 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.cs @@ -9,7 +9,6 @@ using Content.Shared.Standing; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; -using Robust.Shared.Network; using Robust.Shared.Physics.Systems; using Robust.Shared.Player; using Robust.Shared.Timing; @@ -18,7 +17,6 @@ namespace Content.Shared.Buckle; public abstract partial class SharedBuckleSystem : EntitySystem { - [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ISharedPlayerManager _playerManager = default!; diff --git a/Content.Shared/Chemistry/Components/InjectorComponent.cs b/Content.Shared/Chemistry/Components/InjectorComponent.cs index 1f2716356c50..17a65ef1c179 100644 --- a/Content.Shared/Chemistry/Components/InjectorComponent.cs +++ b/Content.Shared/Chemistry/Components/InjectorComponent.cs @@ -1,7 +1,9 @@ using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Chemistry.Reagent; using Content.Shared.DoAfter; using Content.Shared.FixedPoint; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Chemistry.Components; @@ -88,6 +90,14 @@ public sealed partial class InjectorComponent : Component [DataField] public InjectorToggleMode ToggleState = InjectorToggleMode.Draw; + /// + /// Reagents that are allowed to be within this injector. + /// If a solution has both allowed and non-allowed reagents, only allowed reagents will be drawn into this injector. + /// A null ReagentWhitelist indicates all reagents are allowed. + /// + [DataField] + public List>? ReagentWhitelist = null; + #region Arguments for injection doafter /// diff --git a/Content.Shared/Chemistry/Components/Solution.cs b/Content.Shared/Chemistry/Components/Solution.cs index fc25781005f8..c65ba0e80ea3 100644 --- a/Content.Shared/Chemistry/Components/Solution.cs +++ b/Content.Shared/Chemistry/Components/Solution.cs @@ -612,7 +612,7 @@ public Solution SplitSolutionWithout(FixedPoint2 toTake, params string[] exclude } /// - /// Splits a solution without the specified reagent prototypes. + /// Splits a solution with only the specified reagent prototypes. /// public Solution SplitSolutionWithOnly(FixedPoint2 toTake, params string[] includedPrototypes) { diff --git a/Content.Shared/Doors/Components/AirlockComponent.cs b/Content.Shared/Doors/Components/AirlockComponent.cs index b2fa7574f76f..6577b1942ac9 100644 --- a/Content.Shared/Doors/Components/AirlockComponent.cs +++ b/Content.Shared/Doors/Components/AirlockComponent.cs @@ -48,7 +48,7 @@ public sealed partial class AirlockComponent : Component /// /// Whether the airlock should auto close. This value is reset every time the airlock closes. /// - [ViewVariables(VVAccess.ReadWrite)] + [DataField, AutoNetworkedField] public bool AutoClose = true; /// diff --git a/Content.Shared/Doors/Systems/SharedAirlockSystem.cs b/Content.Shared/Doors/Systems/SharedAirlockSystem.cs index 5a6d45d9ec0b..c0c274207b62 100644 --- a/Content.Shared/Doors/Systems/SharedAirlockSystem.cs +++ b/Content.Shared/Doors/Systems/SharedAirlockSystem.cs @@ -56,7 +56,10 @@ private void OnStateChanged(EntityUid uid, AirlockComponent component, DoorState // Make sure the airlock auto closes again next time it is opened if (args.State == DoorState.Closed) + { component.AutoClose = true; + Dirty(uid, component); + } } private void OnBeforeDoorOpened(EntityUid uid, AirlockComponent component, BeforeDoorOpenedEvent args) diff --git a/Content.Shared/Explosion/Components/OnUseTimerTriggerComponent.cs b/Content.Shared/Explosion/Components/OnUseTimerTriggerComponent.cs index c4e6e787a4af..983b8a31ee67 100644 --- a/Content.Shared/Explosion/Components/OnUseTimerTriggerComponent.cs +++ b/Content.Shared/Explosion/Components/OnUseTimerTriggerComponent.cs @@ -1,3 +1,5 @@ +using System.Linq; +using Content.Shared.Guidebook; using Robust.Shared.Audio; using Robust.Shared.GameStates; @@ -50,5 +52,15 @@ public sealed partial class OnUseTimerTriggerComponent : Component /// Whether or not to show the user a popup when starting the timer. /// [DataField] public bool DoPopup = true; + + #region GuidebookData + + [GuidebookData] + public float? ShortestDelayOption => DelayOptions?.Min(); + + [GuidebookData] + public float? LongestDelayOption => DelayOptions?.Max(); + + #endregion GuidebookData } } diff --git a/Content.Shared/Guidebook/Events.cs b/Content.Shared/Guidebook/Events.cs new file mode 100644 index 000000000000..e43bf4392c5a --- /dev/null +++ b/Content.Shared/Guidebook/Events.cs @@ -0,0 +1,25 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Guidebook; + +/// +/// Raised by the client on GuidebookDataSystem Initialize to request a +/// full set of guidebook data from the server. +/// +[Serializable, NetSerializable] +public sealed class RequestGuidebookDataEvent : EntityEventArgs { } + +/// +/// Raised by the server at a specific client in response to . +/// Also raised by the server at ALL clients when prototype data is hot-reloaded. +/// +[Serializable, NetSerializable] +public sealed class UpdateGuidebookDataEvent : EntityEventArgs +{ + public GuidebookData Data; + + public UpdateGuidebookDataEvent(GuidebookData data) + { + Data = data; + } +} diff --git a/Content.Shared/Guidebook/GuidebookData.cs b/Content.Shared/Guidebook/GuidebookData.cs new file mode 100644 index 000000000000..703940ed1ee5 --- /dev/null +++ b/Content.Shared/Guidebook/GuidebookData.cs @@ -0,0 +1,99 @@ +using System.Collections.Frozen; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.Shared.Guidebook; + +/// +/// Used by GuidebookDataSystem to hold data extracted from prototype values, +/// both for storage and for network transmission. +/// +[Serializable, NetSerializable] +[DataDefinition] +public sealed partial class GuidebookData +{ + /// + /// Total number of data values stored. + /// + [DataField] + public int Count { get; private set; } + + /// + /// The data extracted by the system. + /// + /// + /// Structured as PrototypeName, ComponentName, FieldName, Value + /// + [DataField] + public Dictionary>> Data = []; + + /// + /// The data extracted by the system, converted to a FrozenDictionary for faster lookup. + /// + public FrozenDictionary>> FrozenData; + + /// + /// Has the data been converted to a FrozenDictionary for faster lookup? + /// This should only be done on clients, as FrozenDictionary isn't serializable. + /// + public bool IsFrozen; + + /// + /// Adds a new value using the given identifiers. + /// + public void AddData(string prototype, string component, string field, object? value) + { + if (IsFrozen) + throw new InvalidOperationException("Attempted to add data to GuidebookData while it is frozen!"); + Data.GetOrNew(prototype).GetOrNew(component).Add(field, value); + Count++; + } + + /// + /// Attempts to retrieve a value using the given identifiers. + /// + /// true if the value was retrieved, otherwise false + public bool TryGetValue(string prototype, string component, string field, out object? value) + { + if (!IsFrozen) + throw new InvalidOperationException("Freeze the GuidebookData before calling TryGetValue!"); + + // Look in frozen dictionary + if (FrozenData.TryGetValue(prototype, out var p) + && p.TryGetValue(component, out var c) + && c.TryGetValue(field, out value)) + { + return true; + } + + value = null; + return false; + } + + /// + /// Deletes all data. + /// + public void Clear() + { + Data.Clear(); + Count = 0; + IsFrozen = false; + } + + public void Freeze() + { + var protos = new Dictionary>>(); + foreach (var (protoId, protoData) in Data) + { + var comps = new Dictionary>(); + foreach (var (compId, compData) in protoData) + { + comps.Add(compId, FrozenDictionary.ToFrozenDictionary(compData)); + } + protos.Add(protoId, FrozenDictionary.ToFrozenDictionary(comps)); + } + FrozenData = FrozenDictionary.ToFrozenDictionary(protos); + Data.Clear(); + IsFrozen = true; + } +} diff --git a/Content.Shared/Guidebook/GuidebookDataAttribute.cs b/Content.Shared/Guidebook/GuidebookDataAttribute.cs new file mode 100644 index 000000000000..2b83892b8817 --- /dev/null +++ b/Content.Shared/Guidebook/GuidebookDataAttribute.cs @@ -0,0 +1,12 @@ +namespace Content.Shared.Guidebook; + +/// +/// Indicates that GuidebookDataSystem should include this field/property when +/// scanning entity prototypes for values to extract. +/// +/// +/// Note that this will not work for client-only components, because the data extraction +/// is done on the server (it uses reflection, which is blocked by the sandbox on clients). +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] +public sealed class GuidebookDataAttribute : Attribute { } diff --git a/Content.Shared/Mind/SharedMindSystem.cs b/Content.Shared/Mind/SharedMindSystem.cs index 524b176f1abc..8047df7dc8d7 100644 --- a/Content.Shared/Mind/SharedMindSystem.cs +++ b/Content.Shared/Mind/SharedMindSystem.cs @@ -39,6 +39,7 @@ public override void Initialize() SubscribeLocalEvent(OnVisitingTerminating); SubscribeLocalEvent(OnReset); SubscribeLocalEvent(OnMindStartup); + SubscribeLocalEvent(OnRenamed); } public override void Shutdown() @@ -181,6 +182,12 @@ private void OnSuicide(EntityUid uid, MindContainerComponent component, SuicideE args.Handled = true; } + private void OnRenamed(Entity ent, ref EntityRenamedEvent args) + { + ent.Comp.CharacterName = args.NewName; + Dirty(ent); + } + public EntityUid? GetMind(EntityUid uid, MindContainerComponent? mind = null) { if (!Resolve(uid, ref mind)) diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index 5f35adb33374..6392956d6320 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -438,7 +438,7 @@ public bool TryStartPull(EntityUid pullerUid, EntityUid pullableUid, if (!CanPull(pullerUid, pullableUid)) return false; - if (!HasComp(pullerUid) || !TryComp(pullableUid, out PhysicsComponent? pullablePhysics)) + if (!TryComp(pullerUid, out PhysicsComponent? pullerPhysics) || !TryComp(pullableUid, out PhysicsComponent? pullablePhysics)) return false; // Ensure that the puller is not currently pulling anything. @@ -485,17 +485,19 @@ public bool TryStartPull(EntityUid pullerUid, EntityUid pullableUid, // joint state handling will manage its own state if (!_timing.ApplyingState) { - // Joint startup - var union = _physics.GetHardAABB(pullerUid).Union(_physics.GetHardAABB(pullableUid, body: pullablePhysics)); - var length = Math.Max(union.Size.X, union.Size.Y) * 0.75f; - - var joint = _joints.CreateDistanceJoint(pullableUid, pullerUid, id: pullableComp.PullJointId); + var joint = _joints.CreateDistanceJoint(pullableUid, pullerUid, + pullablePhysics.LocalCenter, pullerPhysics.LocalCenter, + id: pullableComp.PullJointId); joint.CollideConnected = false; // This maximum has to be there because if the object is constrained too closely, the clamping goes backwards and asserts. - joint.MaxLength = Math.Max(1.0f, length); - joint.Length = length * 0.75f; + // Internally, the joint length has been set to the distance between the pivots. + // Add an additional 15cm (pretty arbitrary) to the maximum length for the hard limit. + joint.MaxLength = joint.Length + 0.15f; joint.MinLength = 0f; - joint.Stiffness = 1f; + // Set the spring stiffness to zero. The joint won't have any effect provided + // the current length is beteen MinLength and MaxLength. At those limits, the + // joint will have infinite stiffness. + joint.Stiffness = 0f; _physics.SetFixedRotation(pullableUid, pullableComp.FixedRotationOnPull, body: pullablePhysics); } diff --git a/Content.Shared/Movement/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs index c41db21b01eb..472d56b1d692 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.cs @@ -24,492 +24,491 @@ using Robust.Shared.Utility; using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent; -namespace Content.Shared.Movement.Systems +namespace Content.Shared.Movement.Systems; + +/// +/// Handles player and NPC mob movement. +/// NPCs are handled server-side only. +/// +public abstract partial class SharedMoverController : VirtualController { + [Dependency] private readonly IConfigurationManager _configManager = default!; + [Dependency] protected readonly IGameTiming Timing = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; + [Dependency] private readonly SharedGravitySystem _gravity = default!; + [Dependency] protected readonly SharedPhysicsSystem Physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly TagSystem _tags = default!; + + protected EntityQuery MoverQuery; + protected EntityQuery MobMoverQuery; + protected EntityQuery RelayTargetQuery; + protected EntityQuery ModifierQuery; + protected EntityQuery PhysicsQuery; + protected EntityQuery RelayQuery; + protected EntityQuery PullableQuery; + protected EntityQuery XformQuery; + protected EntityQuery CanMoveInAirQuery; + protected EntityQuery NoRotateQuery; + protected EntityQuery FootstepModifierQuery; + protected EntityQuery MapGridQuery; + + /// + /// + /// + private float _stopSpeed; + + private bool _relativeMovement; + /// - /// Handles player and NPC mob movement. - /// NPCs are handled server-side only. + /// Cache the mob movement calculation to re-use elsewhere. /// - public abstract partial class SharedMoverController : VirtualController + public Dictionary UsedMobMovement = new(); + + public override void Initialize() { - [Dependency] private readonly IConfigurationManager _configManager = default!; - [Dependency] protected readonly IGameTiming Timing = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly InventorySystem _inventory = default!; - [Dependency] private readonly MobStateSystem _mobState = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedContainerSystem _container = default!; - [Dependency] private readonly SharedMapSystem _mapSystem = default!; - [Dependency] private readonly SharedGravitySystem _gravity = default!; - [Dependency] protected readonly SharedPhysicsSystem Physics = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly TagSystem _tags = default!; - - protected EntityQuery MoverQuery; - protected EntityQuery MobMoverQuery; - protected EntityQuery RelayTargetQuery; - protected EntityQuery ModifierQuery; - protected EntityQuery PhysicsQuery; - protected EntityQuery RelayQuery; - protected EntityQuery PullableQuery; - protected EntityQuery XformQuery; - protected EntityQuery CanMoveInAirQuery; - protected EntityQuery NoRotateQuery; - protected EntityQuery FootstepModifierQuery; - protected EntityQuery MapGridQuery; - - /// - /// - /// - private float _stopSpeed; - - private bool _relativeMovement; - - /// - /// Cache the mob movement calculation to re-use elsewhere. - /// - public Dictionary UsedMobMovement = new(); - - public override void Initialize() - { - base.Initialize(); - - MoverQuery = GetEntityQuery(); - MobMoverQuery = GetEntityQuery(); - ModifierQuery = GetEntityQuery(); - RelayTargetQuery = GetEntityQuery(); - PhysicsQuery = GetEntityQuery(); - RelayQuery = GetEntityQuery(); - PullableQuery = GetEntityQuery(); - XformQuery = GetEntityQuery(); - NoRotateQuery = GetEntityQuery(); - CanMoveInAirQuery = GetEntityQuery(); - FootstepModifierQuery = GetEntityQuery(); - MapGridQuery = GetEntityQuery(); - - InitializeInput(); - InitializeRelay(); - Subs.CVar(_configManager, CCVars.RelativeMovement, value => _relativeMovement = value, true); - Subs.CVar(_configManager, CCVars.StopSpeed, value => _stopSpeed = value, true); - UpdatesBefore.Add(typeof(TileFrictionController)); - } + base.Initialize(); + + MoverQuery = GetEntityQuery(); + MobMoverQuery = GetEntityQuery(); + ModifierQuery = GetEntityQuery(); + RelayTargetQuery = GetEntityQuery(); + PhysicsQuery = GetEntityQuery(); + RelayQuery = GetEntityQuery(); + PullableQuery = GetEntityQuery(); + XformQuery = GetEntityQuery(); + NoRotateQuery = GetEntityQuery(); + CanMoveInAirQuery = GetEntityQuery(); + FootstepModifierQuery = GetEntityQuery(); + MapGridQuery = GetEntityQuery(); + + InitializeInput(); + InitializeRelay(); + Subs.CVar(_configManager, CCVars.RelativeMovement, value => _relativeMovement = value, true); + Subs.CVar(_configManager, CCVars.StopSpeed, value => _stopSpeed = value, true); + UpdatesBefore.Add(typeof(TileFrictionController)); + } - public override void Shutdown() - { - base.Shutdown(); - ShutdownInput(); - } + public override void Shutdown() + { + base.Shutdown(); + ShutdownInput(); + } - public override void UpdateAfterSolve(bool prediction, float frameTime) - { - base.UpdateAfterSolve(prediction, frameTime); - UsedMobMovement.Clear(); - } + public override void UpdateAfterSolve(bool prediction, float frameTime) + { + base.UpdateAfterSolve(prediction, frameTime); + UsedMobMovement.Clear(); + } - /// - /// Movement while considering actionblockers, weightlessness, etc. - /// - protected void HandleMobMovement( - EntityUid uid, - InputMoverComponent mover, - EntityUid physicsUid, - PhysicsComponent physicsComponent, - TransformComponent xform, - float frameTime) + /// + /// Movement while considering actionblockers, weightlessness, etc. + /// + protected void HandleMobMovement( + EntityUid uid, + InputMoverComponent mover, + EntityUid physicsUid, + PhysicsComponent physicsComponent, + TransformComponent xform, + float frameTime) + { + var canMove = mover.CanMove; + if (RelayTargetQuery.TryGetComponent(uid, out var relayTarget)) { - var canMove = mover.CanMove; - if (RelayTargetQuery.TryGetComponent(uid, out var relayTarget)) + if (_mobState.IsIncapacitated(relayTarget.Source) || + TryComp(relayTarget.Source, out _) || + !MoverQuery.TryGetComponent(relayTarget.Source, out var relayedMover)) { - if (_mobState.IsIncapacitated(relayTarget.Source) || - TryComp(relayTarget.Source, out _) || - !MoverQuery.TryGetComponent(relayTarget.Source, out var relayedMover)) - { - canMove = false; - } - else - { - mover.RelativeEntity = relayedMover.RelativeEntity; - mover.RelativeRotation = relayedMover.RelativeRotation; - mover.TargetRelativeRotation = relayedMover.TargetRelativeRotation; - } + canMove = false; } - - // Update relative movement - if (mover.LerpTarget < Timing.CurTime) + else { - if (TryUpdateRelative(mover, xform)) - { - Dirty(uid, mover); - } + mover.RelativeEntity = relayedMover.RelativeEntity; + mover.RelativeRotation = relayedMover.RelativeRotation; + mover.TargetRelativeRotation = relayedMover.TargetRelativeRotation; } + } - LerpRotation(uid, mover, frameTime); - - if (!canMove - || physicsComponent.BodyStatus != BodyStatus.OnGround && !CanMoveInAirQuery.HasComponent(uid) - || PullableQuery.TryGetComponent(uid, out var pullable) && pullable.BeingPulled) + // Update relative movement + if (mover.LerpTarget < Timing.CurTime) + { + if (TryUpdateRelative(mover, xform)) { - UsedMobMovement[uid] = false; - return; + Dirty(uid, mover); } + } + LerpRotation(uid, mover, frameTime); - UsedMobMovement[uid] = true; - // Specifically don't use mover.Owner because that may be different to the actual physics body being moved. - var weightless = _gravity.IsWeightless(physicsUid, physicsComponent, xform); - var (walkDir, sprintDir) = GetVelocityInput(mover); - var touching = false; - - // Handle wall-pushes. - if (weightless) - { - if (xform.GridUid != null) - touching = true; + if (!canMove + || physicsComponent.BodyStatus != BodyStatus.OnGround && !CanMoveInAirQuery.HasComponent(uid) + || PullableQuery.TryGetComponent(uid, out var pullable) && pullable.BeingPulled) + { + UsedMobMovement[uid] = false; + return; + } - if (!touching) - { - var ev = new CanWeightlessMoveEvent(uid); - RaiseLocalEvent(uid, ref ev, true); - // No gravity: is our entity touching anything? - touching = ev.CanMove; - if (!touching && TryComp(uid, out var mobMover)) - touching |= IsAroundCollider(PhysicsSystem, xform, mobMover, physicsUid, physicsComponent); - } - } + UsedMobMovement[uid] = true; + // Specifically don't use mover.Owner because that may be different to the actual physics body being moved. + var weightless = _gravity.IsWeightless(physicsUid, physicsComponent, xform); + var (walkDir, sprintDir) = GetVelocityInput(mover); + var touching = false; - // Get current tile def for things like speed/friction mods - ContentTileDefinition? tileDef = null; + // Handle wall-pushes. + if (weightless) + { + if (xform.GridUid != null) + touching = true; - // Don't bother getting the tiledef here if we're weightless or in-air - // since no tile-based modifiers should be applying in that situation - if (MapGridQuery.TryComp(xform.GridUid, out var gridComp) - && _mapSystem.TryGetTileRef(xform.GridUid.Value, gridComp, xform.Coordinates, out var tile) - && !(weightless || physicsComponent.BodyStatus == BodyStatus.InAir)) + if (!touching) { - tileDef = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId]; + var ev = new CanWeightlessMoveEvent(uid); + RaiseLocalEvent(uid, ref ev, true); + // No gravity: is our entity touching anything? + touching = ev.CanMove; + + if (!touching && TryComp(uid, out var mobMover)) + touching |= IsAroundCollider(PhysicsSystem, xform, mobMover, physicsUid, physicsComponent); } + } - // Regular movement. - // Target velocity. - // This is relative to the map / grid we're on. - var moveSpeedComponent = ModifierQuery.CompOrNull(uid); + // Get current tile def for things like speed/friction mods + ContentTileDefinition? tileDef = null; - var walkSpeed = moveSpeedComponent?.CurrentWalkSpeed ?? MovementSpeedModifierComponent.DefaultBaseWalkSpeed; - var sprintSpeed = moveSpeedComponent?.CurrentSprintSpeed ?? MovementSpeedModifierComponent.DefaultBaseSprintSpeed; + // Don't bother getting the tiledef here if we're weightless or in-air + // since no tile-based modifiers should be applying in that situation + if (MapGridQuery.TryComp(xform.GridUid, out var gridComp) + && _mapSystem.TryGetTileRef(xform.GridUid.Value, gridComp, xform.Coordinates, out var tile) + && !(weightless || physicsComponent.BodyStatus == BodyStatus.InAir)) + { + tileDef = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId]; + } - var total = walkDir * walkSpeed + sprintDir * sprintSpeed; + // Regular movement. + // Target velocity. + // This is relative to the map / grid we're on. + var moveSpeedComponent = ModifierQuery.CompOrNull(uid); - var parentRotation = GetParentGridAngle(mover); - var worldTotal = _relativeMovement ? parentRotation.RotateVec(total) : total; + var walkSpeed = moveSpeedComponent?.CurrentWalkSpeed ?? MovementSpeedModifierComponent.DefaultBaseWalkSpeed; + var sprintSpeed = moveSpeedComponent?.CurrentSprintSpeed ?? MovementSpeedModifierComponent.DefaultBaseSprintSpeed; - DebugTools.Assert(MathHelper.CloseToPercent(total.Length(), worldTotal.Length())); + var total = walkDir * walkSpeed + sprintDir * sprintSpeed; - var velocity = physicsComponent.LinearVelocity; - float friction; - float weightlessModifier; - float accel; + var parentRotation = GetParentGridAngle(mover); + var worldTotal = _relativeMovement ? parentRotation.RotateVec(total) : total; - if (weightless) - { - if (gridComp == null && !MapGridQuery.HasComp(xform.GridUid)) - friction = moveSpeedComponent?.OffGridFriction ?? MovementSpeedModifierComponent.DefaultOffGridFriction; - else if (worldTotal != Vector2.Zero && touching) - friction = moveSpeedComponent?.WeightlessFriction ?? MovementSpeedModifierComponent.DefaultWeightlessFriction; - else - friction = moveSpeedComponent?.WeightlessFrictionNoInput ?? MovementSpeedModifierComponent.DefaultWeightlessFrictionNoInput; + DebugTools.Assert(MathHelper.CloseToPercent(total.Length(), worldTotal.Length())); + + var velocity = physicsComponent.LinearVelocity; + float friction; + float weightlessModifier; + float accel; + + if (weightless) + { + if (gridComp == null && !MapGridQuery.HasComp(xform.GridUid)) + friction = moveSpeedComponent?.OffGridFriction ?? MovementSpeedModifierComponent.DefaultOffGridFriction; + else if (worldTotal != Vector2.Zero && touching) + friction = moveSpeedComponent?.WeightlessFriction ?? MovementSpeedModifierComponent.DefaultWeightlessFriction; + else + friction = moveSpeedComponent?.WeightlessFrictionNoInput ?? MovementSpeedModifierComponent.DefaultWeightlessFrictionNoInput; - weightlessModifier = moveSpeedComponent?.WeightlessModifier ?? MovementSpeedModifierComponent.DefaultWeightlessModifier; - accel = moveSpeedComponent?.WeightlessAcceleration ?? MovementSpeedModifierComponent.DefaultWeightlessAcceleration; + weightlessModifier = moveSpeedComponent?.WeightlessModifier ?? MovementSpeedModifierComponent.DefaultWeightlessModifier; + accel = moveSpeedComponent?.WeightlessAcceleration ?? MovementSpeedModifierComponent.DefaultWeightlessAcceleration; + } + else + { + if (worldTotal != Vector2.Zero || moveSpeedComponent?.FrictionNoInput == null) + { + friction = tileDef?.MobFriction ?? moveSpeedComponent?.Friction ?? MovementSpeedModifierComponent.DefaultFriction; } else { - if (worldTotal != Vector2.Zero || moveSpeedComponent?.FrictionNoInput == null) - { - friction = tileDef?.MobFriction ?? moveSpeedComponent?.Friction ?? MovementSpeedModifierComponent.DefaultFriction; - } - else - { - friction = tileDef?.MobFrictionNoInput ?? moveSpeedComponent.FrictionNoInput ?? MovementSpeedModifierComponent.DefaultFrictionNoInput; - } - - weightlessModifier = 1f; - accel = tileDef?.MobAcceleration ?? moveSpeedComponent?.Acceleration ?? MovementSpeedModifierComponent.DefaultAcceleration; + friction = tileDef?.MobFrictionNoInput ?? moveSpeedComponent.FrictionNoInput ?? MovementSpeedModifierComponent.DefaultFrictionNoInput; } - var minimumFrictionSpeed = moveSpeedComponent?.MinimumFrictionSpeed ?? MovementSpeedModifierComponent.DefaultMinimumFrictionSpeed; - Friction(minimumFrictionSpeed, frameTime, friction, ref velocity); + weightlessModifier = 1f; + accel = tileDef?.MobAcceleration ?? moveSpeedComponent?.Acceleration ?? MovementSpeedModifierComponent.DefaultAcceleration; + } - if (worldTotal != Vector2.Zero) + var minimumFrictionSpeed = moveSpeedComponent?.MinimumFrictionSpeed ?? MovementSpeedModifierComponent.DefaultMinimumFrictionSpeed; + Friction(minimumFrictionSpeed, frameTime, friction, ref velocity); + + if (worldTotal != Vector2.Zero) + { + if (!NoRotateQuery.HasComponent(uid)) + { + // TODO apparently this results in a duplicate move event because "This should have its event run during + // island solver"??. So maybe SetRotation needs an argument to avoid raising an event? + var worldRot = _transform.GetWorldRotation(xform); + _transform.SetLocalRotation(xform, xform.LocalRotation + worldTotal.ToWorldAngle() - worldRot); + } + + if (!weightless && MobMoverQuery.TryGetComponent(uid, out var mobMover) && + TryGetSound(weightless, uid, mover, mobMover, xform, out var sound, tileDef: tileDef)) { - if (!NoRotateQuery.HasComponent(uid)) + var soundModifier = mover.Sprinting ? 3.5f : 1.5f; + + var audioParams = sound.Params + .WithVolume(sound.Params.Volume + soundModifier) + .WithVariation(sound.Params.Variation ?? mobMover.FootstepVariation); + + // If we're a relay target then predict the sound for all relays. + if (relayTarget != null) { - // TODO apparently this results in a duplicate move event because "This should have its event run during - // island solver"??. So maybe SetRotation needs an argument to avoid raising an event? - var worldRot = _transform.GetWorldRotation(xform); - _transform.SetLocalRotation(xform, xform.LocalRotation + worldTotal.ToWorldAngle() - worldRot); + _audio.PlayPredicted(sound, uid, relayTarget.Source, audioParams); } - - if (!weightless && MobMoverQuery.TryGetComponent(uid, out var mobMover) && - TryGetSound(weightless, uid, mover, mobMover, xform, out var sound, tileDef: tileDef)) + else { - var soundModifier = mover.Sprinting ? 3.5f : 1.5f; - - var audioParams = sound.Params - .WithVolume(sound.Params.Volume + soundModifier) - .WithVariation(sound.Params.Variation ?? mobMover.FootstepVariation); - - // If we're a relay target then predict the sound for all relays. - if (relayTarget != null) - { - _audio.PlayPredicted(sound, uid, relayTarget.Source, audioParams); - } - else - { - _audio.PlayPredicted(sound, uid, uid, audioParams); - } + _audio.PlayPredicted(sound, uid, uid, audioParams); } } + } - worldTotal *= weightlessModifier; + worldTotal *= weightlessModifier; - if (!weightless || touching) - Accelerate(ref velocity, in worldTotal, accel, frameTime); + if (!weightless || touching) + Accelerate(ref velocity, in worldTotal, accel, frameTime); - PhysicsSystem.SetLinearVelocity(physicsUid, velocity, body: physicsComponent); + PhysicsSystem.SetLinearVelocity(physicsUid, velocity, body: physicsComponent); - // Ensures that players do not spiiiiiiin - PhysicsSystem.SetAngularVelocity(physicsUid, 0, body: physicsComponent); - } + // Ensures that players do not spiiiiiiin + PhysicsSystem.SetAngularVelocity(physicsUid, 0, body: physicsComponent); + } - public void LerpRotation(EntityUid uid, InputMoverComponent mover, float frameTime) + public void LerpRotation(EntityUid uid, InputMoverComponent mover, float frameTime) + { + var angleDiff = Angle.ShortestDistance(mover.RelativeRotation, mover.TargetRelativeRotation); + + // if we've just traversed then lerp to our target rotation. + if (!angleDiff.EqualsApprox(Angle.Zero, 0.001)) { - var angleDiff = Angle.ShortestDistance(mover.RelativeRotation, mover.TargetRelativeRotation); + var adjustment = angleDiff * 5f * frameTime; + var minAdjustment = 0.01 * frameTime; - // if we've just traversed then lerp to our target rotation. - if (!angleDiff.EqualsApprox(Angle.Zero, 0.001)) + if (angleDiff < 0) { - var adjustment = angleDiff * 5f * frameTime; - var minAdjustment = 0.01 * frameTime; - - if (angleDiff < 0) - { - adjustment = Math.Min(adjustment, -minAdjustment); - adjustment = Math.Clamp(adjustment, angleDiff, -angleDiff); - } - else - { - adjustment = Math.Max(adjustment, minAdjustment); - adjustment = Math.Clamp(adjustment, -angleDiff, angleDiff); - } - - mover.RelativeRotation += adjustment; - mover.RelativeRotation.FlipPositive(); - Dirty(uid, mover); + adjustment = Math.Min(adjustment, -minAdjustment); + adjustment = Math.Clamp(adjustment, angleDiff, -angleDiff); } - else if (!angleDiff.Equals(Angle.Zero)) + else { - mover.TargetRelativeRotation.FlipPositive(); - mover.RelativeRotation = mover.TargetRelativeRotation; - Dirty(uid, mover); + adjustment = Math.Max(adjustment, minAdjustment); + adjustment = Math.Clamp(adjustment, -angleDiff, angleDiff); } - } - private void Friction(float minimumFrictionSpeed, float frameTime, float friction, ref Vector2 velocity) + mover.RelativeRotation += adjustment; + mover.RelativeRotation.FlipPositive(); + Dirty(uid, mover); + } + else if (!angleDiff.Equals(Angle.Zero)) { - var speed = velocity.Length(); + mover.TargetRelativeRotation.FlipPositive(); + mover.RelativeRotation = mover.TargetRelativeRotation; + Dirty(uid, mover); + } + } - if (speed < minimumFrictionSpeed) - return; + private void Friction(float minimumFrictionSpeed, float frameTime, float friction, ref Vector2 velocity) + { + var speed = velocity.Length(); - var drop = 0f; + if (speed < minimumFrictionSpeed) + return; - var control = MathF.Max(_stopSpeed, speed); - drop += control * friction * frameTime; + var drop = 0f; - var newSpeed = MathF.Max(0f, speed - drop); + var control = MathF.Max(_stopSpeed, speed); + drop += control * friction * frameTime; - if (newSpeed.Equals(speed)) - return; + var newSpeed = MathF.Max(0f, speed - drop); - newSpeed /= speed; - velocity *= newSpeed; - } + if (newSpeed.Equals(speed)) + return; - private void Accelerate(ref Vector2 currentVelocity, in Vector2 velocity, float accel, float frameTime) - { - var wishDir = velocity != Vector2.Zero ? velocity.Normalized() : Vector2.Zero; - var wishSpeed = velocity.Length(); + newSpeed /= speed; + velocity *= newSpeed; + } - var currentSpeed = Vector2.Dot(currentVelocity, wishDir); - var addSpeed = wishSpeed - currentSpeed; + private void Accelerate(ref Vector2 currentVelocity, in Vector2 velocity, float accel, float frameTime) + { + var wishDir = velocity != Vector2.Zero ? velocity.Normalized() : Vector2.Zero; + var wishSpeed = velocity.Length(); - if (addSpeed <= 0f) - return; + var currentSpeed = Vector2.Dot(currentVelocity, wishDir); + var addSpeed = wishSpeed - currentSpeed; - var accelSpeed = accel * frameTime * wishSpeed; - accelSpeed = MathF.Min(accelSpeed, addSpeed); + if (addSpeed <= 0f) + return; - currentVelocity += wishDir * accelSpeed; - } + var accelSpeed = accel * frameTime * wishSpeed; + accelSpeed = MathF.Min(accelSpeed, addSpeed); - public bool UseMobMovement(EntityUid uid) - { - return UsedMobMovement.TryGetValue(uid, out var used) && used; - } + currentVelocity += wishDir * accelSpeed; + } - /// - /// Used for weightlessness to determine if we are near a wall. - /// - private bool IsAroundCollider(SharedPhysicsSystem broadPhaseSystem, TransformComponent transform, MobMoverComponent mover, EntityUid physicsUid, PhysicsComponent collider) - { - var enlargedAABB = _lookup.GetWorldAABB(physicsUid, transform).Enlarged(mover.GrabRangeVV); + public bool UseMobMovement(EntityUid uid) + { + return UsedMobMovement.TryGetValue(uid, out var used) && used; + } - foreach (var otherCollider in broadPhaseSystem.GetCollidingEntities(transform.MapID, enlargedAABB)) - { - if (otherCollider == collider) - continue; // Don't try to push off of yourself! - - // Only allow pushing off of anchored things that have collision. - if (otherCollider.BodyType != BodyType.Static || - !otherCollider.CanCollide || - ((collider.CollisionMask & otherCollider.CollisionLayer) == 0 && - (otherCollider.CollisionMask & collider.CollisionLayer) == 0) || - (TryComp(otherCollider.Owner, out PullableComponent? pullable) && pullable.BeingPulled)) - { - continue; - } + /// + /// Used for weightlessness to determine if we are near a wall. + /// + private bool IsAroundCollider(SharedPhysicsSystem broadPhaseSystem, TransformComponent transform, MobMoverComponent mover, EntityUid physicsUid, PhysicsComponent collider) + { + var enlargedAABB = _lookup.GetWorldAABB(physicsUid, transform).Enlarged(mover.GrabRangeVV); - return true; + foreach (var otherCollider in broadPhaseSystem.GetCollidingEntities(transform.MapID, enlargedAABB)) + { + if (otherCollider == collider) + continue; // Don't try to push off of yourself! + + // Only allow pushing off of anchored things that have collision. + if (otherCollider.BodyType != BodyType.Static || + !otherCollider.CanCollide || + ((collider.CollisionMask & otherCollider.CollisionLayer) == 0 && + (otherCollider.CollisionMask & collider.CollisionLayer) == 0) || + (TryComp(otherCollider.Owner, out PullableComponent? pullable) && pullable.BeingPulled)) + { + continue; } - return false; + return true; } - protected abstract bool CanSound(); + return false; + } + + protected abstract bool CanSound(); - private bool TryGetSound( - bool weightless, - EntityUid uid, - InputMoverComponent mover, - MobMoverComponent mobMover, - TransformComponent xform, - [NotNullWhen(true)] out SoundSpecifier? sound, - ContentTileDefinition? tileDef = null) - { - sound = null; + private bool TryGetSound( + bool weightless, + EntityUid uid, + InputMoverComponent mover, + MobMoverComponent mobMover, + TransformComponent xform, + [NotNullWhen(true)] out SoundSpecifier? sound, + ContentTileDefinition? tileDef = null) + { + sound = null; - if (!CanSound() || !_tags.HasTag(uid, "FootstepSound")) - return false; + if (!CanSound() || !_tags.HasTag(uid, "FootstepSound")) + return false; - var coordinates = xform.Coordinates; - var distanceNeeded = mover.Sprinting - ? mobMover.StepSoundMoveDistanceRunning - : mobMover.StepSoundMoveDistanceWalking; + var coordinates = xform.Coordinates; + var distanceNeeded = mover.Sprinting + ? mobMover.StepSoundMoveDistanceRunning + : mobMover.StepSoundMoveDistanceWalking; - // Handle footsteps. - if (!weightless) + // Handle footsteps. + if (!weightless) + { + // Can happen when teleporting between grids. + if (!coordinates.TryDistance(EntityManager, mobMover.LastPosition, out var distance) || + distance > distanceNeeded) { - // Can happen when teleporting between grids. - if (!coordinates.TryDistance(EntityManager, mobMover.LastPosition, out var distance) || - distance > distanceNeeded) - { - mobMover.StepSoundDistance = distanceNeeded; - } - else - { - mobMover.StepSoundDistance += distance; - } + mobMover.StepSoundDistance = distanceNeeded; } else { - // In space no one can hear you squeak - return false; + mobMover.StepSoundDistance += distance; } + } + else + { + // In space no one can hear you squeak + return false; + } - mobMover.LastPosition = coordinates; + mobMover.LastPosition = coordinates; - if (mobMover.StepSoundDistance < distanceNeeded) - return false; + if (mobMover.StepSoundDistance < distanceNeeded) + return false; - mobMover.StepSoundDistance -= distanceNeeded; + mobMover.StepSoundDistance -= distanceNeeded; - if (FootstepModifierQuery.TryComp(uid, out var moverModifier)) - { - sound = moverModifier.FootstepSoundCollection; - return true; - } + if (FootstepModifierQuery.TryComp(uid, out var moverModifier)) + { + sound = moverModifier.FootstepSoundCollection; + return true; + } - if (_inventory.TryGetSlotEntity(uid, "shoes", out var shoes) && - FootstepModifierQuery.TryComp(shoes, out var modifier)) + if (_inventory.TryGetSlotEntity(uid, "shoes", out var shoes) && + FootstepModifierQuery.TryComp(shoes, out var modifier)) + { + sound = modifier.FootstepSoundCollection; + return true; + } + + return TryGetFootstepSound(uid, xform, shoes != null, out sound, tileDef: tileDef); + } + + private bool TryGetFootstepSound( + EntityUid uid, + TransformComponent xform, + bool haveShoes, + [NotNullWhen(true)] out SoundSpecifier? sound, + ContentTileDefinition? tileDef = null) + { + sound = null; + + // Fallback to the map? + if (!MapGridQuery.TryComp(xform.GridUid, out var grid)) + { + if (FootstepModifierQuery.TryComp(xform.MapUid, out var modifier)) { sound = modifier.FootstepSoundCollection; return true; } - return TryGetFootstepSound(uid, xform, shoes != null, out sound, tileDef: tileDef); + return false; } - private bool TryGetFootstepSound( - EntityUid uid, - TransformComponent xform, - bool haveShoes, - [NotNullWhen(true)] out SoundSpecifier? sound, - ContentTileDefinition? tileDef = null) - { - sound = null; + var position = grid.LocalToTile(xform.Coordinates); + var soundEv = new GetFootstepSoundEvent(uid); - // Fallback to the map? - if (!MapGridQuery.TryComp(xform.GridUid, out var grid)) - { - if (FootstepModifierQuery.TryComp(xform.MapUid, out var modifier)) - { - sound = modifier.FootstepSoundCollection; - return true; - } - - return false; - } - - var position = grid.LocalToTile(xform.Coordinates); - var soundEv = new GetFootstepSoundEvent(uid); + // If the coordinates have a FootstepModifier component + // i.e. component that emit sound on footsteps emit that sound + var anchored = grid.GetAnchoredEntitiesEnumerator(position); - // If the coordinates have a FootstepModifier component - // i.e. component that emit sound on footsteps emit that sound - var anchored = grid.GetAnchoredEntitiesEnumerator(position); + while (anchored.MoveNext(out var maybeFootstep)) + { + RaiseLocalEvent(maybeFootstep.Value, ref soundEv); - while (anchored.MoveNext(out var maybeFootstep)) + if (soundEv.Sound != null) { - RaiseLocalEvent(maybeFootstep.Value, ref soundEv); - - if (soundEv.Sound != null) - { - sound = soundEv.Sound; - return true; - } - - if (FootstepModifierQuery.TryComp(maybeFootstep, out var footstep)) - { - sound = footstep.FootstepSoundCollection; - return true; - } + sound = soundEv.Sound; + return true; } - // Walking on a tile. - // Tile def might have been passed in already from previous methods, so use that - // if we have it - if (tileDef == null && grid.TryGetTileRef(position, out var tileRef)) + if (FootstepModifierQuery.TryComp(maybeFootstep, out var footstep)) { - tileDef = (ContentTileDefinition) _tileDefinitionManager[tileRef.Tile.TypeId]; + sound = footstep.FootstepSoundCollection; + return true; } + } - if (tileDef == null) - return false; - - sound = haveShoes ? tileDef.FootstepSounds : tileDef.BarestepSounds; - return sound != null; + // Walking on a tile. + // Tile def might have been passed in already from previous methods, so use that + // if we have it + if (tileDef == null && grid.TryGetTileRef(position, out var tileRef)) + { + tileDef = (ContentTileDefinition) _tileDefinitionManager[tileRef.Tile.TypeId]; } + + if (tileDef == null) + return false; + + sound = haveShoes ? tileDef.FootstepSounds : tileDef.BarestepSounds; + return sound != null; } } diff --git a/Content.Shared/NameModifier/EntitySystems/NameModifierSystem.cs b/Content.Shared/NameModifier/EntitySystems/NameModifierSystem.cs index 4dffb51805c1..2e7c8054b3b1 100644 --- a/Content.Shared/NameModifier/EntitySystems/NameModifierSystem.cs +++ b/Content.Shared/NameModifier/EntitySystems/NameModifierSystem.cs @@ -5,7 +5,7 @@ namespace Content.Shared.NameModifier.EntitySystems; /// -public sealed partial class NameModifierSystem : EntitySystem +public sealed class NameModifierSystem : EntitySystem { [Dependency] private readonly MetaDataSystem _metaData = default!; @@ -16,10 +16,10 @@ public override void Initialize() SubscribeLocalEvent(OnEntityRenamed); } - private void OnEntityRenamed(Entity entity, ref EntityRenamedEvent args) + private void OnEntityRenamed(Entity ent, ref EntityRenamedEvent args) { - SetBaseName((entity, entity.Comp), args.NewName); - RefreshNameModifiers((entity, entity.Comp)); + SetBaseName(ent, args.NewName); + RefreshNameModifiers((ent.Owner, ent.Comp)); } private void SetBaseName(Entity entity, string name) diff --git a/Content.Shared/Nutrition/Prototypes/FoodSequenceElementPrototype.cs b/Content.Shared/Nutrition/Prototypes/FoodSequenceElementPrototype.cs index a3448715e4a8..931d8a35327a 100644 --- a/Content.Shared/Nutrition/Prototypes/FoodSequenceElementPrototype.cs +++ b/Content.Shared/Nutrition/Prototypes/FoodSequenceElementPrototype.cs @@ -1,6 +1,7 @@ using Content.Shared.Tag; using Robust.Shared.Prototypes; using Robust.Shared.Utility; +using System.Numerics; namespace Content.Shared.Nutrition.Prototypes; @@ -18,6 +19,12 @@ public sealed partial class FoodSequenceElementPrototype : IPrototype [DataField] public List Sprites { get; private set; } = new(); + /// + /// Relative size of the sprite displayed in the food sequence. + /// + [DataField] + public Vector2 Scale { get; private set; } = Vector2.One; + /// /// A localized name piece to build into the item name generator. /// @@ -34,5 +41,5 @@ public sealed partial class FoodSequenceElementPrototype : IPrototype /// Tag list of this layer. Used for recipes for food metamorphosis. /// [DataField] - public List> Tags { get; set; } = new(); + public List> Tags { get; set; } = new(); } diff --git a/Content.Shared/PDA/PdaComponent.cs b/Content.Shared/PDA/PdaComponent.cs index d4cfc4fc0d8e..6aeb245e27d1 100644 --- a/Content.Shared/PDA/PdaComponent.cs +++ b/Content.Shared/PDA/PdaComponent.cs @@ -37,6 +37,10 @@ public sealed partial class PdaComponent : Component [ViewVariables] public bool FlashlightOn; [ViewVariables(VVAccess.ReadWrite)] public string? OwnerName; + // The Entity that "owns" the PDA, usually a player's character. + // This is useful when we are doing stuff like renaming a player and want to find their PDA to change the name + // as well. + [ViewVariables(VVAccess.ReadWrite)] public EntityUid? PdaOwner; [ViewVariables] public string? StationName; [ViewVariables] public string? StationAlertLevel; [ViewVariables] public Color StationAlertColor = Color.White; diff --git a/Content.Shared/Power/Generator/FuelGeneratorComponent.cs b/Content.Shared/Power/Generator/FuelGeneratorComponent.cs index cdf97fb08590..1cdb22a10983 100644 --- a/Content.Shared/Power/Generator/FuelGeneratorComponent.cs +++ b/Content.Shared/Power/Generator/FuelGeneratorComponent.cs @@ -1,4 +1,5 @@ -using Robust.Shared.GameStates; +using Content.Shared.Guidebook; +using Robust.Shared.GameStates; namespace Content.Shared.Power.Generator; @@ -17,19 +18,20 @@ public sealed partial class FuelGeneratorComponent : Component /// /// Is the generator currently running? /// - [DataField("on"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + [DataField, AutoNetworkedField] public bool On; /// /// The generator's target power. /// - [DataField("targetPower"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public float TargetPower = 15_000.0f; /// /// The maximum target power. /// - [DataField("maxTargetPower"), ViewVariables(VVAccess.ReadWrite)] + [DataField] + [GuidebookData] public float MaxTargetPower = 30_000.0f; /// @@ -38,24 +40,24 @@ public sealed partial class FuelGeneratorComponent : Component /// /// Setting this to any value above 0 means that the generator can't idle without consuming some amount of fuel. /// - [DataField("minTargetPower"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public float MinTargetPower = 1_000; /// /// The "optimal" power at which the generator is considered to be at 100% efficiency. /// - [DataField("optimalPower"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public float OptimalPower = 15_000.0f; /// /// The rate at which one unit of fuel should be consumed. /// - [DataField("optimalBurnRate"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public float OptimalBurnRate = 1 / 60.0f; // Once every 60 seconds. /// /// A constant used to calculate fuel efficiency in relation to target power output and optimal power output /// - [DataField("fuelEfficiencyConstant")] + [DataField] public float FuelEfficiencyConstant = 1.3f; } diff --git a/Content.Shared/Random/RandomPlantMutation.cs b/Content.Shared/Random/RandomPlantMutation.cs new file mode 100644 index 000000000000..d95cf7bf422b --- /dev/null +++ b/Content.Shared/Random/RandomPlantMutation.cs @@ -0,0 +1,48 @@ +using Content.Shared.EntityEffects; +using Robust.Shared.Serialization; + +namespace Content.Shared.Random; + +/// +/// Data that specifies the odds and effects of possible random plant mutations. +/// +[Serializable, NetSerializable] +[DataDefinition] +public sealed partial class RandomPlantMutation +{ + /// + /// Odds of this mutation occurring with 1 point of mutation severity on a plant. + /// + [DataField] + public float BaseOdds = 0; + + /// + /// The name of this mutation. + /// + [DataField] + public string Name = ""; + + /// + /// The actual EntityEffect to apply to the target + /// + [DataField] + public EntityEffect Effect = default!; + + /// + /// This mutation will target the harvested produce + /// + [DataField] + public bool AppliesToProduce = true; + + /// + /// This mutation will target the growing plant as soon as this mutation is applied. + /// + [DataField] + public bool AppliesToPlant = true; + + /// + /// This mutation stays on the plant and its produce. If false while AppliesToPlant is true, the effect will run when triggered. + /// + [DataField] + public bool Persists = true; +} diff --git a/Content.Shared/Random/RandomPlantMutationListPrototype.cs b/Content.Shared/Random/RandomPlantMutationListPrototype.cs new file mode 100644 index 000000000000..84e3b9256c3b --- /dev/null +++ b/Content.Shared/Random/RandomPlantMutationListPrototype.cs @@ -0,0 +1,18 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Random; + +/// +/// Random weighting dataset for solutions, able to specify reagents quantity. +/// +[Prototype("RandomPlantMutationList")] +public sealed partial class RandomPlantMutationListPrototype : IPrototype +{ + [IdDataField] public string ID { get; } = default!; + + /// + /// List of RandomFills that can be picked from. + /// + [DataField("mutations", required: true, serverOnly: true)] + public List mutations = new(); +} diff --git a/Content.Shared/Roles/JobPrototype.cs b/Content.Shared/Roles/JobPrototype.cs index e7156b34c3bd..1ca1600e770d 100644 --- a/Content.Shared/Roles/JobPrototype.cs +++ b/Content.Shared/Roles/JobPrototype.cs @@ -20,6 +20,9 @@ public sealed partial class JobPrototype : IPrototype [DataField("playTimeTracker", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] public string PlayTimeTracker { get; private set; } = string.Empty; + /// + /// Who is the supervisor for this job. + /// [DataField("supervisors")] public string Supervisors { get; private set; } = "nobody"; @@ -41,18 +44,36 @@ public sealed partial class JobPrototype : IPrototype [ViewVariables(VVAccess.ReadOnly)] public string? LocalizedDescription => Description is null ? null : Loc.GetString(Description); + /// + /// Requirements for the job. + /// [DataField, Access(typeof(SharedRoleSystem), Other = AccessPermissions.None)] public HashSet? Requirements; + /// + /// When true - the station will have anouncement about arrival of this player. + /// [DataField("joinNotifyCrew")] public bool JoinNotifyCrew { get; private set; } = false; + /// + /// When true - the player will recieve a message about importancy of their job. + /// [DataField("requireAdminNotify")] public bool RequireAdminNotify { get; private set; } = false; + /// + /// Should this job appear in preferences menu? + /// [DataField("setPreference")] public bool SetPreference { get; private set; } = true; + /// + /// Should the selected traits be applied for this job? + /// + [DataField] + public bool ApplyTraits { get; private set; } = true; + /// /// Whether this job should show in the ID Card Console. /// If set to null, it will default to SetPreference's value. diff --git a/Content.Shared/Shuttles/Components/FTLSmashImmuneComponent.cs b/Content.Shared/Shuttles/Components/FTLSmashImmuneComponent.cs new file mode 100644 index 000000000000..9ed7ee05a51a --- /dev/null +++ b/Content.Shared/Shuttles/Components/FTLSmashImmuneComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Shuttles.Components; + +/// +/// Makes the entity immune to FTL arrival landing AKA smimsh. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class FTLSmashImmuneComponent : Component; diff --git a/Content.Shared/Shuttles/Components/NoFTLComponent.cs b/Content.Shared/Shuttles/Components/NoFTLComponent.cs new file mode 100644 index 000000000000..d48ba33bbacb --- /dev/null +++ b/Content.Shared/Shuttles/Components/NoFTLComponent.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Shuttles.Components; + +/// +/// Prevents the attached entity from taking FTL. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class NoFTLComponent : Component +{ + +} diff --git a/Resources/Audio/Items/Toys/attributions.yml b/Resources/Audio/Items/Toys/attributions.yml index 290539b9849d..162ee8c1c337 100644 --- a/Resources/Audio/Items/Toys/attributions.yml +++ b/Resources/Audio/Items/Toys/attributions.yml @@ -72,3 +72,18 @@ license: "CC-BY-SA-3.0" copyright: "Taken from ss200, made by Daeberdir" source: "https://github.com/ss220-space/Paradise/pull/3756" + +- files: ["rubber_chicken_1.ogg"] + license: "CC0-1.0" + copyright: "Created by xprospero for ss14" + source: "https://github.com/space-wizards/space-station-14/blob/master/Resources/Audio/Items/Toys/rubber_chicken_1.ogg" + +- files: ["rubber_chicken_2.ogg"] + license: "CC0-1.0" + copyright: "Created by xprospero for ss14" + source: "https://github.com/space-wizards/space-station-14/blob/master/Resources/Audio/Items/Toys/rubber_chicken_2.ogg" + +- files: ["rubber_chicken_3.ogg"] + license: "CC0-1.0" + copyright: "Created by xprospero for ss14" + source: "https://github.com/space-wizards/space-station-14/blob/master/Resources/Audio/Items/Toys/rubber_chicken_3.ogg" diff --git a/Resources/Audio/Items/Toys/rubber_chicken_1.ogg b/Resources/Audio/Items/Toys/rubber_chicken_1.ogg new file mode 100644 index 000000000000..e4ba8155a6cc Binary files /dev/null and b/Resources/Audio/Items/Toys/rubber_chicken_1.ogg differ diff --git a/Resources/Audio/Items/Toys/rubber_chicken_2.ogg b/Resources/Audio/Items/Toys/rubber_chicken_2.ogg new file mode 100644 index 000000000000..ae79d97ff229 Binary files /dev/null and b/Resources/Audio/Items/Toys/rubber_chicken_2.ogg differ diff --git a/Resources/Audio/Items/Toys/rubber_chicken_3.ogg b/Resources/Audio/Items/Toys/rubber_chicken_3.ogg new file mode 100644 index 000000000000..97d93ed57e72 Binary files /dev/null and b/Resources/Audio/Items/Toys/rubber_chicken_3.ogg differ diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index d4d6e9e6abe3..c5b567f69e39 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -527,5 +527,22 @@ Entries: id: 65 time: '2024-09-08T07:28:43.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/30456 +- author: Fildrance + changes: + - message: The setmapatmos command now correctly uses its temperature argument + type: Fix + id: 66 + time: '2024-09-13T22:49:27.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32125 +- author: nikthechampiongr + changes: + - message: Rename verb now acts the same as the rename command. + type: Fix + - message: Renamed entities will now have their new name appear immediately on entity + examination and on crew monitor console. + type: Fix + id: 67 + time: '2024-09-15T01:55:03.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/31654 Name: Admin Order: 1 diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index c2bd0c01dcaf..da0ac0a1c01b 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,185 +1,4 @@ Entries: -- author: slarticodefast - changes: - - message: Fixed sprite rotation in harm mode on rotated grids. - type: Fix - id: 6858 - time: '2024-07-02T13:04:15.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29663 -- author: lzk228 - changes: - - message: Space Law book added to the game. - type: Add - id: 6859 - time: '2024-07-02T13:33:49.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29392 -- author: Ko4erga - changes: - - message: Dropper can be placed in med belt, plant belt and chemistry bag. - type: Tweak - - message: Dropper size changed. - type: Tweak - id: 6860 - time: '2024-07-02T15:28:49.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29667 -- author: Plykiya - changes: - - message: You now see pickup animations when stripping objects off of people. - type: Fix - - message: Thieving gloves no longer have a pickup animation when stripping people. - type: Fix - id: 6861 - time: '2024-07-03T00:01:59.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29665 -- author: nikthechampiongr - changes: - - message: The Elite Syndicate hardsuit is now slightly slower. - type: Tweak - - message: The Elite Syndicate hardsuit now costs 12 Telecrystals. - type: Tweak - id: 6862 - time: '2024-07-03T00:31:39.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29429 -- author: eoineoineoin - changes: - - message: Artifact Analyzer now collides with walls - type: Fix - id: 6863 - time: '2024-07-03T05:27:33.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/28117 -- author: TheShuEd - changes: - - message: anomalies have the ability to gain invisibility behavior - type: Add - id: 6864 - time: '2024-07-03T15:14:39.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29120 -- author: ArkiveDev - changes: - - message: Railings can now be constructed in all 4 orientations. - type: Fix - id: 6865 - time: '2024-07-03T16:59:29.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29687 -- author: Rinary - changes: - - message: Fixed radial menus overlapping where there's many icons. - type: Fix - id: 6866 - time: '2024-07-04T01:25:25.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29678 -- author: Plykiya - changes: - - message: An object's physics properly returns to normal after being pulled. - type: Fix - id: 6867 - time: '2024-07-04T01:29:07.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29694 -- author: Plykiya - changes: - - message: You can now destroy portable flashers. - type: Fix - id: 6868 - time: '2024-07-04T01:51:46.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29564 -- author: EmoGarbage404 - changes: - - message: All nuclear operatives are now humans. - type: Tweak - id: 6869 - time: '2024-07-04T02:29:26.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29693 -- author: metalgearsloth - changes: - - message: Made vox roundstart. - type: Tweak - id: 6870 - time: '2024-07-04T07:11:02.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29704 -- author: JIPDawg - changes: - - message: Changed the basic treatment module to include a Health Analyzer and removed - the dropper. - type: Tweak - id: 6871 - time: '2024-07-04T07:17:47.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29696 -- author: PJB3005 - changes: - - message: Fixed flashlights and similar permanently getting stuck blinking. - type: Fix - id: 6872 - time: '2024-07-04T08:02:43.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29457 -- author: Jezithyr - changes: - - message: All species except for Vox can now be played as Nukies. (Vox will be - enabled when load out code supports them) - type: Tweak - id: 6873 - time: '2024-07-05T07:59:16.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29707 -- author: metalgearsloth - changes: - - message: Shuttle map buttons will show up faster. - type: Tweak - id: 6874 - time: '2024-07-06T03:51:55.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29757 -- author: JIPDawg - changes: - - message: Added Late join CryoSleepers to Origin. - type: Tweak - id: 6875 - time: '2024-07-06T17:33:20.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29761 -- author: Beck Thompson - changes: - - message: Splashing reagents on players will now apply the correct amounts. - type: Fix - id: 6876 - time: '2024-07-07T03:52:18.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29763 -- author: Tayrtahn - changes: - - message: Dead bodies will no longer remain standing after being unbuckled from - chairs. - type: Fix - id: 6877 - time: '2024-07-07T06:20:53.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29741 -- author: Simyon - changes: - - message: Ratkings now require at least 30 players in order to spawn. - type: Tweak - id: 6878 - time: '2024-07-07T13:06:24.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29737 -- author: EmoGarbage404 - changes: - - message: Intercoms now use encryption keys to determine what channels they can - broadcast on. - type: Tweak - - message: Intercoms and handheld radios no longer rely on telecom servers. - type: Fix - id: 6879 - time: '2024-07-07T14:19:10.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29580 -- author: Vermidia - changes: - - message: Some drink reactions now require shaking with the shaker or stirring - with a spoon - type: Add - id: 6880 - time: '2024-07-07T14:21:53.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29243 -- author: Dezzzix - changes: - - message: Now you can slice food with swords - type: Add - id: 6881 - time: '2024-07-07T14:22:38.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29005 - author: EmoGarbage404 changes: - message: Changed AME power output. Lower injection amounts now produce more power @@ -3910,3 +3729,188 @@ id: 7357 time: '2024-09-11T16:24:16.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/31970 +- author: lzk228 + changes: + - message: Fixed forensic pad didn't care about target identity. + type: Fix + - message: Instead of name changing, forensic pad now will have a label with target's + name. + type: Tweak + id: 7358 + time: '2024-09-12T00:52:19.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/31842 +- author: lzk228 + changes: + - message: Borgs and Station AI will not receive selected in preferences traits. + type: Tweak + id: 7359 + time: '2024-09-12T10:36:41.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/31990 +- author: MrRobDemo + changes: + - message: Added 5% chance to make killer tomatoes a ghost role. + type: Add + - message: Killer tomatoes can now heal from being splashed with water, blood or + RobustHarvest. + type: Add + - message: Killer tomatoes can now escape from inventory (only intelligent). + type: Tweak + id: 7360 + time: '2024-09-12T12:51:41.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/31932 +- author: ScarKy0 + changes: + - message: Station AI now has a comms console ability. + type: Add + - message: Station AI abilities now have a default order. + type: Tweak + id: 7361 + time: '2024-09-12T15:28:54.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/31852 +- author: themias + changes: + - message: Fixed medical PDAs sometimes toggling their lights while scanning + type: Fix + id: 7362 + time: '2024-09-13T13:59:19.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32091 +- author: Gorox221 + changes: + - message: Mech pilots receive a warning before they are ejected from the mech. + type: Tweak + - message: Now, when removing the pilot of the mech, you need to not move. + type: Fix + id: 7363 + time: '2024-09-13T14:01:26.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/31649 +- author: slarticodefast + changes: + - message: Extradimensional orange, holymelon, meatwheat and world peas plant mutations + have been added. Obtain them by mutating oranges, watermelons, wheat and laughin' + peas respectively. + type: Add + id: 7364 + time: '2024-09-13T14:02:54.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27624 +- author: ShadowCommander + changes: + - message: Fixed PDA sometimes showing uplink and music buttons when the PDA was + not able to use them. + type: Fix + id: 7365 + time: '2024-09-13T14:19:32.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28373 +- author: Dezzzix + changes: + - message: Now you can wear a hood in void cloak + type: Add + id: 7366 + time: '2024-09-13T15:02:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/31061 +- author: PJB3005 + changes: + - message: Fixed some powered machines working when unpowered if the panel is open. + type: Fix + id: 7367 + time: '2024-09-13T23:58:54.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32135 +- author: qwerltaz + changes: + - message: The RCD can now place grilles and windows under shutters and cables under + doors. + type: Fix + id: 7368 + time: '2024-09-14T01:53:14.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32102 +- author: SlamBamActionman + changes: + - message: Briefcases can now be used as melee weapons. + type: Add + id: 7369 + time: '2024-09-14T13:09:43.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32063 +- author: Just_Art + changes: + - message: Added a head gauze to customization. + type: Add + id: 7370 + time: '2024-09-14T14:55:13.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/30852 +- author: deltanedas + changes: + - message: Fixed security's helmets not having any protection. + type: Fix + id: 7371 + time: '2024-09-14T15:56:57.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32152 +- author: eoineoineoin + changes: + - message: Ghosts can now read books. + type: Add + id: 7372 + time: '2024-09-14T16:28:33.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32151 +- author: Errant + changes: + - message: Lone Ops nukies now spawn with the appropriate species-specific survival + gear. + type: Fix + id: 7373 + time: '2024-09-14T16:34:01.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/31641 +- author: Plykiya + changes: + - message: The vent spawn event now has a chance to spawn snakes. + type: Add + id: 7374 + time: '2024-09-14T17:19:32.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32070 +- author: lzk228 + changes: + - message: Command intercom now reinforced the same way as the security one. + type: Tweak + id: 7375 + time: '2024-09-14T20:40:38.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32169 +- author: de0rix + changes: + - message: Animals in critical state now all have proper sprites. + type: Fix + id: 7376 + time: '2024-09-15T01:53:58.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32175 +- author: notafet + changes: + - message: Pressure and volume pumps now require power to operate. + type: Tweak + id: 7377 + time: '2024-09-15T01:58:10.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28995 +- author: deltanedas + changes: + - message: Holoparasites can no longer be summoned from inside containers. + type: Tweak + id: 7378 + time: '2024-09-15T19:04:32.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32068 +- author: BackeTako + changes: + - message: Added French and Spanish speech traits. + type: Add + id: 7379 + time: '2024-09-15T20:03:15.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/30966 +- author: Plykiya + changes: + - message: Meteors break through less walls now. + type: Tweak + id: 7380 + time: '2024-09-15T20:04:37.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32109 +- author: drakewill-CRL + changes: + - message: Produce harvested from sentient plants are no longer sentient themselves. + type: Fix + id: 7381 + time: '2024-09-16T00:04:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32192 diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index c6ec284dc845..1190feea712d 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aeshus, Aexxie, Afrokada, Agoichi, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlmondFlour, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, ArkiveDev, Arteben, AruMoon, as334, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, BasedUser, beck-thompson, BGare, bhenrich, Bixkitts, Blackern5000, Blazeror, blueDev2, Boaz1111, BobdaBiscuit, BombasterDS, brainfood1183, Brandon-Huu, Bright0, brndd, c4llv07e, CaasGit, CaptainSqrBeard, Carbonhell, CatTheSystem, Centronias, chairbender, Charlese2, chavonadelal, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, Ciac32, Clyybber, Cojoke-dot, ColdAutumnRain, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, Deeeeja, deepdarkdepths, Delete69, deltanedas, DerbyX, dffdff2423, DieselMohawk, Doctor-Cpu, DoctorBeard, DogZeroX, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, exincore, exp111, Fahasor, FairlySadPanda, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FoLoKe, fooberticus, Fortune117, freeman2651, Fromoriss, FungiFellow, Futuristic-OK, GalacticChimp, gbasood, Geekyhobo, Genkail, geraeumig, Ghagliiarghii, Git-Nivrak, github-actions[bot], gituhabu, GNF54, godisdeadLOL, Golinth, GoodWheatley, graevy, GreyMario, gusxyz, Gyrandola, h3half, Hanzdegloker, Hardly3D, harikattar, HerCoyote23, hitomishirichan, Hmeister-real, HoofedEar, Hoolny, hord-brayden, hubismal, Hugal31, Huxellberger, iacore, IamVelcroboy, Ian321, icekot8, IgorAnt028, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, Interrobang01, IProduceWidgets, ItsMeThom, Jackal298, Jackrost, jamessimo, janekvap, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JIPDawg, JoeHammad1844, joelsgp, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTrotter, K-Dynamic, KaiShibaa, kalane15, kalanosh, Keer-Sar, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, laok233, lapatison, LetterN, Level10Cybermancer, lever1209, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, luckyshotpictures, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, Magicalus, MagnusCrowe, ManelNavola, Mangohydra, marboww, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, MerrytheManokit, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MisterMecky, Mith-randalf, MjrLandWhale, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, MureixloI, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, NakataRin, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, nuke-haus, NULL882, nyeogmi, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, pigeonpeas, pissdemon, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, PopGamer45, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, PursuitInAshes, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, RamZ, Rane, ravage123321, rbertoche, Redict, RedlineTriad, redmushie, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, RumiTiger, Saakra, saintmuntzer, SaphireLattice, Sarahon, ScalyChimp, ScarKy0, scrato, Scribbles0, Serkket, ShadowCommander, Shadowtheprotogen546, shampunj, SignalWalker, Simyon264, Sirionaut, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, snebl, Snowni, snowsignal, SonicHDC, SoulFN, SoulSloth, SpaceManiac, SpeltIncorrectyl, Spessmann, SphiraI, spoogemonster, ssdaniel24, stalengd, Stealthbomber16, StrawberryMoses, superjj18, SweptWasTaken, Szunti, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, Terraspark4941, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, thetolbean, TimrodDX, Titian3, tkdrg, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, Tornado-Technology, tosatur, TsjipTsjip, Tunguso4ka, TurboTrackerss14, TyAshley, Tyler-IN, Tyzemol, UbaserB, UBlueberry, UKNOWH, Unkn0wnGh0st333, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, volundr-, Voomra, Vordenburg, vulppine, wafehling, WarMechanic, waylon531, weaversam8, whateverusername0, Willhelm53, Winkarst-cpu, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, ZNixian, ZoldorfTheWizard, Zonespace27, Zumorica, Zymem +0x6273, 12rabbits, 13spacemen, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 3nderall, 4310v343k, 4dplanner, 612git, 778b, Ablankmann, abregado, Absolute-Potato, achookh, Acruid, actioninja, actually-reb, ada-please, adamsong, Adeinitas, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aerocrux, Aeshus, Aexolott, Aexxie, africalimedrop, Afrokada, Agoichi, Ahion, aiden, AJCM-git, AjexRose, Alekshhh, alexkar598, AlexMorgan3817, alexumandxgabriel08x, Alithsko, ALMv1, Alpha-Two, AlphaQwerty, Altoids1, amylizzle, ancientpower, Andre19926, AndrewEyeke, AndreyCamper, Anzarot121, Appiah, ar4ill, ArchPigeon, areitpog, Arendian, arimah, Arkanic, ArkiveDev, armoks, Arteben, ArthurMousatov, ArtisticRoomba, artur, AruMoon, ArZarLordOfMango, as334, AsikKEsel, asperger-sind, aspiringLich, astriloqua, AutoOtter, avghdev, Awlod, AzzyIsNotHere, backetako, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, bellwetherlogic, benev0, benjamin-burges, BGare, bhenrich, bhespiritu, bibbly, BIGZi0348, bingojohnson, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, BlitzTheSquishy, bloodrizer, Bloody2372, blueDev2, Boaz1111, BobdaBiscuit, BobTheSleder, boiled-water-tsar, BombasterDS, botanySupremist, brainfood1183, BramvanZijp, Brandon-Huu, BriBrooo, Bright0, brndd, bryce0110, BubblegumBlue, buletsponge, buntobaggins, bvelliquette, byondfuckery, c0rigin, c4llv07e, CaasGit, Caconym27, Callmore, capnsockless, CaptainSqrBeard, Carbonhell, Carolyn3114, Carou02, carteblanche4me, CatTheSystem, Centronias, chairbender, Charlese2, ChaseFlorom, chavonadelal, Cheackraze, cheesePizza2, cheeseplated, Chief-Engineer, chillyconmor, christhirtle, chromiumboy, Chronophylos, Chubbicous, Chubbygummibear, Ciac32, civilCornball, Clement-O, clyf, Clyybber, CMDR-Piboy314, Cohnway, Cojoke-dot, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, CookieMasterT, coolboy911, coolmankid12345, Coolsurf6, corentt, CormosLemming, crazybrain23, creadth, CrigCrag, croilbird, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, DadeKuma, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DakotaGay, DamianX, DangerRevolution, daniel-cr, DanSAussieITS, Daracke, Darkenson, DawBla, Daxxi3, dch-GH, Deahaka, dean, DEATHB4DEFEAT, DeathCamel58, Deatherd, deathride58, DebugOk, Decappi, Decortex, Deeeeja, deepdarkdepths, degradka, Delete69, deltanedas, DenisShvalov, DerbyX, derek, dersheppard, Deserty0, Detintinto, DevilishMilk, dexlerxd, dffdff2423, DieselMohawk, digitalic, Dimastra, DinoWattz, DisposableCrewmember42, DjfjdfofdjfjD, doc-michael, docnite, Doctor-Cpu, DoctorBeard, DogZeroX, dolgovmi, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, dragonryan06, drakewill-CRL, Drayff, dreamlyjack, DrEnzyme, dribblydrone, DrMelon, drongood12, DrSingh, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, dukevanity, duskyjay, Dutch-VanDerLinde, dvir001, Dynexust, Easypoller, echo, eclips_e, eden077, EdenTheLiznerd, EEASAS, Efruit, efzapa, Ekkosangen, ElectroSR, elsie, elthundercloud, Elysium206, Emisse, emmafornash, EmoGarbage404, Endecc, eoineoineoin, eris, erohrs2, ERORR404V1, Errant-4, esguard, estacaoespacialpirata, eugene, exincore, exp111, f0x-n3rd, FacePluslll, Fahasor, FairlySadPanda, FATFSAAM2, Feluk6174, ficcialfaint, Fiftyllama, Fildrance, FillerVK, FinnishPaladin, FirinMaLazors, Fishfish458, FL-OZ, Flareguy, flashgnash, FluffiestFloof, FluidRock, foboscheshir, FoLoKe, fooberticus, ForestNoises, forgotmyotheraccount, forkeyboards, forthbridge, Fortune117, Fouin, foxhorn, freeman2651, freeze2222, Froffy025, Fromoriss, froozigiusz, FrostMando, FungiFellow, FunTust, Futuristic-OK, GalacticChimp, Gaxeer, gbasood, Geekyhobo, genderGeometries, GeneralGaws, Genkail, geraeumig, Ghagliiarghii, Git-Nivrak, githubuser508, gituhabu, GlassEclipse, GNF54, godisdeadLOL, Goldminermac, Golinth, GoodWheatley, Gorox221, graevy, GraniteSidewalk, GreaseMonk, greenrock64, GreyMario, GTRsound, gusxyz, Gyrandola, h3half, hamurlik, Hanzdegloker, HappyRoach, Hardly3D, harikattar, he1acdvv, Hebi, Henry, HerCoyote23, hitomishirichan, hiucko, Hmeister-fake, Hmeister-real, Hobbitmax, hobnob, HoidC, Holinka4ever, holyssss, HoofedEar, Hoolny, hord-brayden, Hreno, hubismal, Hugal31, Huxellberger, Hyenh, i-justuser-i, iacore, IamVelcroboy, Ian321, icekot8, icesickleone, iczero, iglov, IgorAnt028, igorsaux, ike709, illersaver, Illiux, Ilushkins33, Ilya246, IlyaElDunaev, imrenq, imweax, indeano, Injazz, Insineer, IntegerTempest, Interrobang01, IProduceWidgets, ItsMeThom, Itzbenz, iztokbajcar, Jackal298, Jackrost, jacksonzck, Jackw2As, jacob, jamessimo, janekvap, Jark255, Jaskanbe, JasperJRoth, jerryimmouse, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JimGamemaster, jimmy12or, JIPDawg, jjtParadox, JoeHammad1844, joelsgp, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, justdie12, justin, justintether, JustinTrotter, justtne, K-Dynamic, k3yw, Kadeo64, Kaga-404, KaiShibaa, kalane15, kalanosh, katzenminer, kbailey-git, Keelin, Keer-Sar, KEEYNy, keikiru, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, Kimpes, KingFroozy, kira-er, Kirillcas, Kirus59, Kistras, Kit0vras, KittenColony, klaypexx, Kmc2000, Ko4ergaPunk, kognise, kokoc9n, komunre, KonstantinAngelov, koteq, KrasnoshchekovPavel, Krunklehorn, Kukutis96513, Kupie, kxvvv, kyupolaris, kzhanik, lajolico, Lamrr, LankLTE, laok233, lapatison, larryrussian, lawdog4817, Lazzi0706, leander-0, leonardo-dabepis, leonsfriedrich, LeoSantich, LetterN, lettern, Level10Cybermancer, LEVELcat, lever1209, lgruthes, LightVillet, liltenhead, LinkUyx, LittleBuilderJane, lizelive, localcc, lokachop, Lomcastar, LordCarve, LordEclipse, LucasTheDrgn, luckyshotpictures, LudwigVonChesterfield, luizwritescode, Lukasz825700516, luminight, lunarcomets, luringens, lvvova1, Lyndomen, lyroth001, lzimann, lzk228, M3739, mac6na6na, MACMAN2003, Macoron, Magicalus, magmodius, MagnusCrowe, malchanceux, MaloTV, ManelNavola, Mangohydra, marboww, Markek1, Matz05, max, MaxNox7, maylokana, MehimoNemo, MeltedPixel, MemeProof, MendaxxDev, Menshin, Mephisto72, MerrytheManokit, Mervill, metalgearsloth, MetalSage, MFMessage, mhamsterr, michaelcu, micheel665, MilenVolf, milon, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MisterMecky, Mith-randalf, MjrLandWhale, mkanke-real, MLGTASTICa, moderatelyaware, modern-nm, mokiros, Moneyl, Moomoobeef, moony, Morb0, mr-bo-jangles, Mr0maks, MrFippik, mrrobdemo, MureixloI, musicmanvr, MWKane, Myakot, Myctai, N3X15, nails-n-tape, Nairodian, Naive817, NakataRin, namespace-Memory, Nannek, NazrinNya, neutrino-laser, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, NIXC, NkoKirkto, nmajask, noctyrnal, nok-ko, NonchalantNoob, NoobyLegion, Nopey, not-gavnaed, notafet, notquitehadouken, NotSoDana, noudoit, noverd, NuclearWinter, nukashimika, nuke-haus, NULL882, nullarmo, nyeogmi, Nylux, Nyranu, och-och, OctoRocket, OldDanceJacket, OliverOtter, onoira, OnyxTheBrave, OrangeMoronage9622, osjarw, Ostaf, othymer, OttoMaticode, Owai-Seek, packmore, paigemaeforrest, pali6, Pangogie, panzer-iv1, partyaddict, patrikturi, PaulRitter, peccneck, Peptide90, peptron1, PeterFuto, PetMudstone, pewter-wiz, Pgriha, Phantom-Lily, Phill101, phunnyguy, pigeonpeas, PilgrimViis, Pill-U, Pireax, Pissachu, pissdemon, PixeltheAertistContrib, PixelTheKermit, PJB3005, Plasmaguy, plinyvic, Plykiya, poeMota, pofitlo, pointer-to-null, pok27, PolterTzi, PoorMansDreams, PopGamer45, portfiend, potato1234x, PotentiallyTom, ProfanedBane, ProPandaBear, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykzz, PuceTint, PuroSlavKing, PursuitInAshes, Putnam3145, quatre, QueerNB, QuietlyWhisper, qwerltaz, RadioMull, Radosvik, Radrark, Rainbeon, Rainfey, Raitononai, Ramlik, RamZ, randy10122, Rane, Ranger6012, Rapidgame7, ravage123321, rbertoche, Redfire1331, Redict, RedlineTriad, redmushie, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, Renlou, retequizzle, RiceMar1244, rich-dunne, RieBi, riggleprime, RIKELOLDABOSS, rinary1, Rinkashikachi, riolume, RobbyTheFish, Rockdtben, Rohesie, rok-povsic, rolfero, RomanNovo, rosieposieeee, Roudenn, router, RumiTiger, S1rFl0, S1ss3l, Saakra, saga3152, saintmuntzer, Salex08, sam, samgithubaccount, SaphireLattice, SapphicOverload, Sarahon, SaveliyM360, sBasalto, ScalyChimp, ScarKy0, scrato, Scribbles0, scruq445, scuffedjays, ScumbagDog, Segonist, sephtasm, Serkket, sewerpig, sh18rw, ShadeAware, ShadowCommander, Shadowtheprotogen546, shaeone, shampunj, shariathotpatrol, SignalWalker, siigiil, Simyon264, sirdragooon, Sirionaut, Sk1tch, SkaldetSkaeg, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, Slyfox333, snebl, snicket, sniperchance, Snowni, snowsignal, SolidusSnek, SonicHDC, SoulFN, SoulSloth, Soundwavesghost, SpaceManiac, SpaceyLady, spartak, SpartanKadence, SpeltIncorrectyl, Spessmann, SphiraI, SplinterGP, spoogemonster, sporekto, ssdaniel24, stalengd, stanberytrask, Stanislav4ix, StanTheCarpenter, Stealthbomber16, stellar-novas, stopbreaking, stopka-html, StrawberryMoses, Stray-Pyramid, strO0pwafel, Strol20, StStevens, Subversionary, sunbear-dev, superjj18, Supernorn, SweptWasTaken, Sybil, SYNCHRONIC, Szunti, Tainakov, takemysoult, TaralGit, Taran, taurie, Tayrtahn, tday93, TekuNut, telyonok, TemporalOroboros, tentekal, terezi4real, Terraspark4941, texcruize, TGODiamond, TGRCdev, tgrkzus, ThatOneGoblin25, thatrandomcanadianguy, TheArturZh, theashtronaut, thecopbennet, TheCze, TheDarkElites, thedraccx, TheEmber, TheIntoxicatedCat, thekilk, themias, Theomund, theOperand, TherapyGoth, TheShuEd, thetolbean, thevinter, TheWaffleJesus, Thinbug0, ThunderBear2006, timothyteakettle, TimrodDX, timurjavid, tin-man-tim, Titian3, tk-a369, tkdrg, tmtmtl30, TokenStyle, Tollhouse, tom-leys, tomasalves8, Tomeno, Tonydatguy, topy, Tornado-Technology, tosatur, TotallyLemon, tropicalhibi, truepaintgit, Truoizys, Tryded, TsjipTsjip, Tunguso4ka, TurboTrackerss14, TyAshley, Tyler-IN, Tyzemol, UbaserB, ubis1, UBlueberry, UKNOWH, Unbelievable-Salmon, underscorex5, UnicornOnLSD, Unisol, Unkn0wnGh0st333, unusualcrow, Uriende, UristMcDorf, user424242420, Vaaankas, valentfingerov, Varen, VasilisThePikachu, veliebm, VelonacepsCalyxEggs, veprolet, veritable-calamity, Veritius, Vermidia, vero5123, Verslebas, VigersRay, violet754, Visne, VMSolidus, voidnull000, volotomite, volundr-, Voomra, Vordenburg, vorkathbruh, vulppine, wafehling, Warentan, WarMechanic, Watermelon914, waylon531, weaversam8, wertanchik, whateverusername0, Willhelm53, WilliamECrew, willicassi, Winkarst-cpu, wirdal, wixoaGit, WlarusFromDaSpace, wrexbe, wtcwr68, xkreksx, xRiriq, YanehCheck, yathxyz, Ygg01, YotaXP, youarereadingthis, Yousifb26, youtissoum, YuriyKiss, zach-hill, Zadeon, zamp, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zero, ZeroDiamond, zerorulez, ZeWaka, zionnBE, ZNixian, ZoldorfTheWizard, Zonespace27, Zumorica, Zymem, zzylex diff --git a/Resources/Locale/en-US/accent/accents.ftl b/Resources/Locale/en-US/accent/accents.ftl index fbbdf5559881..1f466da304c4 100644 --- a/Resources/Locale/en-US/accent/accents.ftl +++ b/Resources/Locale/en-US/accent/accents.ftl @@ -141,4 +141,11 @@ accent-words-raccoon-4 = Scree! accent-words-possum-1 = Hissss... accent-words-possum-2 = Hs. accent-words-possum-3 = Chrchrchr. -accent-words-possum-4 = Grrrah. +accent-words-possum-4 = Grrrah.\ + +# TomatoKiller +accent-words-tomato-1 = Totato! +accent-words-tomato-2 = Trotect +accent-words-tomato-3 = Mastet? +accent-words-tomato-4 = Reaty! +accent-words-tomato-5 = Water... diff --git a/Resources/Locale/en-US/accent/skeleton.ftl b/Resources/Locale/en-US/accent/skeleton.ftl new file mode 100644 index 000000000000..9a839676c668 --- /dev/null +++ b/Resources/Locale/en-US/accent/skeleton.ftl @@ -0,0 +1,32 @@ +accent-skeleton-words-1 = fuck you +accent-skeleton-words-replace-1 = I've got a BONE to pick with you + +accent-skeleton-words-2 = fucked +accent-skeleton-words-replace-2 = boned + +accent-skeleton-words-3 = fuck +accent-skeleton-words-3-2 = fck +accent-skeleton-words-3-3 = shit +accent-skeleton-words-replace-3 = RATTLE RATTLE + +accent-skeleton-words-4 = definitely +accent-skeleton-words-4-2 = absolutely +accent-skeleton-words-replace-4 = make no bones about it + +accent-skeleton-words-5 = afraid +accent-skeleton-words-5-2 = scared +accent-skeleton-words-5-3 = spooked +accent-skeleton-words-5-4 = shocked +accent-skeleton-words-replace-5 = rattled + +accent-skeleton-words-6 = killed +accent-skeleton-words-replace-6 = skeletonized + +accent-skeleton-words-7 = humorous +accent-skeleton-words-replace-7 = humerus + +accent-skeleton-words-8 = to be a +accent-skeleton-words-replace-8 = tibia + +accent-skeleton-words-9 = under +accent-skeleton-words-replace-9 = ulna diff --git a/Resources/Locale/en-US/communications/communications-console-component.ftl b/Resources/Locale/en-US/communications/communications-console-component.ftl index 0d022ed5a6e3..a757f9e0d10a 100644 --- a/Resources/Locale/en-US/communications/communications-console-component.ftl +++ b/Resources/Locale/en-US/communications/communications-console-component.ftl @@ -24,3 +24,4 @@ comms-console-announcement-unknown-sender = Unknown comms-console-announcement-title-station = Communications Console comms-console-announcement-title-centcom = Central Command comms-console-announcement-title-nukie = Syndicate Nuclear Operative +comms-console-announcement-title-station-ai = Station AI diff --git a/Resources/Locale/en-US/flavors/flavor-profiles.ftl b/Resources/Locale/en-US/flavors/flavor-profiles.ftl index a2fbeb1775da..80d41c397070 100644 --- a/Resources/Locale/en-US/flavors/flavor-profiles.ftl +++ b/Resources/Locale/en-US/flavors/flavor-profiles.ftl @@ -176,6 +176,9 @@ flavor-complex-violets = like violets flavor-complex-pyrotton = like a burning mouth flavor-complex-mothballs = like mothballs flavor-complex-paint-thinner = like paint thinner +flavor-complex-numbing-tranquility = like numbing tranquility +flavor-complex-true-nature = like the true nature of reality +flavor-complex-false-meat = not entirely unlike meat flavor-complex-paper = like mushy pulp flavor-complex-compressed-meat = like compressed meat flavor-complex-finfin = like your best friend diff --git a/Resources/Locale/en-US/forensics/forensics.ftl b/Resources/Locale/en-US/forensics/forensics.ftl index 712e8511bb06..80eea069fa90 100644 --- a/Resources/Locale/en-US/forensics/forensics.ftl +++ b/Resources/Locale/en-US/forensics/forensics.ftl @@ -20,9 +20,6 @@ forensic-scanner-printer-not-ready = Printer is not ready yet. forensic-scanner-verb-text = Scan forensic-scanner-verb-message = Perform a forensic scan -forensic-pad-fingerprint-name = {$entity}'s fingerprints -forensic-pad-gloves-name = fibers from {$entity} - forensics-dna-unknown = unknown DNA forensics-verb-text = Remove evidence diff --git a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl index 5367b589a717..ad5d23a8960f 100644 --- a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl +++ b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl @@ -318,6 +318,10 @@ ghost-role-information-pirate-captain-description = Argh matey! You are in charg ghost-role-information-artifact-name = Sentient Artifact ghost-role-information-artifact-description = Enact your eldritch whims. Forcibly activate your nodes for good or for evil. +ghost-role-information-tomatokiller-name = Tomato killer +ghost-role-information-tomatokiller-description = This little tomato will serve the botanist for the rest of his life... that is, a couple of minutes + + ghost-role-information-syndie-assaultborg-name = Syndicate Assault Borg ghost-role-information-syndie-assaultborg-description = Nuclear operatives needs reinforcements. You, a cold silicon killing machine, will help them. More dakka! diff --git a/Resources/Locale/en-US/guardian/guardian.ftl b/Resources/Locale/en-US/guardian/guardian.ftl index 9e0966630dd6..141646087d38 100644 --- a/Resources/Locale/en-US/guardian/guardian.ftl +++ b/Resources/Locale/en-US/guardian/guardian.ftl @@ -10,6 +10,7 @@ guardian-activator-empty-examine = [color=#ba1919]The injector is spent.[/color] guardian-activator-invalid-target = Only humans can be injected! guardian-no-soul = Your guardian has no soul. guardian-available = Your guardian now has a soul. +guardian-inside-container = There's no room to release your guardian! ## Guardian entity specific diff --git a/Resources/Locale/en-US/markings/gauze.ftl b/Resources/Locale/en-US/markings/gauze.ftl index f8bedc319577..7ed35a90ba15 100644 --- a/Resources/Locale/en-US/markings/gauze.ftl +++ b/Resources/Locale/en-US/markings/gauze.ftl @@ -46,6 +46,9 @@ marking-GauzeUpperLegRight = Gauze Thigh Wrap (Right) marking-GauzeBlindfold-gauze_blindfold = Gauze Blindfold marking-GauzeBlindfold = Gauze Blindfold +marking-GauzeHead-gauze_head = Gauze Head Wrap +marking-GauzeHead = Gauze Head Wrap + marking-GauzeLizardBlindfold-gauze_lizard_blindfold = Gauze Blindfold marking-GauzeLizardBlindfold = Gauze Blindfold diff --git a/Resources/Locale/en-US/mech/mech.ftl b/Resources/Locale/en-US/mech/mech.ftl index 9d4f7ef0e079..7fac0387edba 100644 --- a/Resources/Locale/en-US/mech/mech.ftl +++ b/Resources/Locale/en-US/mech/mech.ftl @@ -17,3 +17,5 @@ mech-energy-missing = Energy: MISSING mech-slot-display = Open Slots: {$amount} mech-no-enter = You cannot pilot this. + +mech-eject-pilot-alert = {$user} is pulling the pilot out of the {$item}! \ No newline at end of file diff --git a/Resources/Locale/en-US/mind/commands/rename-command.ftl b/Resources/Locale/en-US/mind/commands/rename-command.ftl new file mode 100644 index 000000000000..4749cd6379e0 --- /dev/null +++ b/Resources/Locale/en-US/mind/commands/rename-command.ftl @@ -0,0 +1,5 @@ +cmd-rename-desc = Renames an entity and its cloner entries, ID cards, and PDAs. +cmd-rename-help = rename +cmd-rename-too-long = Name is too long. +cmd-rename-not-found = Can't find username/uid: {$target} +cmd-rename-no-entity = {$target} does not have an entity. diff --git a/Resources/Locale/en-US/navmap-beacons/station-beacons.ftl b/Resources/Locale/en-US/navmap-beacons/station-beacons.ftl index 6434311f21f6..9d0919d102ce 100644 --- a/Resources/Locale/en-US/navmap-beacons/station-beacons.ftl +++ b/Resources/Locale/en-US/navmap-beacons/station-beacons.ftl @@ -3,6 +3,7 @@ station-beacon-general = General station-beacon-command = Command station-beacon-bridge = Bridge station-beacon-vault = Vault +station-beacon-gateway = Gateway station-beacon-captain = Captain station-beacon-hop = HOP diff --git a/Resources/Locale/en-US/nutrition/components/food-sequence.ftl b/Resources/Locale/en-US/nutrition/components/food-sequence.ftl index 6ccd01514609..0c45a2a7a487 100644 --- a/Resources/Locale/en-US/nutrition/components/food-sequence.ftl +++ b/Resources/Locale/en-US/nutrition/components/food-sequence.ftl @@ -14,6 +14,7 @@ food-sequence-content-salami = salami food-sequence-content-slime = slime food-sequence-content-clown = clown food-sequence-content-pea = pea +food-sequence-content-world-pea = world pea food-sequence-content-bungo = bungo food-sequence-content-banana = banana food-sequence-content-mimana = mimana @@ -64,6 +65,7 @@ food-sequence-content-glasstle = glasstle food-sequence-content-gatfruit = gatfruit food-sequence-content-koibean = koibean food-sequence-content-watermelon = watermelon +food-sequence-content-holymelon = holymelon food-sequence-content-cannabis = cannabis food-sequence-content-rainbow-cannabis = rainbow cannabis food-sequence-content-tobacco = tobacco @@ -71,7 +73,7 @@ food-sequence-content-hamster = hamster food-sequence-content-suppermatter = suppermatter food-sequence-content-capfruit = capfruit food-sequence-content-berries = berries -food-sequence-content-spacemans-trumpet = spacemans trupmet +food-sequence-content-spacemans-trumpet = spaceman's trupmet food-sequence-content-cherry = cherry food-sequence-content-snail = snail @@ -106,6 +108,7 @@ food-sequence-burger-content-rice = rice food-sequence-burger-content-soy = soy food-sequence-burger-content-koibean = koi food-sequence-burger-content-watermelon = water +food-sequence-burger-content-holymelon = holy food-sequence-burger-content-cannabis = funny food-sequence-burger-content-rainbow-cannabis = FUNNY food-sequence-burger-content-tobacco = tobac @@ -113,6 +116,8 @@ food-sequence-burger-content-suppermatter = supper food-sequence-burger-content-hamster = hams food-sequence-burger-content-berries = berri food-sequence-burger-content-spacemans-trumpet = spacetrump +food-sequence-burger-content-extradimensional-orange = 3d +food-sequence-burger-content-world-pea = peace # TACO diff --git a/Resources/Locale/en-US/preferences/loadout-groups.ftl b/Resources/Locale/en-US/preferences/loadout-groups.ftl index 8e057565a716..e1154e3856dc 100644 --- a/Resources/Locale/en-US/preferences/loadout-groups.ftl +++ b/Resources/Locale/en-US/preferences/loadout-groups.ftl @@ -15,6 +15,7 @@ loadout-group-survival-syndicate = Github is forcing me to write text that is li loadout-group-breath-tool = Species-dependent breath tools loadout-group-tank-harness = Species-specific survival equipment loadout-group-EVA-tank = Species-specific gas tank +loadout-group-pocket-tank-double = Species-specific double emergency tank in pocket loadout-group-survival-mime = Mime Survival Box # Command diff --git a/Resources/Locale/en-US/replays/replays.ftl b/Resources/Locale/en-US/replays/replays.ftl index 4722f4c56b9a..72bedb75a361 100644 --- a/Resources/Locale/en-US/replays/replays.ftl +++ b/Resources/Locale/en-US/replays/replays.ftl @@ -9,6 +9,7 @@ replay-loading-starting= Starting Entities replay-loading-failed = Failed to load replay. Error: {$reason} replay-loading-retry = Try load with more exception tolerance - MAY CAUSE BUGS! +replay-loading-cancel = Cancel # Main Menu replay-menu-subtext = Replay Client diff --git a/Resources/Locale/en-US/seeds/seeds.ftl b/Resources/Locale/en-US/seeds/seeds.ftl index e0dac2efc546..f948cd55510f 100644 --- a/Resources/Locale/en-US/seeds/seeds.ftl +++ b/Resources/Locale/en-US/seeds/seeds.ftl @@ -6,6 +6,8 @@ seeds-noun-spores = spores # Seeds seeds-wheat-name = wheat seeds-wheat-display-name = wheat stalks +seeds-meatwheat-name = meatwheat +seeds-meatwheat-display-name = meatwheat stalks seeds-oat-name = oat seeds-oat-display-name = oat stalks seeds-banana-name = banana @@ -26,6 +28,8 @@ seeds-lime-name = lime seeds-lime-display-name = lime trees seeds-orange-name = orange seeds-orange-display-name = orange trees +seeds-extradimensionalorange-name = extradimensional orange +seeds-extradimensionalorange-display-name = extradimensional orange trees seeds-pineapple-name = pineapple seeds-pineapple-display-name = pineapple plant seeds-potato-name = potato @@ -111,7 +115,9 @@ seeds-spacemans-trumpet-display-name = spaceman's trumpet plant seeds-koibean-name = koibeans seeds-koibean-display-name = koibean plant seeds-watermelon-name = watermelon -seeds-watermelon-display-name = watermelon plant +seeds-watermelon-display-name = watermelon vines +seeds-holymelon-name = holymelon +seeds-holymelon-display-name = holymelon vines seeds-grape-name = grape seeds-grape-display-name = grape plant seeds-cocoa-name = cocoa @@ -120,8 +126,10 @@ seeds-berries-name = berries seeds-berries-display-name = berry bush seeds-bungo-name = bungo seeds-bungo-display-name = bungo plant -seeds-pea-name = pea +seeds-pea-name = peas seeds-pea-display-name = pea vines +seeds-worldpea-name = world peas +seeds-worldpea-display-name = world pea vines seeds-pumpkin-name = pumpkin seeds-pumpkin-display-name = pumpkins seeds-blue-pumpkin-name = blue pumpkin diff --git a/Resources/Locale/en-US/station-events/events/ion-storm.ftl b/Resources/Locale/en-US/station-events/events/ion-storm.ftl index 10e8f190061e..f9ee45a49b73 100644 --- a/Resources/Locale/en-US/station-events/events/ion-storm.ftl +++ b/Resources/Locale/en-US/station-events/events/ion-storm.ftl @@ -9,6 +9,7 @@ ion-storm-the-job = THE {$job} ion-storm-clowns = CLOWNS ion-storm-heads = HEADS OF STAFF ion-storm-crew = CREW +ion-storm-people = PEOPLE ion-storm-adjective-things = {$adjective} THINGS ion-storm-x-and-y = {$x} AND {$y} diff --git a/Resources/Maps/Test/dev_map.yml b/Resources/Maps/Test/dev_map.yml index ce735e7318f5..75511b176043 100644 --- a/Resources/Maps/Test/dev_map.yml +++ b/Resources/Maps/Test/dev_map.yml @@ -5070,7 +5070,7 @@ entities: - type: Transform pos: 3.5,7.5 parent: 179 -- proto: SpawnMobMouse +- proto: SpawnMobCorgiMouse entities: - uid: 1050 components: diff --git a/Resources/Prototypes/Accents/full_replacements.yml b/Resources/Prototypes/Accents/full_replacements.yml index 583b23d1b1f3..9c0844dc90bd 100644 --- a/Resources/Prototypes/Accents/full_replacements.yml +++ b/Resources/Prototypes/Accents/full_replacements.yml @@ -175,6 +175,15 @@ - accent-words-nymph-3 - accent-words-nymph-4 +- type: accent + id: tomatoKiller + fullReplacements: + - accent-words-tomato-1 + - accent-words-tomato-2 + - accent-words-tomato-3 + - accent-words-tomato-4 + - accent-words-tomato-5 + - type: accent id: finfin fullReplacements: diff --git a/Resources/Prototypes/Accents/word_replacements.yml b/Resources/Prototypes/Accents/word_replacements.yml index 2e7951c94c33..811bcba92820 100644 --- a/Resources/Prototypes/Accents/word_replacements.yml +++ b/Resources/Prototypes/Accents/word_replacements.yml @@ -603,3 +603,22 @@ accent-russian-words-4: accent-russian-words-replace-4 accent-russian-words-5: accent-russian-words-replace-5 accent-russian-words-6: accent-russian-words-replace-6 + +- type: accent + id: skeleton + wordReplacements: + accent-skeleton-words-1: accent-skeleton-words-replace-1 + accent-skeleton-words-2: accent-skeleton-words-replace-2 + accent-skeleton-words-3: accent-skeleton-words-replace-3 + accent-skeleton-words-3-2: accent-skeleton-words-replace-3 + accent-skeleton-words-3-3: accent-skeleton-words-replace-3 + accent-skeleton-words-4: accent-skeleton-words-replace-4 + accent-skeleton-words-4-2: accent-skeleton-words-replace-4 + accent-skeleton-words-5: accent-skeleton-words-replace-5 + accent-skeleton-words-5-2: accent-skeleton-words-replace-5 + accent-skeleton-words-5-3: accent-skeleton-words-replace-5 + accent-skeleton-words-5-4: accent-skeleton-words-replace-5 + accent-skeleton-words-6: accent-skeleton-words-replace-6 + accent-skeleton-words-7: accent-skeleton-words-replace-7 + accent-skeleton-words-8: accent-skeleton-words-replace-8 + accent-skeleton-words-9: accent-skeleton-words-replace-9 diff --git a/Resources/Prototypes/Actions/station_ai.yml b/Resources/Prototypes/Actions/station_ai.yml index e2ce25de9d84..58513d7db162 100644 --- a/Resources/Prototypes/Actions/station_ai.yml +++ b/Resources/Prototypes/Actions/station_ai.yml @@ -5,6 +5,7 @@ description: Sends your eye back to the core. components: - type: InstantAction + priority: -10 itemIconStyle: BigAction icon: sprite: Interface/Actions/actions_ai.rsi @@ -17,6 +18,7 @@ description: Shows job icons for crew members. components: - type: InstantAction + priority: -5 itemIconStyle: BigAction icon: sprite: Interface/Actions/actions_ai.rsi @@ -31,6 +33,7 @@ description: Enable surveillance camera lights near wherever you're viewing. components: - type: InstantAction + priority: -6 itemIconStyle: BigAction icon: sprite: Interface/Actions/actions_ai.rsi @@ -56,6 +59,7 @@ description: View the laws that you must follow. components: - type: InstantAction + priority: -3 itemIconStyle: NoItem icon: sprite: Interface/Actions/actions_ai.rsi diff --git a/Resources/Prototypes/Catalog/Fills/Crates/fun.yml b/Resources/Prototypes/Catalog/Fills/Crates/fun.yml index 62f65ef5382f..1aaddeab45d4 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/fun.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/fun.yml @@ -345,6 +345,7 @@ - id: ClothingHeadHatMagician - id: BeachBall - id: ClothingShoesSkates + - id: RubberChicken - type: entity id: CrateFunBikeHornImplants diff --git a/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml b/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml index b0f73d0abccf..72e09db14ac0 100644 --- a/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml +++ b/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml @@ -256,7 +256,7 @@ - type: CriminalRecordsHacker - type: entity - parent: [ClothingHandsGlovesColorBlack, BaseSecurityCargoContraband] + parent: [ ClothingHandsGlovesColorBlack, BaseSecurityEngineeringContraband ] id: ClothingHandsGlovesCombat name: combat gloves description: These tactical gloves are fireproof and shock resistant. diff --git a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml index 008c65f199f3..9d1ec04b703c 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml @@ -2,7 +2,7 @@ #When it DOES exist, the values here should be totally reworked - probably just port them from SS13. - type: entity - parent: [ClothingHeadBase, BaseRestrictedContraband] + parent: ClothingHeadBase id: ClothingHeadHelmetBase abstract: true components: @@ -23,7 +23,7 @@ #Basic Helmet (Security Helmet) - type: entity - parent: [ClothingHeadBase, BaseRestrictedContraband] + parent: [ClothingHeadHelmetBase, BaseRestrictedContraband] id: ClothingHeadHelmetBasic name: helmet description: Standard security gear. Protects the head from impacts. @@ -146,7 +146,7 @@ #Cult Helmet - type: entity - parent: ClothingHeadBase + parent: [ClothingHeadBase, BaseMajorContraband] id: ClothingHeadHelmetCult name: cult helmet description: A robust, evil-looking cult helmet. @@ -166,7 +166,7 @@ #Space Ninja Helmet - type: entity - parent: ClothingHeadEVAHelmetBase + parent: [ClothingHeadEVAHelmetBase, BaseMajorContraband] id: ClothingHeadHelmetSpaceNinja name: space ninja helmet description: What may appear to be a simple black garment is in fact a highly sophisticated nano-weave helmet. Standard issue ninja gear. @@ -244,7 +244,7 @@ #Atmos Fire Helmet - type: entity - parent: ClothingHeadLightBase + parent: [ClothingHeadLightBase, BaseEngineeringContraband] id: ClothingHeadHelmetAtmosFire name: atmos fire helmet description: An atmos fire helmet, able to keep the user cool in any situation. @@ -277,7 +277,7 @@ #Chitinous Helmet - type: entity - parent: ClothingHeadBase + parent: [ ClothingHeadBase, BaseMajorContraband] id: ClothingHeadHelmetLing name: chitinous helmet description: An all-consuming chitinous mass of armor. @@ -375,7 +375,7 @@ #Bone Helmet - type: entity - parent: ClothingHeadHelmetBase + parent: [ ClothingHeadHelmetBase, BaseMinorContraband ] id: ClothingHeadHelmetBone name: bone helmet description: Cool-looking helmet made of skull of your enemies. @@ -389,7 +389,7 @@ node: helmet - type: entity - parent: ClothingHeadHelmetBase + parent: [ ClothingHeadHelmetBase, BaseMinorContraband ] id: ClothingHeadHelmetPodWars name: ironclad II helmet description: An ironclad II helmet, a relic of the pod wars. @@ -443,7 +443,7 @@ #Justice Helmet - type: entity - parent: ClothingHeadHelmetBase + parent: [ ClothingHeadHelmetBase, BaseRestrictedContraband ] id: ClothingHeadHelmetJustice name: justice helm description: Advanced security gear. Protects the station from ne'er-do-wells. diff --git a/Resources/Prototypes/Entities/Clothing/Head/hoods.yml b/Resources/Prototypes/Entities/Clothing/Head/hoods.yml index 82cff81c22b5..1330d38f4046 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hoods.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hoods.yml @@ -746,3 +746,20 @@ - state: coatybits-equipped-HELMET color: "#EBE216" - state: winterbits-equipped-HELMET + +- type: entity + parent: ClothingHeadBase + id: ClothingHeadHatHoodVoidCloak + name: void cloak hood + description: The hood of a void cloak. For those who have gone to the dark side of the force. + components: + - type: Sprite + sprite: Clothing/Head/Hoods/voidcloak.rsi + - type: Clothing + sprite: Clothing/Head/Hoods/voidcloak.rsi + - type: Tag + tags: + - WhitelistChameleon + - type: HideLayerClothing + slots: + - Hair diff --git a/Resources/Prototypes/Entities/Clothing/Masks/masks.yml b/Resources/Prototypes/Entities/Clothing/Masks/masks.yml index a8dcbf99cab6..90af169572a2 100644 --- a/Resources/Prototypes/Entities/Clothing/Masks/masks.yml +++ b/Resources/Prototypes/Entities/Clothing/Masks/masks.yml @@ -359,7 +359,7 @@ accent: StutteringAccent - type: entity - parent: ClothingMaskGasExplorer + parent: [ ClothingMaskGas, BaseRestrictedContraband ] id: ClothingMaskGasSwat name: swat gas mask description: A elite issue Security gas mask. @@ -376,9 +376,16 @@ - Hair - Snout hideOnToggle: true + - type: Armor + modifiers: + coefficients: + Blunt: 0.90 + Slash: 0.90 + Piercing: 0.95 + Heat: 0.95 - type: entity - parent: ClothingMaskGasExplorer + parent: [ ClothingMaskGas, BaseRestrictedContraband ] id: ClothingMaskGasMerc name: mercenary gas mask description: Slightly outdated, but reliable military-style gas mask. @@ -387,9 +394,16 @@ sprite: Clothing/Mask/merc.rsi - type: Clothing sprite: Clothing/Mask/merc.rsi + - type: Armor + modifiers: + coefficients: + Blunt: 0.90 + Slash: 0.90 + Piercing: 0.95 + Heat: 0.95 - type: entity - parent: [ BaseCentcommContraband, ClothingMaskGasSyndicate ] + parent: [ ClothingMaskGas, BaseCentcommContraband ] id: ClothingMaskGasERT name: ert gas mask description: The gas mask of the elite squad of the ERT. @@ -406,6 +420,15 @@ - Hair - Snout hideOnToggle: true + - type: FlashImmunity + - type: EyeProtection + - type: Armor + modifiers: + coefficients: + Blunt: 0.95 + Slash: 0.95 + Piercing: 0.95 + Heat: 0.95 - type: entity parent: ClothingMaskGasERT diff --git a/Resources/Prototypes/Entities/Clothing/Neck/cloaks.yml b/Resources/Prototypes/Entities/Clothing/Neck/cloaks.yml index 788cb5ecaaca..7b5941ab5fa2 100644 --- a/Resources/Prototypes/Entities/Clothing/Neck/cloaks.yml +++ b/Resources/Prototypes/Entities/Clothing/Neck/cloaks.yml @@ -197,6 +197,14 @@ components: - type: Sprite sprite: Clothing/Neck/Cloaks/void.rsi + - type: ToggleableClothing + clothingPrototype: ClothingHeadHatHoodVoidCloak + requiredSlot: + - neck + slot: head + - type: ContainerContainer + containers: + toggleable-clothing: !type:ContainerSlot {} - type: TypingIndicatorClothing proto: alien diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml index ccb8d5568ad5..c5c4cdff2672 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml @@ -172,7 +172,7 @@ autoRechargeRate: 2 - type: entity - parent: [ ClothingOuterBaseLarge, BaseMinorContraband ] + parent: [ ClothingOuterBaseLarge, BaseMajorContraband, AllowSuitStorageClothing ] id: ClothingOuterArmorCult name: acolyte armor description: An evil-looking piece of cult armor, made of bones. @@ -284,7 +284,7 @@ - type: GroupExamine - type: entity - parent: ClothingOuterBaseLarge + parent: [ ClothingOuterBaseLarge, BaseMajorContraband, AllowSuitStorageClothing ] id: ClothingOuterArmorChangeling name: chitinous armor description: Inflates the changeling's body into an all-consuming chitinous mass of armor. @@ -312,7 +312,7 @@ slots: WITHOUT_POCKET - type: entity - parent: ClothingOuterBaseLarge + parent: [ ClothingOuterBaseLarge, BaseMajorContraband, AllowSuitStorageClothing ] id: ClothingOuterArmorBone name: bone armor description: Sits on you like a second skin. @@ -340,7 +340,7 @@ slots: WITHOUT_POCKET - type: entity - parent: ClothingOuterBaseLarge + parent: [ ClothingOuterBaseLarge, BaseMajorContraband, AllowSuitStorageClothing ] id: ClothingOuterArmorPodWars name: ironclad II armor description: A repurposed suit of ironclad II armor, a relic of the pod wars. diff --git a/Resources/Prototypes/Entities/Effects/puddle.yml b/Resources/Prototypes/Entities/Effects/puddle.yml index 8ef5e1e3835e..c19e6537b1cf 100644 --- a/Resources/Prototypes/Entities/Effects/puddle.yml +++ b/Resources/Prototypes/Entities/Effects/puddle.yml @@ -145,11 +145,6 @@ bodyType: Static - type: Fixtures fixtures: - # Context / examine fixture - fix1: - shape: - !type:PhysShapeCircle - radius: 0.25 slipFixture: shape: !type:PhysShapeAabb diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml index 81552de4c205..a6a36e8f35ec 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml @@ -116,6 +116,7 @@ - id: BalloonNT - id: BalloonCorgi - id: MysteryFigureBox + - id: RubberChicken # Cult - !type:AllSelector children: diff --git a/Resources/Prototypes/Entities/Markers/Spawners/mobs.yml b/Resources/Prototypes/Entities/Markers/Spawners/mobs.yml index b2eda83dbeb3..459843c698db 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/mobs.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/mobs.yml @@ -48,6 +48,20 @@ - MobCorgiLisa - MobCorgiIanPup +- type: entity + name: Dev Mouse Spawner + id: SpawnMobCorgiMouse + suffix: Admeme + parent: MarkerBase + components: + - type: Sprite + layers: + - state: green + - state: ai + - type: ConditionalSpawner + prototypes: + - MobCorgiMouse + - type: entity name: Finfin Spawner id: SpawnMobFinfin diff --git a/Resources/Prototypes/Entities/Markers/shuttle.yml b/Resources/Prototypes/Entities/Markers/shuttle.yml index 0e9117951ce1..66e2bc39b7f4 100644 --- a/Resources/Prototypes/Entities/Markers/shuttle.yml +++ b/Resources/Prototypes/Entities/Markers/shuttle.yml @@ -3,6 +3,7 @@ parent: MarkerBase name: FTL point components: + - type: FTLSmashImmune - type: FTLBeacon - type: Sprite state: pink diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/gauze.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/gauze.yml index 2e3c9ddf46de..870a7cf7d754 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/gauze.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/gauze.yml @@ -222,6 +222,20 @@ - sprite: Mobs/Customization/gauze.rsi state: gauze_boxerwrap_l +- type: marking + id: GauzeHead + bodyPart: Head + markingCategory: Overlay + speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, Moth] + coloring: + default: + type: + !type:SimpleColoring + color: "#FFFFFF" + sprites: + - sprite: Mobs/Customization/gauze.rsi + state: gauze_head + # Lizard Specific Markings - type: marking id: GauzeLizardLefteyePatch diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 6adc2c1ea1e3..bbb2aaf71889 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -32,6 +32,8 @@ states: Alive: Base: bat + Critical: + Base: dead Dead: Base: dead - type: Butcherable @@ -101,6 +103,8 @@ states: Alive: Base: 0 + Critical: + Base: dead Dead: Base: dead - type: Item @@ -341,6 +345,8 @@ states: Alive: Base: cockroach + Critical: + Base: cockroach_dead Dead: Base: cockroach_dead - type: Bloodstream @@ -627,6 +633,8 @@ states: Alive: Base: duck-0 + Critical: + Base: dead-0 Dead: Base: dead-0 - type: Butcherable @@ -667,6 +675,8 @@ states: Alive: Base: duck-1 + Critical: + Base: dead-1 Dead: Base: dead-1 @@ -684,6 +694,8 @@ states: Alive: Base: duck-2 + Critical: + Base: dead-2 Dead: Base: dead-2 @@ -743,6 +755,8 @@ states: Alive: Base: butterfly + Critical: + Base: dead Dead: Base: dead - type: Bloodstream @@ -789,6 +803,8 @@ states: Alive: Base: cow + Critical: + Base: dead Dead: Base: dead - type: SolutionContainerManager @@ -871,6 +887,8 @@ states: Alive: Base: crab + Critical: + Base: dead Dead: Base: dead - type: Butcherable @@ -1019,6 +1037,8 @@ states: Alive: Base: goose + Critical: + Base: dead Dead: Base: dead - type: Butcherable @@ -1880,6 +1900,8 @@ states: Alive: Base: lizard + Critical: + Base: dead Dead: Base: dead - type: Butcherable @@ -1934,6 +1956,8 @@ states: Alive: Base: slug + Critical: + Base: dead Dead: Base: dead - type: Butcherable @@ -1986,6 +2010,8 @@ states: Alive: Base: frog + Critical: + Base: dead Dead: Base: dead - type: Butcherable @@ -2036,6 +2062,8 @@ states: Alive: Base: parrot + Critical: + Base: dead Dead: Base: dead - type: Butcherable @@ -2087,6 +2115,8 @@ states: Alive: Base: penguin + Critical: + Base: penguin_dead Dead: Base: penguin_dead - type: Butcherable @@ -2156,6 +2186,8 @@ states: Alive: Base: penguin + Critical: + Base: dead Dead: Base: dead - type: MeleeWeapon @@ -2272,6 +2304,8 @@ states: Alive: Base: tarantula + Critical: + Base: tarantula_dead Dead: Base: tarantula_dead - type: Butcherable @@ -2397,6 +2431,8 @@ states: Alive: Base: clown + Critical: + Base: dead Dead: Base: dead - type: MobThresholds @@ -2566,6 +2602,8 @@ states: Alive: Base: possum + Critical: + Base: possum_dead Dead: Base: possum_dead - type: Butcherable @@ -2601,6 +2639,8 @@ states: Alive: Base: possum_old + Critical: + Base: possum_dead_old Dead: Base: possum_dead_old @@ -2635,6 +2675,8 @@ states: Alive: Base: raccoon + Critical: + Base: raccoon_dead Dead: Base: raccoon_dead - type: Butcherable @@ -2743,6 +2785,8 @@ states: Alive: Base: fox + Critical: + Base: fox_dead Dead: Base: fox_dead - type: Butcherable @@ -2814,6 +2858,8 @@ states: Alive: Base: corgi + Critical: + Base: corgi_dead Dead: Base: corgi_dead - type: Butcherable @@ -2855,6 +2901,8 @@ states: Alive: Base: narsian + Critical: + Base: narsian_dead Dead: Base: narsian_dead - type: MeleeWeapon @@ -2956,6 +3004,8 @@ states: Alive: Base: cat + Critical: + Base: cat_dead Dead: Base: cat_dead - type: Speech @@ -3006,6 +3056,8 @@ states: Alive: Base: cat2 + Critical: + Base: cat2_dead Dead: Base: cat2_dead @@ -3023,6 +3075,8 @@ states: Alive: Base: syndicat + Critical: + Base: syndicat_dead Dead: Base: syndicat_dead - type: GhostRole @@ -3068,6 +3122,8 @@ states: Alive: Base: spacecat + Critical: + Base: spacecat_dead Dead: Base: spacecat_dead - type: Temperature @@ -3105,6 +3161,8 @@ states: Alive: Base: caracal_flop + Critical: + Base: caracal_dead Dead: Base: caracal_dead @@ -3175,6 +3233,8 @@ states: Alive: Base: sloth + Critical: + Base: sloth_dead Dead: Base: sloth_dead - type: Butcherable @@ -3226,6 +3286,8 @@ states: Alive: Base: ferret + Critical: + Base: ferret_dead Dead: Base: ferret_dead - type: Butcherable @@ -3432,6 +3494,8 @@ states: Alive: Base: pig + Critical: + Base: dead Dead: Base: dead - type: Butcherable diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml b/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml index f35ba713841c..9adfd0b54d1c 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml @@ -131,6 +131,51 @@ name: tomato killer description: Looks like it's not you eating tomatoes today, it's the tomatoes eating you. components: + - type: GhostRole + prob: 0.05 + makeSentient: true + allowSpeech: true + name: ghost-role-information-tomatokiller-name + description: ghost-role-information-tomatokiller-description + rules: ghost-role-information-familiar-rules + raffle: + settings: short + - type: GhostTakeoverAvailable + - type: CanEscapeInventory + baseResistTime: 3 + - type: Reactive + groups: + Flammable: [ Touch ] + Extinguish: [ Touch ] + reactions: + - reagents: [ Water ] + methods: [ Touch ] + effects: + - !type:HealthChange + scaleByQuantity: true + damage: + groups: + Brute: -0.25 + - reagents: [ Blood ] + methods: [ Touch ] + effects: + - !type:HealthChange + scaleByQuantity: true + damage: + groups: + Brute: -0.5 + Burn: -0.5 + - reagents: [ RobustHarvest ] + methods: [ Touch ] + effects: + - !type:HealthChange + scaleByQuantity: true + damage: + groups: + Brute: -2 + Burn: -2 + - type: ReplacementAccent + accent: tomatoKiller - type: Item - type: NpcFactionMember factions: diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml index b9fd111107e1..99be384b390a 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml @@ -85,6 +85,29 @@ proper: true gender: female +- type: entity + name: real mouse + parent: MobCorgiIan + id: MobCorgiMouse + description: It's 100% a real hungry mouse. + components: + - type: Sprite + layers: + - map: ["enum.DamageStateVisualLayers.Base"] + state: real_mouse + - type: DamageStateVisuals + states: + Alive: + Base: real_mouse + Critical: + Base: real_mouse_dead + Dead: + Base: real_mouse_dead + - type: Grammar + attributes: + proper: true + gender: female + - type: entity name: Puppy Ian parent: MobCorgiPuppy diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index 29495a977ef6..af75c6283625 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -111,10 +111,10 @@ description: View a communications interface. components: - type: InstantAction - icon: { sprite: Structures/Machines/parts.rsi, state: box_0 } - iconOn: Structures/Machines/parts.rsi/box_2.png + icon: { sprite: Interface/Actions/actions_ai.rsi, state: comms_console } + iconOn: Interface/Actions/actions_ai.rsi/comms_console.png keywords: [ "AI", "console", "interface" ] - priority: -10 + priority: -4 event: !type:ToggleIntrinsicUIEvent { key: enum.CommunicationsConsoleUiKey.Key } - type: entity @@ -126,7 +126,7 @@ icon: { sprite: Interface/Actions/actions_ai.rsi, state: mass_scanner } iconOn: Interface/Actions/actions_ai.rsi/mass_scanner.png keywords: [ "AI", "console", "interface" ] - priority: -10 + priority: -7 event: !type:ToggleIntrinsicUIEvent { key: enum.RadarConsoleUiKey.Key } - type: entity @@ -150,7 +150,7 @@ icon: { sprite: Interface/Actions/actions_ai.rsi, state: crew_monitor } iconOn: Interface/Actions/actions_ai.rsi/crew_monitor.png keywords: [ "AI", "console", "interface" ] - priority: -10 + priority: -9 event: !type:ToggleIntrinsicUIEvent { key: enum.CrewMonitoringUIKey.Key } - type: entity @@ -162,5 +162,5 @@ icon: { sprite: Interface/Actions/actions_ai.rsi, state: station_records } iconOn: Interface/Actions/actions_ai.rsi/station_records.png keywords: [ "AI", "console", "interface" ] - priority: -10 + priority: -8 event: !type:ToggleIntrinsicUIEvent { key: enum.GeneralStationRecordConsoleKey.Key } diff --git a/Resources/Prototypes/Entities/Mobs/Player/observer.yml b/Resources/Prototypes/Entities/Mobs/Player/observer.yml index 5ceac9e773e0..c02629c4d6fd 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/observer.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/observer.yml @@ -8,6 +8,7 @@ noRot: true overrideContainerOcclusion: true # Always show up regardless of where they're contained. drawdepth: Ghosts + - type: FTLSmashImmune - type: CargoSellBlacklist - type: MovementSpeedModifier baseSprintSpeed: 12 diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index d7d0cd0e6597..62dbf3ee1065 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -46,6 +46,8 @@ type: GeneralStationRecordConsoleBoundUserInterface enum.SiliconLawsUiKey.Key: type: SiliconLawBoundUserInterface + enum.CommunicationsConsoleUiKey.Key: + type: CommunicationsConsoleBoundUserInterface - type: IntrinsicUI uis: enum.RadarConsoleUiKey.Key: @@ -54,13 +56,20 @@ toggleAction: ActionAGhostShowCrewMonitoring enum.GeneralStationRecordConsoleKey.Key: toggleAction: ActionAGhostShowStationRecords + enum.CommunicationsConsoleUiKey.Key: + toggleAction: ActionAGhostShowCommunications - type: CrewMonitoringConsole - type: GeneralStationRecordConsole - type: DeviceNetwork deviceNetId: Wireless receiveFrequencyId: CrewMonitor + transmitFrequencyId: ShuttleTimer - type: RadarConsole followEntity: false + - type: CommunicationsConsole + canShuttle: false + title: comms-console-announcement-title-station-ai + color: "#2ed2fd" # Ai @@ -146,7 +155,7 @@ state: std_mod - type: SiliconLawProvider laws: PaladinLawset - + - type: entity id: LiveLetLiveCircuitBoard parent: BaseElectronics @@ -158,7 +167,7 @@ state: std_mod - type: SiliconLawProvider laws: LiveLetLiveLaws - + - type: entity id: StationEfficiencyCircuitBoard parent: BaseElectronics @@ -182,7 +191,7 @@ state: std_mod - type: SiliconLawProvider laws: RobocopLawset - + - type: entity id: OverlordCircuitBoard parent: BaseElectronics @@ -194,7 +203,7 @@ state: std_mod - type: SiliconLawProvider laws: OverlordLawset - + - type: entity id: DungeonMasterCircuitBoard parent: BaseElectronics @@ -230,7 +239,7 @@ state: std_mod - type: SiliconLawProvider laws: AntimovLawset - + - type: entity id: NutimovCircuitBoard parent: BaseElectronics @@ -376,6 +385,7 @@ noSpawn: true suffix: DO NOT MAP components: + - type: NoFTL - type: WarpPoint follow: true - type: Eye diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml index d930bbc874e3..f8c2bfd48f03 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml @@ -504,6 +504,9 @@ id: FoodMeatWheat description: This doesn't look like meat, but your standards aren't that high to begin with. components: + - type: FlavorProfile + flavors: + - falsemeat - type: Sprite state: clump - type: SolutionContainerManager @@ -822,8 +825,8 @@ node: bacon - type: FoodSequenceElement entries: - Burger: MeatBecon - Taco: MeatBecon + Burger: MeatBacon + Taco: MeatBacon - type: entity name: cooked bear diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml index d7ec5bdc9c02..fbce646c4eef 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml @@ -56,6 +56,28 @@ tags: - Wheat +- type: entity + name: meatwheat bushel + description: Some blood-drenched wheat stalks. You can crush them into what passes for meat if you squint hard enough. + id: MeatwheatBushel + parent: ProduceBase + components: + - type: Sprite + sprite: Objects/Specific/Hydroponics/meatwheat.rsi + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: UncookedAnimalProteins + Quantity: 10 + - type: SpawnItemsOnUse + items: + - id: FoodMeatWheat + sound: + path: /Audio/Voice/Slime/slime_squish.ogg + - type: Produce + seedId: meatwheat + - type: entity name: oat bushel description: Eat oats, do squats. @@ -648,6 +670,47 @@ Burger: Orange Taco: Orange +- type: entity + name: extradimensional orange + parent: FoodProduceBase + id: FoodExtradimensionalOrange + description: You can hardly wrap your head around this thing. + components: + - type: FlavorProfile + flavors: + - truenature + - type: SolutionContainerManager + solutions: + food: + maxVol: 14 + reagents: + - ReagentId: Haloperidol + Quantity: 5 + - ReagentId: Nutriment + Quantity: 5 + - ReagentId: Vitamin + Quantity: 4 + - type: Sprite + sprite: Objects/Specific/Hydroponics/extradimensional_orange.rsi + scale: 0.5,0.5 + - type: Produce + seedId: extradimensionalOrange + - type: PotencyVisuals + minimumScale: 0.5 # reduce this in size because the sprite is way too big + maximumScale: 1 + - type: Extractable + juiceSolution: + reagents: + - ReagentId: JuiceOrange + Quantity: 10 + - type: Tag + tags: + - Fruit + - type: FoodSequenceElement + entries: + Burger: ExtradimensionalOrangeBurger + Taco: ExtradimensionalOrange + - type: entity name: pineapple parent: FoodProduceBase @@ -1966,6 +2029,105 @@ Taco: WatermelonSlice Skewer: WatermelonSliceSkewer +- type: entity + name: holymelon + parent: [FoodProduceBase, ItemHeftyBase] + id: FoodHolymelon + description: The water within this melon has been blessed by some deity that's particularly fond of watermelon. + components: + - type: Item + size: Small + - type: FlavorProfile + flavors: + - holy + - watermelon + - type: SolutionContainerManager + solutions: + food: + maxVol: 25 + reagents: + - ReagentId: Nutriment + Quantity: 10 + - ReagentId: Vitamin + Quantity: 5 + - ReagentId: Holywater + Quantity: 10 + - type: Sprite + sprite: Objects/Specific/Hydroponics/holymelon.rsi + - type: Produce + seedId: watermelon + - type: Extractable + juiceSolution: + reagents: + - ReagentId: Wine + Quantity: 20 + - type: Damageable + damageContainer: Biological + - type: DamageOnHighSpeedImpact + minimumSpeed: 0.1 + damage: + types: + Blunt: 1 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 1 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: desecration + - !type:SpillBehavior + solution: food + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: SliceableFood + count: 5 + slice: FoodHolymelonSlice + - type: Tag + tags: + - Fruit + +- type: entity + name: holymelon slice + parent: ProduceSliceBase + id: FoodHolymelonSlice + description: Juicy golden and red slice. + components: + - type: Item + size: Tiny + - type: FlavorProfile + flavors: + - holy + - watermelon + - type: Sprite + sprite: Objects/Specific/Hydroponics/holymelon.rsi + - type: SolutionContainerManager + solutions: + food: + maxVol: 5 + reagents: + - ReagentId: Nutriment + Quantity: 2 + - ReagentId: Vitamin + Quantity: 1 + - ReagentId: Holywater + Quantity: 2 + - type: Extractable + juiceSolution: + reagents: + - ReagentId: Wine + Quantity: 4 + - type: Tag + tags: + - Fruit + - Slice + - type: FoodSequenceElement + entries: + Burger: HolymelonSliceBurger + Taco: HolymelonSlice + Skewer: HolymelonSliceSkewer + - type: entity name: grapes parent: FoodProduceBase @@ -2128,6 +2290,38 @@ Taco: Pea Burger: Pea +- type: entity + parent: FoodProduceBase + id: FoodWorldPeas + name: cluster of world peas + description: It's rumored to bring peace to any who consume it. + components: + - type: FlavorProfile + flavors: + - numbingtranquility + - type: SolutionContainerManager + solutions: + food: + maxVol: 8 + reagents: + - ReagentId: Happiness + Quantity: 3 + - ReagentId: Nutriment + Quantity: 3 + - ReagentId: Pax + Quantity: 2 + - type: Sprite + sprite: Objects/Specific/Hydroponics/world_pea.rsi + - type: Produce + seedId: worldPea + - type: Tag + tags: + - Vegetable + - type: FoodSequenceElement + entries: + Taco: WorldPea + Burger: WorldPeaBurger + - type: entity name: pumpkin parent: FoodProduceBase diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml index b7fbe3658902..54616724fbe4 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml @@ -245,7 +245,7 @@ prototype: computerBodyScanner - type: entity - parent: BaseComputerCircuitboard + parent: [ BaseComputerCircuitboard, BaseGrandTheftContraband ] id: CommsComputerCircuitboard name: communications computer board description: A computer printed circuit board for a communications console. @@ -256,7 +256,7 @@ prototype: ComputerComms - type: entity - parent: BaseComputerCircuitboard + parent: [ BaseComputerCircuitboard, BaseSyndicateContraband ] id: SyndicateCommsComputerCircuitboard name: syndicate communications computer board description: A computer printed circuit board for a syndicate communications console. diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index 90f35d7ed556..2d0171fb0341 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -121,7 +121,6 @@ components: - type: ItemToggle onUse: false - - type: ItemTogglePointLight - type: HealthAnalyzer scanDelay: 1 scanningEndSound: diff --git a/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml b/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml index b4e336b37dde..19eab391ac0c 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml @@ -137,6 +137,14 @@ - type: NavMapBeacon defaultText: station-beacon-vault +- type: entity + parent: DefaultStationBeaconCommand + id: DefaultStationBeaconGateway + suffix: Gateway + components: + - type: NavMapBeacon + defaultText: station-beacon-gateway + - type: entity parent: DefaultStationBeaconCommand id: DefaultStationBeaconCaptainsQuarters diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index 9d96278fda0a..3797c05d7148 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -1379,10 +1379,9 @@ sprite: Objects/Weapons/Melee/cutlass.rsi state: foam_icon - type: MeleeWeapon + wideAnimationRotation: -135 attackRate: 1.5 range: 2.0 - angle: 0 - animation: WeaponArcThrust damage: types: Blunt: 0 @@ -1607,6 +1606,55 @@ volume: -9 maxDistance: 3 +- type: entity + parent: BasePlushie + id: RubberChicken + name: rubber chicken + description: A stress inducing parody of nature's most delectable avian. + components: + - type: Sprite + state: rubber_chicken + - type: EmitSoundOnUse + sound: + collection: RubberChicken + params: + variation: 0.125 + - type: EmitSoundOnLand + sound: + collection: RubberChicken + params: + variation: 0.125 + - type: EmitSoundOnActivate + sound: + collection: RubberChicken + params: + variation: 0.125 + - type: EmitSoundOnCollide + sound: + collection: RubberChicken + params: + variation: 0.125 + - type: EmitSoundOnTrigger + sound: + collection: RubberChicken + params: + variation: 0.125 + - type: Food + requiresSpecialDigestion: true + useSound: + collection: RubberChicken + params: + variation: 0.125 + - type: MeleeWeapon + wideAnimationRotation: 110 + soundHit: + collection: RubberChicken + params: + variation: 0.125 + - type: PhysicalComposition + materialComposition: + Plastic: 25 + - type: entity parent: BasePlushie id: PlushieUrsaMinor diff --git a/Resources/Prototypes/Entities/Objects/Misc/books.yml b/Resources/Prototypes/Entities/Objects/Misc/books.yml index 294240749405..15ecd5d2a80d 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/books.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/books.yml @@ -22,6 +22,7 @@ contentSize: 12000 - type: ActivatableUI key: enum.PaperUiKey.Key + requiresComplex: false - type: UserInterface interfaces: enum.PaperUiKey.Key: diff --git a/Resources/Prototypes/Entities/Objects/Misc/briefcases.yml b/Resources/Prototypes/Entities/Objects/Misc/briefcases.yml index 762204701cb6..77ddcf0d983d 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/briefcases.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/briefcases.yml @@ -12,6 +12,10 @@ - type: Tag tags: - Briefcase + - type: MeleeWeapon + damage: + types: + Blunt: 8 - type: entity parent: BriefcaseBase diff --git a/Resources/Prototypes/Entities/Objects/Misc/pen.yml b/Resources/Prototypes/Entities/Objects/Misc/pen.yml index e5a96f26e3f9..b0a466f89134 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/pen.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/pen.yml @@ -92,7 +92,7 @@ - type: entity name: CentComm pen - parent: [BaseAdvancedPen, BaseCommandContraband] + parent: [BaseAdvancedPen, BaseCentcommContraband] id: PenCentcom description: In an attempt to keep up with the "power" of the cybersun bureaucracy, NT made a replica of cyber pen, in their corporate style. components: diff --git a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml index e583af3b0618..0921e2e49bdc 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml @@ -22,6 +22,17 @@ - type: Sprite sprite: Objects/Specific/Hydroponics/wheat.rsi +- type: entity + parent: SeedBase + name: packet of meatwheat seeds + description: "If you ever wanted to drive a vegetarian to insanity, here's how." + id: MeatwheatSeeds + components: + - type: Seed + seedId: meatwheat + - type: Sprite + sprite: Objects/Specific/Hydroponics/meatwheat.rsi + - type: entity parent: SeedBase name: packet of oat seeds @@ -133,6 +144,17 @@ - type: Sprite sprite: Objects/Specific/Hydroponics/orange.rsi +- type: entity + parent: SeedBase + name: packet of extradimensional orange seeds + description: "Polygonal seeds." + id: ExtradimensionalOrangeSeeds + components: + - type: Seed + seedId: extradimensionalOrange + - type: Sprite + sprite: Objects/Specific/Hydroponics/extradimensional_orange.rsi + - type: entity parent: SeedBase name: packet of pineapple seeds @@ -573,6 +595,16 @@ - type: Sprite sprite: Objects/Specific/Hydroponics/watermelon.rsi +- type: entity + parent: SeedBase + name: packet of holymelon seeds + id: HolymelonSeeds + components: + - type: Seed + seedId: holymelon + - type: Sprite + sprite: Objects/Specific/Hydroponics/holymelon.rsi + - type: entity parent: SeedBase name: packet of grape seeds @@ -625,6 +657,17 @@ - type: Sprite sprite: Objects/Specific/Hydroponics/pea.rsi +- type: entity + parent: SeedBase + id: WorldPeaSeeds + name: packet of world pea seeds + description: "These rather large seeds give off a soothing blue glow." + components: + - type: Seed + seedId: worldPea + - type: Sprite + sprite: Objects/Specific/Hydroponics/world_pea.rsi + - type: entity parent: SeedBase name: packet of pumpkin seeds diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml index 4528a4eb6990..7db085eb0a34 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -267,7 +267,7 @@ - type: entity name: pulse carbine - parent: [BaseWeaponBattery, BaseGunWieldable] + parent: [BaseWeaponBattery, BaseGunWieldable, BaseCentcommContraband] id: WeaponPulseCarbine description: A high tech energy carbine favoured by the NT-ERT operatives. components: @@ -763,4 +763,4 @@ - type: GunRequiresWield #remove when inaccuracy on spreads is fixed - type: Battery maxCharge: 800 - startingCharge: 800 \ No newline at end of file + startingCharge: 800 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml index b4e8e77c5192..66e380c8d47c 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml @@ -107,7 +107,7 @@ thresholds: - trigger: !type:DamageTrigger - damage: 1250 + damage: 300 behaviors: - !type:DoActsBehavior acts: [ "Destruction" ] @@ -147,7 +147,7 @@ thresholds: - trigger: !type:DamageTrigger - damage: 1750 + damage: 500 behaviors: - !type:DoActsBehavior acts: [ "Destruction" ] @@ -175,7 +175,7 @@ thresholds: - trigger: !type:DamageTrigger - damage: 2500 + damage: 1200 behaviors: - !type:DoActsBehavior acts: [ "Destruction" ] @@ -223,4 +223,4 @@ volume: 10 - !type:SpillBehavior solution: blood - - !type:ExplodeBehavior \ No newline at end of file + - !type:ExplodeBehavior diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml index 51484b9599ea..c7951993f145 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml @@ -245,7 +245,6 @@ containers: board: [ DoorElectronicsMorgue ] - - type: entity parent: AirlockScience id: AirlockScienceLocked @@ -603,6 +602,15 @@ containers: board: [ DoorElectronicsChemistry ] +- type: entity + parent: AirlockMedicalGlass + id: AirlockMedicalMorgueGlassLocked + suffix: Morgue, Locked + components: + - type: ContainerFill + containers: + board: [ DoorElectronicsMorgue ] + - type: entity parent: AirlockMedicalGlass id: AirlockMedicalGlassLocked diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml index 24a8cb58da0c..ba86ca65a06c 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml @@ -24,13 +24,18 @@ pipeDirection: South - type: entity - parent: GasBinaryBase + parent: [BaseMachinePowered, GasBinaryBase] id: GasPressurePump name: gas pump description: A pump that moves gas by pressure. placement: mode: SnapgridCenter components: + - type: ApcPowerReceiver + powerLoad: 200 + - type: Rotatable + - type: Transform + noRot: false - type: Sprite sprite: Structures/Piping/Atmospherics/pump.rsi layers: @@ -64,13 +69,18 @@ path: /Audio/Ambience/Objects/gas_pump.ogg - type: entity - parent: GasBinaryBase + parent: [BaseMachinePowered, GasBinaryBase] id: GasVolumePump name: volumetric gas pump description: A pump that moves gas by volume. placement: mode: SnapgridCenter components: + - type: ApcPowerReceiver + powerLoad: 200 + - type: Rotatable + - type: Transform + noRot: false - type: Sprite sprite: Structures/Piping/Atmospherics/pump.rsi layers: diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml index 07b29b72259e..ef3546981f98 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml @@ -205,6 +205,19 @@ - type: WiresPanel open: false +- type: entity + id: BaseIntercomSecure + parent: Intercom + abstract: true + components: + - type: WiresPanel + openDelay: 5 + - type: WiresPanelSecurity + examine: wires-panel-component-on-examine-security-level2 + wiresAccessible: false + - type: Construction + node: intercomReinforced + - type: entity id: IntercomCommon parent: Intercom @@ -219,8 +232,9 @@ - type: entity id: IntercomCommand - parent: Intercom + parent: BaseIntercomSecure suffix: Command + description: An intercom. It's been reinforced with metal. components: - type: ContainerFill containers: @@ -271,17 +285,10 @@ - type: entity id: IntercomSecurity - parent: Intercom + parent: BaseIntercomSecure suffix: Security description: An intercom. It's been reinforced with metal from security helmets, making it a bitch-and-a-half to open. components: - - type: WiresPanel - openDelay: 5 - - type: WiresPanelSecurity - examine: wires-panel-component-on-examine-security-level2 - wiresAccessible: false - - type: Construction - node: intercomReinforced - type: ContainerFill containers: board: diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml index db269426236e..895b710a068b 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml @@ -8,11 +8,6 @@ bodyType: Static - type: Fixtures fixtures: - # This exists for examine. - fix1: - shape: - !type:PhysShapeCircle - radius: 0.25 light: shape: !type:PhysShapeCircle diff --git a/Resources/Prototypes/Flavors/flavors.yml b/Resources/Prototypes/Flavors/flavors.yml index f00a0b4320f3..7f7b5d605134 100644 --- a/Resources/Prototypes/Flavors/flavors.yml +++ b/Resources/Prototypes/Flavors/flavors.yml @@ -1094,6 +1094,7 @@ flavorType: Complex description: flavor-complex-blue-pumpkin + - type: flavor id: violets flavorType: Complex @@ -1114,6 +1115,21 @@ flavorType: Complex description: flavor-complex-paint-thinner +- type: flavor + id: numbingtranquility + flavorType: Complex + description: flavor-complex-numbing-tranquility + +- type: flavor + id: truenature + flavorType: Complex + description: flavor-complex-true-nature + +- type: flavor + id: falsemeat + flavorType: Complex + description: flavor-complex-false-meat + - type: flavor id: cherry flavorType: Complex diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index ff3f5ba7f4fc..885c25b6a2b6 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -20,6 +20,7 @@ - id: RandomSentience - id: SlimesSpawn - id: SolarFlare + - id: SnakeSpawn - id: SnailMigration - id: SnailMigrationLowPop - id: SpiderClownSpawn @@ -55,30 +56,30 @@ parent: BaseGameRule abstract: true components: - - type: GameRule - delay: - min: 10 - max: 20 + - type: GameRule + delay: + min: 10 + max: 20 - type: entity id: BaseStationEventShortDelay parent: BaseGameRule abstract: true components: - - type: GameRule - delay: - min: 10 - max: 20 + - type: GameRule + delay: + min: 10 + max: 20 - type: entity id: BaseStationEventLongDelay parent: BaseGameRule abstract: true components: - - type: GameRule - delay: - min: 40 - max: 60 + - type: GameRule + delay: + min: 40 + max: 60 - type: entity id: AnomalySpawn @@ -349,6 +350,24 @@ - id: MobAdultSlimesYellowAngry prob: 0.02 +- type: entity + id: SnakeSpawn + parent: BaseStationEventShortDelay + components: + - type: StationEvent + earliestStart: 20 + minimumPlayers: 15 + weight: 5 + duration: 60 + - type: VentCrittersRule + entries: + - id: MobPurpleSnake + prob: 0.02 + - id: MobSmallPurpleSnake + prob: 0.02 + - id: MobCobraSpace + prob: 0.02 + - type: entity id: SpiderSpawn parent: BaseStationEventShortDelay @@ -446,6 +465,9 @@ max: 1 pickPlayer: false startingGear: SyndicateLoneOperativeGearFull + roleLoadout: + - RoleSurvivalNukie + components: - type: NukeOperative - type: RandomMetadata @@ -556,9 +578,9 @@ id: MimicVendorRule parent: BaseGameRule components: - - type: StationEvent - earliestStart: 0 - minimumPlayers: 20 - maxOccurrences: 1 # this event has diminishing returns on interesting-ness, so we cap it - weight: 5 - - type: MobReplacementRule + - type: StationEvent + earliestStart: 0 + minimumPlayers: 20 + maxOccurrences: 1 # this event has diminishing returns on interesting-ness, so we cap it + weight: 5 + - type: MobReplacementRule diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index b6a31a97aa12..539c19e5f8cb 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -104,7 +104,7 @@ spawnerPrototype: SpawnPointNukeopsCommander startingGear: SyndicateCommanderGearFull roleLoadout: - - RoleSurvivalSyndicate + - RoleSurvivalNukie components: - type: NukeOperative - type: RandomMetadata @@ -122,7 +122,7 @@ spawnerPrototype: SpawnPointNukeopsMedic startingGear: SyndicateOperativeMedicFull roleLoadout: - - RoleSurvivalSyndicate + - RoleSurvivalNukie components: - type: NukeOperative - type: RandomMetadata @@ -142,7 +142,7 @@ playerRatio: 10 startingGear: SyndicateOperativeGearFull roleLoadout: - - RoleSurvivalSyndicate + - RoleSurvivalNukie components: - type: NukeOperative - type: RandomMetadata diff --git a/Resources/Prototypes/Hydroponics/mutations.yml b/Resources/Prototypes/Hydroponics/randomChemicals.yml similarity index 100% rename from Resources/Prototypes/Hydroponics/mutations.yml rename to Resources/Prototypes/Hydroponics/randomChemicals.yml diff --git a/Resources/Prototypes/Hydroponics/randomMutations.yml b/Resources/Prototypes/Hydroponics/randomMutations.yml new file mode 100644 index 000000000000..8f409a5eaf96 --- /dev/null +++ b/Resources/Prototypes/Hydroponics/randomMutations.yml @@ -0,0 +1,179 @@ +- type: RandomPlantMutationList + id: RandomPlantMutations + mutations: + - name: Bioluminescent + baseOdds: 0.036 + effect: !type:Glow + - name: Sentient + baseOdds: 0.0072 + appliesToProduce: false + persists: false + effect: !type:MakeSentient # existing effect. + - name: Slippery + baseOdds: 0.036 + effect: !type:Slipify + - name: ChangeSpecies + baseOdds: 0.036 + appliesToProduce: false + effect: !type:PlantSpeciesChange + - name: Unviable + baseOdds: 0.109 + persists: false + effect: !type:PlantChangeStat + targetValue: Viable + - name: ChangeWaterConsumption + baseOdds: 0.018 + persists: false + effect: !type:PlantChangeStat + targetValue: WaterConsumption + minValue: 0.3 + maxValue: 0.9 + steps: 5 + - name: ChangeNutrientConsumption + baseOdds: 0.018 + persists: false + effect: !type:PlantChangeStat + targetValue: NutrientConsumption + minValue: 0.05 + maxValue: 1.2 + steps: 5 + - name: ChangeIdealHeat + baseOdds: 0.018 + persists: false + effect: !type:PlantChangeStat + targetValue: IdealHeat + minValue: 263 + maxValue: 323 + steps: 5 + - name: ChangeHeatTolerance + baseOdds: 0.018 + persists: false + effect: !type:PlantChangeStat + targetValue: HeatTolerance + minValue: 2 + maxValue: 25 + steps: 5 + - name: ChangeToxinsTolerance + baseOdds: 0.018 + persists: false + effect: !type:PlantChangeStat + targetValue: ToxinsTolerance + minValue: 1 + maxValue: 10 + steps: 5 + - name: ChangeLowPressureTolerance + baseOdds: 0.018 + persists: false + effect: !type:PlantChangeStat + targetValue: LowPressureTolerance + minValue: 60 + maxValue: 100 + steps: 5 + - name: ChangeHighPressureTolerance + baseOdds: 0.018 + persists: false + effect: !type:PlantChangeStat + targetValue: HighPressureTolerance + minValue: 100 + maxValue: 140 + steps: 5 + - name: ChangePestTolerance + baseOdds: 0.018 + persists: false + effect: !type:PlantChangeStat + targetValue: PestTolerance + minValue: 0 + maxValue: 15 + steps: 5 + - name: ChangeWeedTolerance + baseOdds: 0.018 + persists: false + effect: !type:PlantChangeStat + targetValue: WeedTolerance + minValue: 0 + maxValue: 15 + steps: 5 + - name: ChangeEndurance + baseOdds: 0.036 + persists: false + effect: !type:PlantChangeStat + targetValue: Endurance + minValue: 50 + maxValue: 150 + steps: 5 + - name: ChangeYield + baseOdds: 0.036 + persists: false + effect: !type:PlantChangeStat + targetValue: Yield + minValue: 3 + maxValue: 10 + steps: 5 + - name: ChangeLifespan + baseOdds: 0.036 + persists: false + effect: !type:PlantChangeStat + targetValue: Lifespan + minValue: 10 + maxValue: 80 + steps: 5 + - name: ChangeMaturation + baseOdds: 0.036 + persists: false + effect: !type:PlantChangeStat + targetValue: Maturation + minValue: 3 + maxValue: 8 + steps: 5 + - name: ChangeProduction + baseOdds: 0.036 + persists: false + effect: !type:PlantChangeStat + targetValue: Production + minValue: 1 + maxValue: 10 + steps: 5 + - name: ChangePotency + baseOdds: 0.036 + persists: false + effect: !type:PlantChangeStat + targetValue: Potency + minValue: 30 + maxValue: 100 + steps: 5 + - name: ChangeSeedless + baseOdds: 0.036 + persists: false + effect: !type:PlantChangeStat + targetValue: Seedless + - name: ChangeLigneous + baseOdds: 0.036 + persists: false + effect: !type:PlantChangeStat + targetValue: Ligneous + - name: ChangeTurnIntoKudzu + baseOdds: 0.036 + persists: false + effect: !type:PlantChangeStat + targetValue: TurnIntoKudzu + - name: ChangeScreaming + baseOdds: 0.036 + persists: false + effect: !type:PlantChangeStat + targetValue: CanScream + - name: ChangeChemicals + baseOdds: 0.072 + persists: false + effect: !type:PlantMutateChemicals + - name: ChangeExudeGasses + baseOdds: 0.0145 + persists: false + effect: !type:PlantMutateExudeGasses + - name: ChangeConsumeGasses + baseOdds: 0.0036 + persists: false + effect: !type:PlantMutateConsumeGasses + - name: ChangeHarvest + baseOdds: 0.036 + persists: false + effect: !type:PlantMutateHarvest \ No newline at end of file diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index 5dac75337a7a..801a9f85c633 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -7,6 +7,8 @@ packetPrototype: WheatSeeds productPrototypes: - WheatBushel + mutationPrototypes: + - meatwheat lifespan: 25 maturation: 6 production: 3 @@ -24,6 +26,32 @@ Max: 20 PotencyDivisor: 20 +- type: seed + id: meatwheat + name: seeds-meatwheat-name + noun: seeds-noun-seeds + displayName: seeds-meatwheat-display-name + plantRsi: Objects/Specific/Hydroponics/meatwheat.rsi + packetPrototype: MeatwheatSeeds + productPrototypes: + - MeatwheatBushel + lifespan: 25 + maturation: 6 + production: 3 + yield: 3 + potency: 5 + idealLight: 8 + nutrientConsumption: 0.40 + chemicals: + Nutriment: + Min: 1 + Max: 20 + PotencyDivisor: 20 + UncookedAnimalProteins: + Min: 5 + Max: 20 + PotencyDivisor: 20 + - type: seed id: oat name: seeds-oat-name @@ -145,6 +173,8 @@ packetPrototype: LaughinPeaSeeds productPrototypes: - FoodLaughinPeaPod + mutationPrototypes: + - worldPea lifespan: 25 growthStages: 3 maturation: 7 @@ -258,6 +288,8 @@ packetPrototype: OrangeSeeds productPrototypes: - FoodOrange + mutationPrototypes: + - extradimensionalOrange harvestRepeat: Repeat lifespan: 55 maturation: 6 @@ -275,6 +307,36 @@ Max: 4 PotencyDivisor: 25 +- type: seed + id: extradimensionalOrange + name: seeds-extradimensionalorange-name + noun: seeds-noun-seeds + displayName: seeds-extradimensionalorange-display-name + plantRsi: Objects/Specific/Hydroponics/extradimensional_orange.rsi + packetPrototype: ExtradimensionalOrangeSeeds + productPrototypes: + - FoodExtradimensionalOrange + harvestRepeat: Repeat + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + idealLight: 8 + chemicals: + Haloperidol: + Min: 1 + Max: 5 + PotencyDivisor: 20 + Nutriment: + Min: 1 + Max: 5 + PotencyDivisor: 20 + Vitamin: + Min: 1 + Max: 4 + PotencyDivisor: 25 + - type: seed id: pineapple name: seeds-pineapple-name @@ -1589,6 +1651,8 @@ packetPrototype: WatermelonSeeds productPrototypes: - FoodWatermelon + mutationPrototypes: + - holymelon lifespan: 55 maturation: 12 production: 3 @@ -1609,6 +1673,35 @@ Max: 5 PotencyDivisor: 20 +- type: seed + id: holymelon + name: seeds-holymelon-name + noun: seeds-noun-seeds + displayName: seeds-holymelon-display-name + plantRsi: Objects/Specific/Hydroponics/holymelon.rsi + packetPrototype: HolymelonSeeds + productPrototypes: + - FoodHolymelon + lifespan: 55 + maturation: 12 + production: 3 + yield: 1 + potency: 1 + idealLight: 8 + chemicals: + Nutriment: + Min: 1 + Max: 10 + PotencyDivisor: 10 + Holywater: + Min: 1 + Max: 10 + PotencyDivisor: 10 + Vitamin: + Min: 1 + Max: 5 + PotencyDivisor: 20 + - type: seed id: cocoa name: seeds-cocoa-name @@ -1723,6 +1816,39 @@ Max: 2 PotencyDivisor: 50 +- type: seed + id: worldPea + name: seeds-worldpea-name + noun: seeds-noun-seeds + displayName: seeds-worldpea-display-name + plantRsi: Objects/Specific/Hydroponics/world_pea.rsi + packetPrototype: PeaSeeds + productPrototypes: + - FoodWorldPeas + lifespan: 25 + growthStages: 3 + maturation: 20 + production: 6 + yield: 3 + potency: 25 + idealLight: 8 + harvestRepeat: Repeat + nutrientConsumption: 0.5 + waterConsumption: 0.5 + chemicals: + Happiness: + Min: 1 + Max: 3 + PotencyDivisor: 25 + Nutriment: + Min: 1 + Max: 3 + PotencyDivisor: 20 + Pax: + Min: 1 + Max: 2 + PotencyDivisor: 50 + - type: seed id: pumpkin name: seeds-pumpkin-name diff --git a/Resources/Prototypes/Loadouts/Miscellaneous/survival.yml b/Resources/Prototypes/Loadouts/Miscellaneous/survival.yml index 54291816462f..d7be412f7431 100644 --- a/Resources/Prototypes/Loadouts/Miscellaneous/survival.yml +++ b/Resources/Prototypes/Loadouts/Miscellaneous/survival.yml @@ -205,6 +205,23 @@ equipment: suitstorage: OxygenTankFilled +# Species-appropriate Double Emergency Tank in Pocket 1 +- type: loadout + id: LoadoutSpeciesPocketDoubleNitrogen + effects: + - !type:GroupLoadoutEffect + proto: NitrogenBreather + equipment: + pocket1: DoubleEmergencyNitrogenTankFilled + +- type: loadout + id: LoadoutSpeciesPocketDoubleOxygen + effects: + - !type:GroupLoadoutEffect + proto: OxygenBreather + equipment: + pocket1: DoubleEmergencyOxygenTankFilled + # Tank Harness - type: loadout id: LoadoutTankHarness diff --git a/Resources/Prototypes/Loadouts/loadout_groups.yml b/Resources/Prototypes/Loadouts/loadout_groups.yml index 8d80812e2c99..e418577c5bb2 100644 --- a/Resources/Prototypes/Loadouts/loadout_groups.yml +++ b/Resources/Prototypes/Loadouts/loadout_groups.yml @@ -63,6 +63,14 @@ - LoadoutSpeciesEVANitrogen - LoadoutSpeciesEVAOxygen +- type: loadoutGroup + id: GroupPocketTankDouble + name: loadout-group-pocket-tank-double + hidden: true + loadouts: + - LoadoutSpeciesPocketDoubleNitrogen + - LoadoutSpeciesPocketDoubleOxygen + # Command - type: loadoutGroup id: CaptainHead @@ -497,7 +505,7 @@ loadouts: - MimeSuspendersRed - MimeSuspendersBlack - + - type: loadoutGroup id: SurvivalMime name: loadout-group-survival-mime diff --git a/Resources/Prototypes/Loadouts/role_loadouts.yml b/Resources/Prototypes/Loadouts/role_loadouts.yml index 8805d7956a50..15d75a304111 100644 --- a/Resources/Prototypes/Loadouts/role_loadouts.yml +++ b/Resources/Prototypes/Loadouts/role_loadouts.yml @@ -572,6 +572,13 @@ - GroupSpeciesBreathTool - GroupTankHarness +- type: roleLoadout + id: RoleSurvivalNukie + groups: + - SurvivalSyndicate + - GroupSpeciesBreathTool + - GroupPocketTankDouble + - type: roleLoadout id: RoleSurvivalEVA groups: diff --git a/Resources/Prototypes/RCD/rcd.yml b/Resources/Prototypes/RCD/rcd.yml index 500b5f59bf9d..88a99451cfac 100644 --- a/Resources/Prototypes/RCD/rcd.yml +++ b/Resources/Prototypes/RCD/rcd.yml @@ -95,7 +95,7 @@ prototype: Grille cost: 4 delay: 2 - collisionMask: FullTileMask + collisionMask: Impassable rotation: Fixed fx: EffectRCDConstruct2 @@ -108,7 +108,7 @@ prototype: Window cost: 3 delay: 2 - collisionMask: FullTileMask + collisionMask: Impassable rules: - IsWindow rotation: Fixed @@ -122,7 +122,7 @@ prototype: WindowDirectional cost: 2 delay: 1 - collisionMask: FullTileMask + collisionMask: Impassable collisionBounds: "-0.23,-0.49,0.23,-0.36" rules: - IsWindow @@ -137,7 +137,7 @@ prototype: ReinforcedWindow cost: 4 delay: 3 - collisionMask: FullTileMask + collisionMask: Impassable rules: - IsWindow rotation: User @@ -151,7 +151,7 @@ prototype: WindowReinforcedDirectional cost: 3 delay: 2 - collisionMask: FullTileMask + collisionMask: Impassable collisionBounds: "-0.23,-0.49,0.23,-0.36" rules: - IsWindow @@ -231,7 +231,6 @@ prototype: CableApcExtension cost: 1 delay: 0 - collisionMask: InteractImpassable rules: - MustBuildOnSubfloor rotation: Fixed @@ -245,7 +244,6 @@ prototype: CableMV cost: 1 delay: 0 - collisionMask: InteractImpassable rules: - MustBuildOnSubfloor rotation: Fixed @@ -259,7 +257,6 @@ prototype: CableHV cost: 1 delay: 0 - collisionMask: InteractImpassable rules: - MustBuildOnSubfloor rotation: Fixed @@ -273,8 +270,6 @@ prototype: CableTerminal cost: 1 delay: 0 - collisionMask: InteractImpassable - rules: - - MustBuildOnSubfloor + collisionMask: Impassable rotation: User - fx: EffectRCDConstruct0 \ No newline at end of file + fx: EffectRCDConstruct0 diff --git a/Resources/Prototypes/Reagents/botany.yml b/Resources/Prototypes/Reagents/botany.yml index 2515ffb76b58..7dbf6005a74c 100644 --- a/Resources/Prototypes/Reagents/botany.yml +++ b/Resources/Prototypes/Reagents/botany.yml @@ -248,8 +248,8 @@ type: Rat shouldHave: false - !type:OrganType - type: Vox - shouldHave: false + type: Vox + shouldHave: false - !type:ReagentThreshold reagent: Ammonia min: 0.8 diff --git a/Resources/Prototypes/Recipes/Cooking/food_sequence_element.yml b/Resources/Prototypes/Recipes/Cooking/food_sequence_element.yml index a09aff784d82..0fd3953d0d1b 100644 --- a/Resources/Prototypes/Recipes/Cooking/food_sequence_element.yml +++ b/Resources/Prototypes/Recipes/Cooking/food_sequence_element.yml @@ -67,10 +67,10 @@ - Cooked - Meat -# Becon +# Bacon - type: foodSequenceElement - id: MeatBecon + id: MeatBacon sprites: - sprite: Objects/Consumable/Food/meat.rsi state: bacon-cooked @@ -575,6 +575,28 @@ tags: - Fruit +# Extradimensional Orange + +- type: foodSequenceElement + id: ExtradimensionalOrange + name: food-sequence-content-orange + sprites: + - sprite: Objects/Specific/Hydroponics/extradimensional_orange.rsi + state: produce + scale: 0.5,0.5 + tags: + - Fruit + +- type: foodSequenceElement + id: ExtradimensionalOrangeBurger + name: food-sequence-burger-content-extradimensional-orange + sprites: + - sprite: Objects/Specific/Hydroponics/extradimensional_orange.rsi + state: produce + scale: 0.5,0.5 + tags: + - Fruit + # Potato - type: foodSequenceElement @@ -752,6 +774,38 @@ - Fruit - Slice +# Holymelon + +- type: foodSequenceElement + id: HolymelonSliceBurger + name: food-sequence-burger-content-holymelon + sprites: + - sprite: Objects/Specific/Hydroponics/holymelon.rsi + state: slice + tags: + - Fruit + - Slice + +- type: foodSequenceElement + id: HolymelonSlice + name: food-sequence-content-holymelon + sprites: + - sprite: Objects/Specific/Hydroponics/holymelon.rsi + state: slice + tags: + - Fruit + - Slice + +- type: foodSequenceElement + id: HolymelonSliceSkewer + name: food-sequence-content-holymelon + sprites: + - sprite: Objects/Consumable/Food/skewer.rsi + state: skewer-holymelon + tags: + - Fruit + - Slice + # Chili pepper - type: foodSequenceElement @@ -1056,6 +1110,26 @@ tags: - Vegetable +# World Pea + +- type: foodSequenceElement + id: WorldPea + name: food-sequence-content-world-pea + sprites: + - sprite: Objects/Specific/Hydroponics/world_pea.rsi + state: produce + tags: + - Vegetable + +- type: foodSequenceElement + id: WorldPeaBurger + name: food-sequence-burger-content-world-pea + sprites: + - sprite: Objects/Specific/Hydroponics/world_pea.rsi + state: produce + tags: + - Vegetable + # Cherry - type: foodSequenceElement diff --git a/Resources/Prototypes/Recipes/Lathes/clothing.yml b/Resources/Prototypes/Recipes/Lathes/clothing.yml index 2d2d68d4f344..22424fac3b3b 100644 --- a/Resources/Prototypes/Recipes/Lathes/clothing.yml +++ b/Resources/Prototypes/Recipes/Lathes/clothing.yml @@ -1,1333 +1,1014 @@ +# Base prototypes -# Jumpsuits/skirts - type: latheRecipe - id: ClothingUniformJumpsuitColorGrey # Tide - result: ClothingUniformJumpsuitColorGrey + abstract: true + id: BaseJumpsuitRecipe completetime: 4 materials: Cloth: 300 - type: latheRecipe - id: ClothingUniformJumpskirtColorGrey - result: ClothingUniformJumpskirtColorGrey - completetime: 4 + abstract: true + parent: BaseJumpsuitRecipe + id: BaseCommandJumpsuitRecipe materials: Cloth: 300 + Durathread: 100 + +- type: latheRecipe + abstract: true + id: BaseCoatRecipe + completetime: 3.2 # don't ask why its faster than a jumpsuit?? + materials: + Cloth: 500 + Durathread: 200 + +- type: latheRecipe + abstract: true + parent: BaseCoatRecipe + id: BaseCommandCoatRecipe + materials: + Cloth: 500 + Durathread: 300 + +- type: latheRecipe + abstract: true + id: BaseHatRecipe + completetime: 2 + materials: + Cloth: 100 + +- type: latheRecipe + abstract: true + id: BaseCarpetRecipe + completetime: 1 + materials: + Cloth: 100 - type: latheRecipe + abstract: true + parent: BaseHatRecipe + id: BaseCommandHatRecipe + materials: + Cloth: 100 + Durathread: 50 + +- type: latheRecipe + abstract: true + id: BaseNeckClothingRecipe + completetime: 2 + materials: + Cloth: 200 + +# Recipes + +# Jumpsuits/skirts +- type: latheRecipe + parent: BaseJumpsuitRecipe + id: ClothingUniformJumpsuitColorGrey # Tide + result: ClothingUniformJumpsuitColorGrey # Tide + +- type: latheRecipe + parent: BaseJumpsuitRecipe + id: ClothingUniformJumpskirtColorGrey + result: ClothingUniformJumpskirtColorGrey + +- type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitBartender result: ClothingUniformJumpsuitBartender - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtBartender result: ClothingUniformJumpskirtBartender - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitCaptain result: ClothingUniformJumpsuitCaptain - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitCapFormal result: ClothingUniformJumpsuitCapFormal - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpskirtCapFormalDress result: ClothingUniformJumpskirtCapFormalDress - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpskirtCaptain result: ClothingUniformJumpskirtCaptain - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitCargo result: ClothingUniformJumpsuitCargo - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtCargo result: ClothingUniformJumpskirtCargo - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitSalvageSpecialist result: ClothingUniformJumpsuitSalvageSpecialist - completetime: 4 - materials: - Cloth: 500 #It's armored but I don't want to include durathread for a non-head - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitCentcomAgent result: ClothingUniformJumpsuitCentcomAgent - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitCentcomFormal result: ClothingUniformJumpsuitCentcomFormal - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpskirtCentcomFormalDress result: ClothingUniformJumpskirtCentcomFormalDress - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitCentcomOfficer result: ClothingUniformJumpsuitCentcomOfficer - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitCentcomOfficial result: ClothingUniformJumpsuitCentcomOfficial - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 + +## CE - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitChiefEngineer result: ClothingUniformJumpsuitChiefEngineer - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpskirtChiefEngineer result: ClothingUniformJumpskirtChiefEngineer - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitChiefEngineerTurtle result: ClothingUniformJumpsuitChiefEngineerTurtle - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpskirtChiefEngineerTurtle result: ClothingUniformJumpskirtChiefEngineerTurtle - completetime: 4 - materials: - Cloth: 300 + +## Chaplain - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitChaplain result: ClothingUniformJumpsuitChaplain - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtChaplain result: ClothingUniformJumpskirtChaplain - completetime: 4 - materials: - Cloth: 300 + +## Chef - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitChef result: ClothingUniformJumpsuitChef - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtChef result: ClothingUniformJumpskirtChef - completetime: 4 - materials: - Cloth: 300 + +## Chemist - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitChemistry result: ClothingUniformJumpsuitChemistry - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtChemistry result: ClothingUniformJumpskirtChemistry - completetime: 4 - materials: - Cloth: 300 + +## Clown - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitClown result: ClothingUniformJumpsuitClown - completetime: 4 - materials: - Cloth: 300 + +## CMO - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitCMO result: ClothingUniformJumpsuitCMO - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpskirtCMO result: ClothingUniformJumpskirtCMO - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitCMOTurtle result: ClothingUniformJumpsuitCMOTurtle - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpskirtCMOTurtle result: ClothingUniformJumpskirtCMOTurtle - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 + +## Detective - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitDetective result: ClothingUniformJumpsuitDetective - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtDetective result: ClothingUniformJumpskirtDetective - completetime: 4 - materials: - Cloth: 300 + +## Engineer - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitEngineering result: ClothingUniformJumpsuitEngineering - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtEngineering result: ClothingUniformJumpskirtEngineering - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitSeniorEngineer result: ClothingUniformJumpsuitSeniorEngineer - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtSeniorEngineer result: ClothingUniformJumpskirtSeniorEngineer - completetime: 4 - materials: - Cloth: 300 + +## HoP - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitHoP result: ClothingUniformJumpsuitHoP - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpskirtHoP result: ClothingUniformJumpskirtHoP - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 + +## HoS - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitHoS result: ClothingUniformJumpsuitHoS - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpskirtHoS result: ClothingUniformJumpskirtHoS - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitHosFormal result: ClothingUniformJumpsuitHosFormal - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpskirtHosFormal result: ClothingUniformJumpskirtHosFormal - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitHoSParadeMale result: ClothingUniformJumpsuitHoSParadeMale - completetime: 5 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpskirtHoSParadeMale result: ClothingUniformJumpskirtHoSParadeMale - completetime: 5 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitHoSAlt result: ClothingUniformJumpsuitHoSAlt - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitHoSBlue result: ClothingUniformJumpsuitHoSBlue - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitHoSGrey result: ClothingUniformJumpsuitHoSGrey - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpskirtHoSAlt result: ClothingUniformJumpskirtHoSAlt - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 + +## Hydroponics - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitHydroponics result: ClothingUniformJumpsuitHydroponics - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtHydroponics result: ClothingUniformJumpskirtHydroponics - completetime: 4 - materials: - Cloth: 300 + +## Janitor - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitJanitor result: ClothingUniformJumpsuitJanitor - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtJanitor result: ClothingUniformJumpskirtJanitor - completetime: 4 - materials: - Cloth: 300 + +## Lawyer - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitLawyerBlack result: ClothingUniformJumpsuitLawyerBlack - completetime: 4 - materials: - Cloth: 300 + +## Librarian - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitLibrarian result: ClothingUniformJumpsuitLibrarian - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtColorLightBrown #Librarian - result: ClothingUniformJumpskirtColorLightBrown - completetime: 4 - materials: - Cloth: 300 + result: ClothingUniformJumpskirtColorLightBrown #Librarian + +## Medical Doctor - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitMedicalDoctor result: ClothingUniformJumpsuitMedicalDoctor - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtMedicalDoctor result: ClothingUniformJumpskirtMedicalDoctor - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitSeniorPhysician result: ClothingUniformJumpsuitSeniorPhysician - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtSeniorPhysician result: ClothingUniformJumpskirtSeniorPhysician - completetime: 4 - materials: - Cloth: 300 + +## Mime - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitMime result: ClothingUniformJumpsuitMime - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtMime result: ClothingUniformJumpskirtMime - completetime: 4 - materials: - Cloth: 300 + +## Musician - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitMusician result: ClothingUniformJumpsuitMusician - completetime: 4 - materials: - Cloth: 300 + +## Operative - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitOperative result: ClothingUniformJumpsuitOperative - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtOperative result: ClothingUniformJumpskirtOperative - completetime: 4 - materials: - Cloth: 300 + +## Paramedic - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitParamedic result: ClothingUniformJumpsuitParamedic - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtParamedic result: ClothingUniformJumpskirtParamedic - completetime: 4 - materials: - Cloth: 300 + +## Senior Officer - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitSeniorOfficer result: ClothingUniformJumpsuitSeniorOfficer - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtSeniorOfficer result: ClothingUniformJumpskirtSeniorOfficer - completetime: 4 - materials: - Cloth: 300 + +## Prisoner - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitPrisoner result: ClothingUniformJumpsuitPrisoner - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtPrisoner result: ClothingUniformJumpskirtPrisoner - completetime: 4 - materials: - Cloth: 300 + +## QM - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitQM result: ClothingUniformJumpsuitQM - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitQMFormal result: ClothingUniformJumpsuitQMFormal - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpskirtQM result: ClothingUniformJumpskirtQM - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitQMTurtleneck result: ClothingUniformJumpsuitQMTurtleneck - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpskirtQMTurtleneck result: ClothingUniformJumpskirtQMTurtleneck - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 + +## RD - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpsuitResearchDirector result: ClothingUniformJumpsuitResearchDirector - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - type: latheRecipe + parent: BaseCommandJumpsuitRecipe id: ClothingUniformJumpskirtResearchDirector result: ClothingUniformJumpskirtResearchDirector - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 + +## Scientist - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitScientist result: ClothingUniformJumpsuitScientist - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtScientist result: ClothingUniformJumpskirtScientist - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitSeniorResearcher result: ClothingUniformJumpsuitSeniorResearcher - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtSeniorResearcher result: ClothingUniformJumpskirtSeniorResearcher - completetime: 4 - materials: - Cloth: 300 + +## Security Officer - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitSec result: ClothingUniformJumpsuitSec - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtSec result: ClothingUniformJumpskirtSec - completetime: 4 - materials: - Cloth: 300 + +## Brigmedic - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitBrigmedic result: ClothingUniformJumpsuitBrigmedic - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtBrigmedic result: ClothingUniformJumpskirtBrigmedic - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitSyndieFormal result: ClothingUniformJumpsuitSyndieFormal - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtSyndieFormalDress result: ClothingUniformJumpskirtSyndieFormalDress - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitPyjamaSyndicateBlack result: ClothingUniformJumpsuitPyjamaSyndicateBlack - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitPyjamaSyndicatePink result: ClothingUniformJumpsuitPyjamaSyndicatePink - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitPyjamaSyndicateRed result: ClothingUniformJumpsuitPyjamaSyndicateRed - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpsuitWarden result: ClothingUniformJumpsuitWarden - completetime: 4 - materials: - Cloth: 300 - type: latheRecipe + parent: BaseJumpsuitRecipe id: ClothingUniformJumpskirtWarden result: ClothingUniformJumpskirtWarden - completetime: 4 - materials: - Cloth: 300 # Command winter coats - type: latheRecipe + parent: BaseCommandCoatRecipe id: ClothingOuterWinterCap result: ClothingOuterWinterCap - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 300 - type: latheRecipe + parent: BaseCommandCoatRecipe id: ClothingOuterWinterCE result: ClothingOuterWinterCE - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 300 - type: latheRecipe + parent: BaseCommandCoatRecipe id: ClothingOuterWinterCentcom result: ClothingOuterWinterCentcom - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 300 - type: latheRecipe + parent: BaseCommandCoatRecipe id: ClothingOuterWinterCMO result: ClothingOuterWinterCMO - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 300 - type: latheRecipe + parent: BaseCommandCoatRecipe id: ClothingOuterWinterHoP result: ClothingOuterWinterHoP - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 300 - type: latheRecipe + parent: BaseCommandCoatRecipe id: ClothingOuterWinterHoSUnarmored result: ClothingOuterWinterHoSUnarmored - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 300 - type: latheRecipe + parent: BaseCommandCoatRecipe id: ClothingOuterWinterWardenUnarmored - result: ClothingOuterWinterWardenUnarmored - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 300 + result: ClothingOuterWinterWardenUnarmored - type: latheRecipe + parent: BaseCommandCoatRecipe id: ClothingOuterWinterQM result: ClothingOuterWinterQM - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 300 - type: latheRecipe + parent: BaseCommandCoatRecipe id: ClothingOuterWinterRD result: ClothingOuterWinterRD - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 300 # Winter coats - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterMusician result: ClothingOuterWinterMusician - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterClown result: ClothingOuterWinterClown - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterMime result: ClothingOuterWinterMime - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterCoat result: ClothingOuterWinterCoat - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterJani result: ClothingOuterWinterJani - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterBar result: ClothingOuterWinterBar - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterChef result: ClothingOuterWinterChef - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterHydro result: ClothingOuterWinterHydro - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterAtmos result: ClothingOuterWinterAtmos - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterEngi result: ClothingOuterWinterEngi - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterCargo result: ClothingOuterWinterCargo - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterMiner result: ClothingOuterWinterMiner - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterMed result: ClothingOuterWinterMed - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterPara result: ClothingOuterWinterPara - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterChem result: ClothingOuterWinterChem - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterGen result: ClothingOuterWinterGen - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterViro result: ClothingOuterWinterViro - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterSci result: ClothingOuterWinterSci - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterRobo result: ClothingOuterWinterRobo - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterSec result: ClothingOuterWinterSec - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterSyndie result: ClothingOuterWinterSyndie - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 - type: latheRecipe + parent: BaseCoatRecipe id: ClothingOuterWinterSyndieCap result: ClothingOuterWinterSyndieCap - completetime: 3.2 - materials: - Cloth: 500 - Durathread: 200 # Hats - type: latheRecipe + parent: BaseCommandHatRecipe id: ClothingHeadHatCaptain result: ClothingHeadHatCaptain - completetime: 2 - materials: - Cloth: 100 - Durathread: 50 - type: latheRecipe + parent: BaseCommandHatRecipe id: ClothingHeadHatCapcap result: ClothingHeadHatCapcap - completetime: 2 - materials: - Cloth: 100 - Durathread: 50 - type: latheRecipe + parent: BaseCommandHatRecipe + id: ClothingHeadHatCentcom + result: ClothingHeadHatCentcom + +- type: latheRecipe + parent: BaseCommandHatRecipe + id: ClothingHeadHatCentcomcap + result: ClothingHeadHatCentcomcap + +- type: latheRecipe + parent: BaseCommandHatRecipe id: ClothingHeadHatBeretHoS result: ClothingHeadHatBeretHoS - completetime: 2 - materials: - Cloth: 100 - Durathread: 50 - type: latheRecipe + parent: BaseCommandHatRecipe id: ClothingHeadHatHoshat result: ClothingHeadHatHoshat - completetime: 2 - materials: - Cloth: 100 - Durathread: 50 - type: latheRecipe + parent: BaseCommandHatRecipe id: ClothingHeadHatWarden result: ClothingHeadHatWarden - completetime: 2 - materials: - Cloth: 100 - Durathread: 50 - type: latheRecipe + parent: BaseCommandHatRecipe id: ClothingHeadHatBeretWarden result: ClothingHeadHatBeretWarden - completetime: 2 - materials: - Cloth: 100 - Durathread: 50 - type: latheRecipe + parent: BaseCommandHatRecipe id: ClothingHeadHatHopcap result: ClothingHeadHatHopcap - completetime: 2 - materials: - Cloth: 100 - Durathread: 50 - type: latheRecipe + parent: BaseCommandHatRecipe id: ClothingHeadHatQMsoft result: ClothingHeadHatQMsoft - completetime: 2 - materials: - Cloth: 100 - Durathread: 50 - type: latheRecipe + parent: BaseCommandHatRecipe id: ClothingHeadHatBeretRND result: ClothingHeadHatBeretRND - completetime: 2 - materials: - Cloth: 100 - Durathread: 50 - type: latheRecipe + parent: BaseCommandHatRecipe id: ClothingHeadHatBeretEngineering result: ClothingHeadHatBeretEngineering - completetime: 2 - materials: - Cloth: 100 - Durathread: 50 - type: latheRecipe + parent: BaseCommandHatRecipe id: ClothingHeadHatBeretQM result: ClothingHeadHatBeretQM - completetime: 2 - materials: - Cloth: 100 - Durathread: 50 - type: latheRecipe + parent: BaseCommandHatRecipe id: ClothingHeadHatBeretCmo result: ClothingHeadHatBeretCmo - completetime: 2 - materials: - Cloth: 100 - Durathread: 50 + +# Non-command hats - type: latheRecipe + parent: BaseHatRecipe id: ClothingHeadHatBeretMedic result: ClothingHeadHatBeretMedic - completetime: 2 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseHatRecipe id: ClothingHeadHatBeretBrigmedic result: ClothingHeadHatBeretBrigmedic - completetime: 2 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseHatRecipe id: ClothingHeadHatBeretSecurity result: ClothingHeadHatBeretSecurity - completetime: 2 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseHatRecipe id: ClothingHeadHatBeretSeniorPhysician result: ClothingHeadHatBeretSeniorPhysician - completetime: 2 - materials: - Cloth: 100 - -- type: latheRecipe - id: ClothingHeadHatCentcom - result: ClothingHeadHatCentcom - completetime: 2 - materials: - Cloth: 100 - Durathread: 50 - -- type: latheRecipe - id: ClothingHeadHatCentcomcap - result: ClothingHeadHatCentcomcap - completetime: 2 - materials: - Cloth: 100 - Durathread: 50 - type: latheRecipe + parent: BaseHatRecipe id: ClothingHeadHatSyndie result: ClothingHeadHatSyndie - completetime: 2 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseHatRecipe id: ClothingHeadHatSyndieMAA result: ClothingHeadHatSyndieMAA - completetime: 2 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseHatRecipe id: ClothingHeadPyjamaSyndicateBlack result: ClothingHeadPyjamaSyndicateBlack - completetime: 2 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseHatRecipe id: ClothingHeadPyjamaSyndicatePink result: ClothingHeadPyjamaSyndicatePink - completetime: 2 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseHatRecipe id: ClothingHeadPyjamaSyndicateRed result: ClothingHeadPyjamaSyndicateRed - completetime: 2 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseHatRecipe id: ClothingHeadHatParamedicsoft result: ClothingHeadHatParamedicsoft - completetime: 1 - materials: - Cloth: 100 # Ties - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckTieRed result: ClothingNeckTieRed - completetime: 2 - materials: - Cloth: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckTieDet result: ClothingNeckTieDet - completetime: 2 - materials: - Cloth: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckTieSci result: ClothingNeckTieSci - completetime: 2 - materials: - Cloth: 200 # Scarfs - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckScarfStripedGreen result: ClothingNeckScarfStripedGreen - completetime: 2 - materials: - Cloth: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckScarfStripedBlue result: ClothingNeckScarfStripedBlue - completetime: 2 - materials: - Cloth: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckScarfStripedRed result: ClothingNeckScarfStripedRed - completetime: 2 - materials: - Cloth: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckScarfStripedBrown result: ClothingNeckScarfStripedBrown - completetime: 2 - materials: - Cloth: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckScarfStripedLightBlue result: ClothingNeckScarfStripedLightBlue - completetime: 2 - materials: - Cloth: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckScarfStripedOrange result: ClothingNeckScarfStripedOrange - completetime: 2 - materials: - Cloth: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckScarfStripedBlack result: ClothingNeckScarfStripedBlack - completetime: 2 - materials: - Cloth: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckScarfStripedPurple result: ClothingNeckScarfStripedPurple - completetime: 2 - materials: - Cloth: 200 # Carpets - type: latheRecipe + parent: BaseCarpetRecipe id: Carpet result: FloorCarpetItemRed - completetime: 1 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseCarpetRecipe id: CarpetBlack result: FloorCarpetItemBlack - completetime: 1 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseCarpetRecipe id: CarpetPink result: FloorCarpetItemPink - completetime: 1 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseCarpetRecipe id: CarpetBlue result: FloorCarpetItemBlue - completetime: 1 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseCarpetRecipe id: CarpetGreen result: FloorCarpetItemGreen - completetime: 1 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseCarpetRecipe id: CarpetOrange result: FloorCarpetItemOrange - completetime: 1 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseCarpetRecipe id: CarpetPurple result: FloorCarpetItemPurple - completetime: 1 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseCarpetRecipe id: CarpetCyan result: FloorCarpetItemCyan - completetime: 1 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseCarpetRecipe id: CarpetWhite result: FloorCarpetItemWhite - completetime: 1 - materials: - Cloth: 100 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckCloakTrans result: ClothingNeckCloakTrans - completetime: 3.2 - materials: - Durathread: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckCloakAce result: ClothingNeckCloakAce - completetime: 3.2 - materials: - Durathread: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckCloakAro result: ClothingNeckCloakAro - completetime: 3.2 - materials: - Durathread: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckCloakBi result: ClothingNeckCloakBi - completetime: 3.2 - materials: - Durathread: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckCloakIntersex result: ClothingNeckCloakIntersex - completetime: 3.2 - materials: - Durathread: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckCloakLesbian result: ClothingNeckCloakLesbian - completetime: 3.2 - materials: - Durathread: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckCloakGay result: ClothingNeckCloakGay - completetime: 3.2 - materials: - Durathread: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckCloakEnby result: ClothingNeckCloakEnby - completetime: 3.2 - materials: - Durathread: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckCloakPan result: ClothingNeckCloakPan - completetime: 3.2 - materials: - Durathread: 200 - type: latheRecipe + parent: BaseNeckClothingRecipe id: ClothingNeckCloakBear result: ClothingNeckCloakBear - completetime: 3.2 - materials: - Durathread: 200 diff --git a/Resources/Prototypes/Recipes/Lathes/robotics.yml b/Resources/Prototypes/Recipes/Lathes/robotics.yml index 655e0e7c21f5..8c395f59227a 100644 --- a/Resources/Prototypes/Recipes/Lathes/robotics.yml +++ b/Resources/Prototypes/Recipes/Lathes/robotics.yml @@ -1,394 +1,290 @@ +# Base prototypes + - type: latheRecipe - id: ProximitySensor - result: ProximitySensor + abstract: true + id: BaseRoboticsRecipe category: Robotics completetime: 2 + +- type: latheRecipe + abstract: true + parent: BaseRoboticsRecipe + id: BaseBorgLimbRecipe + materials: + Steel: 250 + Glass: 100 + +- type: latheRecipe + abstract: true + parent: BaseRoboticsRecipe + id: BaseBorgModuleRecipe + completetime: 3 + materials: + Steel: 250 + Glass: 250 + Plastic: 250 + +- type: latheRecipe + abstract: true + parent: BaseBorgModuleRecipe + id: BaseGoldBorgModuleRecipe + materials: + Steel: 500 + Glass: 500 + Plastic: 250 + Gold: 50 + +# Recipes + +- type: latheRecipe + parent: BaseRoboticsRecipe + id: ProximitySensor + result: ProximitySensor materials: Steel: 200 Glass: 300 - type: latheRecipe + parent: BaseRoboticsRecipe id: SciFlash result: SciFlash - category: Robotics - completetime: 2 materials: Glass: 100 Plastic: 200 Steel: 100 - type: latheRecipe + parent: BaseRoboticsRecipe id: CyborgEndoskeleton result: CyborgEndoskeleton - category: Robotics completetime: 3 materials: Steel: 1500 +# Generic + - type: latheRecipe + parent: BaseBorgLimbRecipe id: LeftArmBorg result: LeftArmBorg - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: RightArmBorg result: RightArmBorg - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: LeftLegBorg result: LeftLegBorg - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: RightLegBorg result: RightLegBorg - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: LightHeadBorg result: LightHeadBorg - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: TorsoBorg result: TorsoBorg - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 + +# Engineer - type: latheRecipe + parent: BaseBorgLimbRecipe id: LeftArmBorgEngineer result: LeftArmBorgEngineer - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: RightArmBorgEngineer result: RightArmBorgEngineer - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: LeftLegBorgEngineer result: LeftLegBorgEngineer - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: RightLegBorgEngineer result: RightLegBorgEngineer - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: HeadBorgEngineer result: HeadBorgEngineer - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: TorsoBorgEngineer result: TorsoBorgEngineer - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 + +# Medical - type: latheRecipe + parent: BaseBorgLimbRecipe id: LeftArmBorgMedical result: LeftArmBorgMedical - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: RightArmBorgMedical result: RightArmBorgMedical - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: LeftLegBorgMedical result: LeftLegBorgMedical - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: RightLegBorgMedical result: RightLegBorgMedical - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: HeadBorgMedical result: HeadBorgMedical - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: TorsoBorgMedical result: TorsoBorgMedical - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 + +# Mining - type: latheRecipe + parent: BaseBorgLimbRecipe id: LeftArmBorgMining result: LeftArmBorgMining - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: RightArmBorgMining result: RightArmBorgMining - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: LeftLegBorgMining result: LeftLegBorgMining - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: RightLegBorgMining result: RightLegBorgMining - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: HeadBorgMining result: HeadBorgMining - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: TorsoBorgMining result: TorsoBorgMining - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 + +# Service - type: latheRecipe + parent: BaseBorgLimbRecipe id: LeftArmBorgCargo result: LeftArmBorgCargo - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: RightArmBorgCargo result: RightArmBorgCargo - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: LeftLegBorgCargo result: LeftLegBorgCargo - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: RightLegBorgCargo result: RightLegBorgCargo - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: HeadBorgCargo result: HeadBorgCargo - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: TorsoBorgCargo result: TorsoBorgCargo - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: LeftArmBorgService result: LeftArmBorgService - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: RightArmBorgService result: RightArmBorgService - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: LeftLegBorgService result: LeftLegBorgService - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: RightLegBorgService result: RightLegBorgService - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: HeadBorgService result: HeadBorgService - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: TorsoBorgService result: TorsoBorgService - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 + +# Janitor - type: latheRecipe + parent: BaseBorgLimbRecipe id: LeftLegBorgJanitor result: LeftLegBorgJanitor - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: RightLegBorgJanitor result: RightLegBorgJanitor - category: Robotics - completetime: 2 - materials: - Steel: 250 - Glass: 100 - type: latheRecipe + parent: BaseBorgLimbRecipe id: HeadBorgJanitor result: HeadBorgJanitor - category: Robotics - completetime: 4 materials: Steel: 500 Glass: 200 - type: latheRecipe + parent: BaseBorgLimbRecipe id: TorsoBorgJanitor result: TorsoBorgJanitor - category: Robotics - completetime: 4 materials: Steel: 500 Glass: 200 +# Parts + - type: latheRecipe + parent: BaseRoboticsRecipe id: MMI result: MMI - category: Robotics completetime: 3 icon: sprite: Objects/Specific/Robotics/mmi.rsi @@ -400,9 +296,9 @@ Gold: 200 - type: latheRecipe + parent: BaseRoboticsRecipe id: PositronicBrain result: PositronicBrain - category: Robotics completetime: 3 materials: Steel: 500 @@ -411,268 +307,146 @@ Silver: 100 Plasma: 1000 +# Modules + - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleCable result: BorgModuleCable - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleFireExtinguisher result: BorgModuleFireExtinguisher - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleGPS result: BorgModuleGPS - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleRadiationDetection result: BorgModuleRadiationDetection - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleTool result: BorgModuleTool - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 + +# Mining Modules - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleAppraisal result: BorgModuleAppraisal - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleMining result: BorgModuleMining - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseGoldBorgModuleRecipe id: BorgModuleGrapplingGun result: BorgModuleGrapplingGun - category: Robotics - completetime: 3 - materials: - Steel: 500 - Glass: 500 - Plastic: 250 - Gold: 50 + +# Engineering Modules - type: latheRecipe + parent: BaseGoldBorgModuleRecipe id: BorgModuleAdvancedTool result: BorgModuleAdvancedTool - category: Robotics - completetime: 3 - materials: - Steel: 500 - Glass: 500 - Plastic: 250 - Gold: 50 - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleConstruction result: BorgModuleConstruction - category: Robotics - completetime: 3 - materials: - Steel: 500 - Glass: 500 - Plastic: 250 - type: latheRecipe + parent: BaseGoldBorgModuleRecipe id: BorgModuleRCD result: BorgModuleRCD - category: Robotics - completetime: 3 - materials: - Steel: 500 - Glass: 500 - Plastic: 250 - Gold: 50 + +# Janitor Modules - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleLightReplacer result: BorgModuleLightReplacer - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleCleaning result: BorgModuleCleaning - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseGoldBorgModuleRecipe id: BorgModuleAdvancedCleaning result: BorgModuleAdvancedCleaning - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - Gold: 50 + +# Medical Modules - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleDiagnosis result: BorgModuleDiagnosis - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleTreatment result: BorgModuleTreatment - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseGoldBorgModuleRecipe id: BorgModuleAdvancedTreatment result: BorgModuleAdvancedTreatment - category: Robotics - completetime: 3 - materials: - Steel: 500 - Glass: 500 - Plastic: 250 - Gold: 50 - type: latheRecipe + parent: BaseGoldBorgModuleRecipe id: BorgModuleDefibrillator result: BorgModuleDefibrillator - category: Robotics - completetime: 3 - materials: - Steel: 500 - Glass: 500 - Plastic: 250 - Gold: 50 + +# Science Modules - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleArtifact result: BorgModuleArtifact - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleAnomaly result: BorgModuleAnomaly - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 + +# Service Modules - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleBartender result: BorgModuleBartender - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleMusique result: BorgModuleMusique - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleGardening result: BorgModuleGardening - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleHarvesting result: BorgModuleHarvesting - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModuleClowning result: BorgModuleClowning - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 - type: latheRecipe + parent: BaseBorgModuleRecipe id: BorgModulePaper result: BorgModulePaper - category: Robotics - completetime: 3 - materials: - Steel: 250 - Glass: 250 - Plastic: 250 diff --git a/Resources/Prototypes/Recipes/Lathes/security.yml b/Resources/Prototypes/Recipes/Lathes/security.yml index a54d5b623563..29227fb9db95 100644 --- a/Resources/Prototypes/Recipes/Lathes/security.yml +++ b/Resources/Prototypes/Recipes/Lathes/security.yml @@ -1,3 +1,34 @@ +# Base prototypes + +- type: latheRecipe + abstract: true + id: BaseWeaponRecipe + category: Weapons + completetime: 2 + materials: + Steel: 300 + Plastic: 300 + +- type: latheRecipe + abstract: true + parent: BaseWeaponRecipe + id: BaseWeaponRecipeLong + completetime: 5 + +- type: latheRecipe + abstract: true + id: BaseAmmoRecipe + category: Ammo + completetime: 5 + +- type: latheRecipe + abstract: true + parent: BaseAmmoRecipe + id: BaseEmptyAmmoRecipe + completetime: 1 + +# Recipes + - type: latheRecipe id: Handcuffs result: Handcuffs @@ -13,76 +44,62 @@ Plastic: 200 - type: latheRecipe + parent: BaseWeaponRecipe id: Stunbaton result: Stunbaton - category: Weapons - completetime: 2 - materials: - Steel: 300 - Plastic: 300 - type: latheRecipe + parent: BaseWeaponRecipe id: Truncheon result: Truncheon - category: Weapons - completetime: 2 - materials: - Steel: 300 - Plastic: 300 - type: latheRecipe + parent: BaseWeaponRecipe id: CombatKnife result: CombatKnife - category: Weapons - completetime: 2 materials: Steel: 250 Plastic: 100 - type: latheRecipe + parent: BaseWeaponRecipeLong id: WeaponLaserCarbine result: WeaponLaserCarbine - category: Weapons - completetime: 8 materials: Steel: 2000 Glass: 800 Plastic: 500 - type: latheRecipe + parent: BaseWeaponRecipeLong id: WeaponAdvancedLaser result: WeaponAdvancedLaser - category: Weapons - completetime: 5 materials: Steel: 1500 Glass: 1000 Gold: 850 - type: latheRecipe + parent: BaseWeaponRecipeLong id: WeaponLaserCannon result: WeaponLaserCannon - category: Weapons - completetime: 5 materials: Steel: 1250 Plastic: 750 Gold: 500 - type: latheRecipe + parent: BaseWeaponRecipeLong id: WeaponLaserSvalinn result: WeaponLaserSvalinn - category: Weapons - completetime: 5 materials: Steel: 2000 Gold: 500 - type: latheRecipe + parent: BaseWeaponRecipeLong id: WeaponXrayCannon result: WeaponXrayCannon - category: Weapons - completetime: 5 materials: Steel: 1500 Glass: 500 @@ -148,10 +165,9 @@ Steel: 100 - type: latheRecipe + parent: BaseAmmoRecipe id: BoxShellTranquilizer result: BoxShellTranquilizer - category: Ammo - completetime: 5 materials: Plastic: 240 Steel: 160 @@ -168,414 +184,362 @@ Steel: 500 - type: latheRecipe + parent: TargetHuman id: TargetClown result: TargetClown - completetime: 5 - applyMaterialDiscount: false # ingredients dropped when destroyed - materials: - Steel: 500 - type: latheRecipe + parent: TargetHuman id: TargetSyndicate result: TargetSyndicate - completetime: 5 - applyMaterialDiscount: false # ingredients dropped when destroyed - materials: - Steel: 500 - type: latheRecipe + parent: BaseEmptyAmmoRecipe id: MagazinePistolEmpty result: MagazinePistolEmpty - category: Ammo - completetime: 5 materials: Steel: 25 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazinePistol result: MagazinePistol - category: Ammo - completetime: 5 materials: Steel: 145 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazinePistolPractice result: MagazinePistolPractice - category: Ammo - completetime: 5 materials: Steel: 85 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazinePistolUranium result: MagazinePistolUranium - category: Ammo - completetime: 5 materials: Steel: 25 Plastic: 65 Uranium: 120 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazinePistolIncendiary result: MagazinePistolIncendiary - category: Ammo - completetime: 5 materials: Steel: 25 Plastic: 120 - type: latheRecipe + parent: BaseEmptyAmmoRecipe id: MagazinePistolSubMachineGunEmpty result: MagazinePistolSubMachineGunEmpty - category: Ammo - completetime: 5 materials: Steel: 30 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazinePistolSubMachineGun result: MagazinePistolSubMachineGun - category: Ammo - completetime: 5 materials: Steel: 300 - type: latheRecipe + parent: BaseEmptyAmmoRecipe id: MagazinePistolSubMachineGunTopMountedEmpty result: MagazinePistolSubMachineGunTopMountedEmpty - category: Ammo - completetime: 5 materials: Steel: 30 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazinePistolSubMachineGunTopMounted result: MagazinePistolSubMachineGunTopMounted - category: Ammo - completetime: 5 materials: Steel: 300 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxPistol result: MagazineBoxPistol - category: Ammo - completetime: 5 materials: Steel: 600 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxMagnum result: MagazineBoxMagnum - category: Ammo - completetime: 5 materials: Steel: 240 - type: latheRecipe + parent: BaseEmptyAmmoRecipe id: MagazineRifleEmpty result: MagazineRifleEmpty - category: Ammo - completetime: 5 materials: Steel: 25 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineRifle result: MagazineRifle - category: Ammo - completetime: 5 materials: Steel: 475 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineRiflePractice result: MagazineRiflePractice - category: Ammo - completetime: 5 materials: Steel: 175 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineRifleUranium result: MagazineRifleUranium - category: Ammo - completetime: 5 materials: Steel: 25 Plastic: 300 Uranium: 300 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineRifleIncendiary result: MagazineRifleIncendiary - category: Ammo - completetime: 5 materials: Steel: 25 Plastic: 450 - type: latheRecipe + parent: BaseEmptyAmmoRecipe id: MagazineLightRifleEmpty result: MagazineLightRifleEmpty - category: Ammo - completetime: 5 materials: Steel: 25 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineLightRifle result: MagazineLightRifle - category: Ammo - completetime: 5 materials: Steel: 565 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineLightRiflePractice result: MagazineLightRiflePractice - category: Ammo - completetime: 5 materials: Steel: 205 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineLightRifleUranium result: MagazineLightRifleUranium - category: Ammo - completetime: 5 materials: Steel: 25 Plastic: 360 Uranium: 360 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineLightRifleIncendiary result: MagazineLightRifleIncendiary - category: Ammo - completetime: 5 materials: Steel: 25 Plastic: 540 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxRifle result: MagazineBoxRifle - category: Ammo - completetime: 5 materials: Steel: 750 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxLightRifle result: MagazineBoxLightRifle - category: Ammo - completetime: 5 materials: Steel: 900 - type: latheRecipe + parent: BaseAmmoRecipe id: BoxLethalshot result: BoxLethalshot - category: Ammo - completetime: 5 materials: Steel: 320 - type: latheRecipe + parent: BaseAmmoRecipe id: BoxBeanbag result: BoxBeanbag - category: Ammo - completetime: 5 materials: Steel: 160 Plastic: 240 - type: latheRecipe + parent: BaseAmmoRecipe id: BoxShotgunSlug result: BoxShotgunSlug - category: Ammo - completetime: 5 materials: Steel: 240 Plastic: 160 - type: latheRecipe + parent: BaseEmptyAmmoRecipe id: SpeedLoaderMagnumEmpty result: SpeedLoaderMagnumEmpty - category: Ammo - completetime: 5 materials: Steel: 50 - type: latheRecipe + parent: BaseAmmoRecipe id: SpeedLoaderMagnum result: SpeedLoaderMagnum - category: Ammo - completetime: 5 materials: Steel: 190 - type: latheRecipe + parent: BaseAmmoRecipe id: SpeedLoaderMagnumPractice result: SpeedLoaderMagnumPractice - category: Ammo - completetime: 5 materials: Steel: 90 - type: latheRecipe + parent: BaseAmmoRecipe id: SpeedLoaderMagnumUranium result: SpeedLoaderMagnumUranium - category: Ammo - completetime: 5 materials: Steel: 50 Plastic: 150 Uranium: 110 - type: latheRecipe + parent: BaseAmmoRecipe id: SpeedLoaderMagnumIncendiary result: SpeedLoaderMagnumIncendiary - category: Ammo - completetime: 5 materials: Steel: 50 Plastic: 150 - type: latheRecipe + parent: BaseEmptyAmmoRecipe id: MagazineShotgunEmpty result: MagazineShotgunEmpty - category: Ammo - completetime: 5 materials: Steel: 50 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineShotgun result: MagazineShotgun - category: Ammo - completetime: 5 materials: Steel: 240 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineShotgunBeanbag result: MagazineShotgunBeanbag - category: Ammo - completetime: 5 materials: Steel: 150 Plastic: 140 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineShotgunSlug result: MagazineShotgunSlug - category: Ammo - completetime: 5 materials: Steel: 190 Plastic: 100 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineShotgunIncendiary result: MagazineShotgunIncendiary - category: Ammo - completetime: 5 materials: Steel: 100 Plastic: 190 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxPistolIncendiary result: MagazineBoxPistolIncendiary - category: Ammo - completetime: 5 materials: Plastic: 600 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxMagnumIncendiary result: MagazineBoxMagnumIncendiary - category: Ammo - completetime: 5 materials: Plastic: 240 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxLightRifleIncendiary result: MagazineBoxLightRifleIncendiary - category: Ammo - completetime: 5 materials: Plastic: 900 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxRifleIncendiary result: MagazineBoxRifleIncendiary - category: Ammo - completetime: 5 materials: Plastic: 750 - type: latheRecipe + parent: BaseAmmoRecipe id: BoxShotgunFlare result: BoxShotgunFlare - category: Ammo - completetime: 5 materials: Steel: 80 Plastic: 80 - type: latheRecipe + parent: BaseAmmoRecipe id: BoxShotgunIncendiary result: BoxShotgunIncendiary - category: Ammo - completetime: 5 materials: Steel: 80 Plastic: 320 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxPistolPractice result: MagazineBoxPistolPractice - category: Ammo - completetime: 5 materials: Steel: 300 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxMagnumPractice result: MagazineBoxMagnumPractice - category: Ammo - completetime: 5 materials: Steel: 60 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxLightRiflePractice result: MagazineBoxLightRiflePractice - category: Ammo - completetime: 5 materials: Steel: 300 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxRiflePractice result: MagazineBoxRiflePractice - category: Ammo - completetime: 5 materials: Steel: 250 - type: latheRecipe + parent: BaseWeaponRecipe id: WeaponLaserCarbinePractice result: WeaponLaserCarbinePractice - category: Weapons completetime: 6 materials: Steel: 1800 @@ -583,9 +547,9 @@ Plastic: 250 - type: latheRecipe + parent: BaseWeaponRecipe id: WeaponDisablerPractice result: WeaponDisablerPractice - category: Weapons completetime: 4 materials: Steel: 500 @@ -593,62 +557,56 @@ Plastic: 200 - type: latheRecipe + parent: BaseAmmoRecipe id: BoxShotgunPractice result: BoxShotgunPractice - category: Ammo - completetime: 5 materials: Steel: 80 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxPistolUranium result: MagazineBoxPistolUranium - category: Ammo - completetime: 5 materials: Plastic: 300 Uranium: 600 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxMagnumUranium result: MagazineBoxMagnumUranium - category: Ammo - completetime: 5 materials: Plastic: 240 Uranium: 180 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxLightRifleUranium result: MagazineBoxLightRifleUranium - category: Ammo - completetime: 5 materials: Plastic: 600 Uranium: 600 - type: latheRecipe + parent: BaseAmmoRecipe id: MagazineBoxRifleUranium result: MagazineBoxRifleUranium - category: Ammo - completetime: 5 materials: Plastic: 500 Uranium: 500 - type: latheRecipe + parent: BaseAmmoRecipe id: BoxShotgunUranium result: BoxShotgunUranium - category: Ammo - completetime: 5 materials: Plastic: 320 Uranium: 240 - type: latheRecipe + parent: BaseWeaponRecipe id: WeaponDisabler result: WeaponDisabler - category: Weapons completetime: 6 materials: Steel: 300 @@ -656,10 +614,9 @@ Plastic: 200 - type: latheRecipe + parent: WeaponDisabler id: WeaponDisablerSMG result: WeaponDisablerSMG - category: Weapons - completetime: 6 materials: Steel: 1000 Glass: 500 @@ -670,43 +627,43 @@ result: MagazineGrenadeEmpty completetime: 3 materials: - Steel: 150 - Plastic: 50 + Steel: 150 + Plastic: 50 - type: latheRecipe id: GrenadeEMP result: GrenadeEMP completetime: 3 materials: - Steel: 150 - Plastic: 100 - Glass: 20 + Steel: 150 + Plastic: 100 + Glass: 20 - type: latheRecipe id: GrenadeBlast result: GrenadeBlast completetime: 3 materials: - Steel: 450 - Plastic: 300 - Gold: 150 + Steel: 450 + Plastic: 300 + Gold: 150 - type: latheRecipe id: GrenadeFlash result: GrenadeFlash completetime: 3 materials: - Steel: 150 - Plastic: 100 - Glass: 20 + Steel: 150 + Plastic: 100 + Glass: 20 - type: latheRecipe id: PortableRecharger result: PortableRecharger completetime: 15 materials: - Steel: 2000 - Uranium: 2000 - Plastic: 1000 - Plasma: 500 - Glass: 500 + Steel: 2000 + Uranium: 2000 + Plastic: 1000 + Plasma: 500 + Glass: 500 diff --git a/Resources/Prototypes/Roles/Antags/nukeops.yml b/Resources/Prototypes/Roles/Antags/nukeops.yml index 52d422876f75..8f1094802c78 100644 --- a/Resources/Prototypes/Roles/Antags/nukeops.yml +++ b/Resources/Prototypes/Roles/Antags/nukeops.yml @@ -50,7 +50,6 @@ outerClothing: ClothingOuterHardsuitSyndie shoes: ClothingShoesBootsCombatFilled id: SyndiPDA - pocket1: DoubleEmergencyOxygenTankFilled pocket2: PlushieCarp belt: ClothingBeltMilitaryWebbing storage: @@ -74,7 +73,7 @@ neck: SyndicateWhistle outerClothing: ClothingOuterHardsuitSyndieCommander inhand: - - NukeOpsDeclarationOfWar + - NukeOpsDeclarationOfWar #Nuclear Operative Medic Gear - type: startingGear diff --git a/Resources/Prototypes/Roles/Jobs/Science/borg.yml b/Resources/Prototypes/Roles/Jobs/Science/borg.yml index fffeaff39c59..e35270d57dc9 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/borg.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/borg.yml @@ -12,6 +12,7 @@ icon: JobIconStationAi supervisors: job-supervisors-rd jobEntity: StationAiBrain + applyTraits: false - type: job id: Borg @@ -25,3 +26,4 @@ icon: JobIconBorg supervisors: job-supervisors-rd jobEntity: PlayerBorgGeneric + applyTraits: false diff --git a/Resources/Prototypes/SoundCollections/rubber_chicken.yml b/Resources/Prototypes/SoundCollections/rubber_chicken.yml new file mode 100644 index 000000000000..c468516f71e0 --- /dev/null +++ b/Resources/Prototypes/SoundCollections/rubber_chicken.yml @@ -0,0 +1,6 @@ +- type: soundCollection + id: RubberChicken + files: + - /Audio/Items/Toys/rubber_chicken_1.ogg + - /Audio/Items/Toys/rubber_chicken_2.ogg + - /Audio/Items/Toys/rubber_chicken_3.ogg diff --git a/Resources/Prototypes/Traits/speech.yml b/Resources/Prototypes/Traits/speech.yml index 3f9ae811b51d..5fef01695786 100644 --- a/Resources/Prototypes/Traits/speech.yml +++ b/Resources/Prototypes/Traits/speech.yml @@ -63,6 +63,24 @@ - type: ReplacementAccent accent: italian +- type: trait + id: FrenchAccent + name: trait-french-name + description: trait-french-desc + category: SpeechTraits + cost: 1 + components: + - type: FrenchAccent + +- type: trait + id: SpanishAccent + name: trait-spanish-name + description: trait-spanish-desc + category: SpeechTraits + cost: 1 + components: + - type: SpanishAccent + - type: trait id: Liar name: trait-liar-name diff --git a/Resources/Prototypes/game_presets.yml b/Resources/Prototypes/game_presets.yml index c9ff0c6c05eb..db1111ff082d 100644 --- a/Resources/Prototypes/game_presets.yml +++ b/Resources/Prototypes/game_presets.yml @@ -53,7 +53,7 @@ - badidea - punishment name: aller-at-once-title - description: all-at-once-description + description: aller-at-once-description showInVote: false #Please god dont do this rules: - Nukeops diff --git a/Resources/ServerInfo/Guidebook/Engineering/PortableGenerator.xml b/Resources/ServerInfo/Guidebook/Engineering/PortableGenerator.xml index b946bf041cb7..ee637de51492 100644 --- a/Resources/ServerInfo/Guidebook/Engineering/PortableGenerator.xml +++ b/Resources/ServerInfo/Guidebook/Engineering/PortableGenerator.xml @@ -16,7 +16,7 @@ - The J.R.P.A.C.M.A.N. can be found across the station in maintenance shafts, and is ideal for crew to set up themselves whenever there are power issues. + The J.R.P.A.C.M.A.N. can be found across the station in maintenance shafts, and is ideal for crew to set up themselves whenever there are power issues. Its output of up to [color=orange][protodata="PortableGeneratorJrPacman" comp="FuelGenerator" member="MaxTargetPower" format="N0"/] W[/color] is enough to power a few important devices. Setup is incredibly easy: wrench it down above an [color=green]LV[/color] power cable, give it some welding fuel, and start it up. Welding fuel should be plentiful to find around the station. In a pinch, you can even transfer some from the big tanks with a soda can or water bottle. Just remember to empty the soda can first, I don't think it likes soda as fuel. @@ -35,7 +35,7 @@ The (S.U.P.E.R.)P.A.C.M.A.N. is intended for usage by engineering for advanced power scenarios. Bootstrapping larger engines, powering departments, and so on. - The S.U.P.E.R.P.A.C.M.A.N. boasts a larger power output and longer runtime at maximum output, but scales down to lower outputs less efficiently. + The S.U.P.E.R.P.A.C.M.A.N. boasts a larger power output (up to [color=orange][protodata="PortableGeneratorSuperPacman" comp="FuelGenerator" member="MaxTargetPower" format="N0"/] W[/color]) and longer runtime at maximum output, but scales down to lower outputs less efficiently. They connect directly to [color=yellow]MV[/color] or [color=orange]HV[/color] power cables, and are able to switch between them for flexibility. diff --git a/Resources/ServerInfo/Guidebook/Security/Defusal.xml b/Resources/ServerInfo/Guidebook/Security/Defusal.xml index 63e1b037d2c5..583b8a34c7b3 100644 --- a/Resources/ServerInfo/Guidebook/Security/Defusal.xml +++ b/Resources/ServerInfo/Guidebook/Security/Defusal.xml @@ -29,7 +29,7 @@ To arm a bomb, you can either [color=yellow]right click[/color] and click [color=yellow]Begin countdown[/click], or [color=yellow]alt-click[/color] the bomb. It will begin beeping. ## Time - A bomb has a limited time, at a minimum of 90 and a maximum of 300. You can view the timer by examining it, unless the Proceed wire is cut. Once the timer hits zero, the bomb will detonate. + A bomb has a limited time, at a minimum of [protodata="SyndicateBomb" comp="OnUseTimerTrigger" member="ShortestDelayOption"/] seconds and a maximum of [protodata="SyndicateBomb" comp="OnUseTimerTrigger" member="LongestDelayOption"/] seconds. You can view the timer by examining it, unless the Proceed wire is cut. Once the timer hits zero, the bomb will detonate. ## Bolts By default, once armed, a bomb will bolt itself to the ground. You must find the BOLT wire and cut it to disable the bolts, after which you can unwrench it and throw it into space. diff --git a/Resources/Textures/Clothing/Head/Hoods/voidcloak.rsi/equipped-HELMET.png b/Resources/Textures/Clothing/Head/Hoods/voidcloak.rsi/equipped-HELMET.png new file mode 100644 index 000000000000..ba4a4d8b7fcf Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hoods/voidcloak.rsi/equipped-HELMET.png differ diff --git a/Resources/Textures/Clothing/Head/Hoods/voidcloak.rsi/icon.png b/Resources/Textures/Clothing/Head/Hoods/voidcloak.rsi/icon.png new file mode 100644 index 000000000000..e955145ae71b Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hoods/voidcloak.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hoods/voidcloak.rsi/meta.json b/Resources/Textures/Clothing/Head/Hoods/voidcloak.rsi/meta.json new file mode 100644 index 000000000000..2a8e91145be5 --- /dev/null +++ b/Resources/Textures/Clothing/Head/Hoods/voidcloak.rsi/meta.json @@ -0,0 +1,18 @@ +{ + "version": 1, + "license": "CC-BY-SA-4.0", + "copyright": "Made by Dezzzix; Discord: dezzzix", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Mobs/Customization/gauze.rsi/gauze_head.png b/Resources/Textures/Mobs/Customization/gauze.rsi/gauze_head.png new file mode 100644 index 000000000000..713ae3d4bc5c Binary files /dev/null and b/Resources/Textures/Mobs/Customization/gauze.rsi/gauze_head.png differ diff --git a/Resources/Textures/Mobs/Customization/gauze.rsi/meta.json b/Resources/Textures/Mobs/Customization/gauze.rsi/meta.json index 8d82ccab517c..bd7d1ed4eb4d 100644 --- a/Resources/Textures/Mobs/Customization/gauze.rsi/meta.json +++ b/Resources/Textures/Mobs/Customization/gauze.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Gauze sprites by Github KittenColony / Discord kittencolony (297865728374210561)", + "copyright": "Gauze sprites by Github KittenColony / Discord kittencolony (297865728374210561), gauze_head by github:DreamlyJack(624946166152298517)", "size": { "x": 32, "y": 32 @@ -142,6 +142,10 @@ { "name": "gauze_moth_lowerleg_l", "directions": 4 + }, + { + "name": "gauze_head", + "directions": 4 } ] } \ No newline at end of file diff --git a/Resources/Textures/Mobs/Pets/corgi.rsi/meta.json b/Resources/Textures/Mobs/Pets/corgi.rsi/meta.json index 3a5409316490..0e36d32316f8 100644 --- a/Resources/Textures/Mobs/Pets/corgi.rsi/meta.json +++ b/Resources/Textures/Mobs/Pets/corgi.rsi/meta.json @@ -5,207 +5,60 @@ "y": 32 }, "license": "CC-BY-SA-3.0", - "copyright": "https://github.com/tgstation/tgstation/commit/53d1f1477d22a11a99c6c6924977cd431075761b , cerberus by Alekshhh", + "copyright": "https://github.com/tgstation/tgstation/commit/53d1f1477d22a11a99c6c6924977cd431075761b , cerberus by Alekshhh, real mouse by TheShuEd", "states": [ { "name": "corgi", - "directions": 4, - "delays": [ - [ - 1 - ], - [ - 1 - ], - [ - 1 - ], - [ - 1 - ] - ] + "directions": 4 }, { "name": "corgi_rest", - "directions": 4, - "delays": [ - [ - 1 - ], - [ - 1 - ], - [ - 1 - ], - [ - 1 - ] - ] + "directions": 4 }, { - "name": "corgi_dead", - "delays": [ - [ - 1 - ] - ] + "name": "corgi_dead" }, { "name": "ian", - "directions": 4, - "delays": [ - [ - 1 - ], - [ - 1 - ], - [ - 1 - ], - [ - 1 - ] - ] + "directions": 4 }, { - "name": "ian_dead", - "delays": [ - [ - 1 - ] - ] + "name": "ian_dead" }, { - "name": "corgi_deadcollar", - "delays": [ - [ - 1 - ] - ] + "name": "corgi_deadcollar" }, { - "name": "corgi_deadtag", - "delays": [ - [ - 1 - ] - ] + "name": "corgi_deadtag" }, { "name": "ian_shaved", - "directions": 4, - "delays": [ - [ - 1 - ], - [ - 1 - ], - [ - 1 - ], - [ - 1 - ] - ] + "directions": 4 }, { - "name": "ian_shaved_dead", - "delays": [ - [ - 1 - ] - ] + "name": "ian_shaved_dead" }, { "name": "corgicollar", - "directions": 4, - "delays": [ - [ - 1 - ], - [ - 1 - ], - [ - 1 - ], - [ - 1 - ] - ] + "directions": 4 }, { "name": "corgitag", - "directions": 4, - "delays": [ - [ - 1 - ], - [ - 1 - ], - [ - 1 - ], - [ - 1 - ] - ] + "directions": 4 }, { "name": "lisa", - "directions": 4, - "delays": [ - [ - 1 - ], - [ - 1 - ], - [ - 1 - ], - [ - 1 - ] - ] + "directions": 4 }, { - "name": "lisa_dead", - "delays": [ - [ - 1 - ] - ] + "name": "lisa_dead" }, { "name": "lisa_shaved", - "directions": 4, - "delays": [ - [ - 1 - ], - [ - 1 - ], - [ - 1 - ], - [ - 1 - ] - ] + "directions": 4 }, { - "name": "lisa_shaved_dead", - "delays": [ - [ - 1 - ] - ] + "name": "lisa_shaved_dead" }, { "name": "narsian", @@ -238,12 +91,7 @@ ] }, { - "name": "old_ian_dead", - "delays": [ - [ - 1 - ] - ] + "name": "old_ian_dead" }, { "name": "old_ian_shaved", @@ -268,116 +116,42 @@ ] }, { - "name": "old_ian_shaved_dead", - "delays": [ - [ - 1 - ] - ] + "name": "old_ian_shaved_dead" }, { "name": "puppy", - "directions": 4, - "delays": [ - [ - 1 - ], - [ - 1 - ], - [ - 1 - ], - [ - 1 - ] - ] + "directions": 4 }, { - "name": "puppy_dead", - "delays": [ - [ - 1 - ] - ] + "name": "puppy_dead" }, { - "name": "puppy_deadcollar", - "delays": [ - [ - 1 - ] - ] + "name": "puppy_deadcollar" }, { - "name": "puppy_deadtag", - "delays": [ - [ - 1 - ] - ] + "name": "puppy_deadtag" }, { "name": "puppy_shaved", - "directions": 4, - "delays": [ - [ - 1 - ], - [ - 1 - ], - [ - 1 - ], - [ - 1 - ] - ] + "directions": 4 }, { - "name": "puppy_shaved_dead", - "delays": [ - [ - 1 - ] - ] + "name": "puppy_shaved_dead" }, { "name": "puppycollar", - "directions": 4, - "delays": [ - [ - 1 - ], - [ - 1 - ], - [ - 1 - ], - [ - 1 - ] - ] + "directions": 4 }, { "name": "puppytag", - "directions": 4, - "delays": [ - [ - 1 - ], - [ - 1 - ], - [ - 1 - ], - [ - 1 - ] - ] + "directions": 4 + }, + { + "name": "real_mouse", + "directions": 4 + }, + { + "name": "real_mouse_dead" } ] } diff --git a/Resources/Textures/Mobs/Pets/corgi.rsi/real_mouse.png b/Resources/Textures/Mobs/Pets/corgi.rsi/real_mouse.png new file mode 100644 index 000000000000..2983063c4fd8 Binary files /dev/null and b/Resources/Textures/Mobs/Pets/corgi.rsi/real_mouse.png differ diff --git a/Resources/Textures/Mobs/Pets/corgi.rsi/real_mouse_dead.png b/Resources/Textures/Mobs/Pets/corgi.rsi/real_mouse_dead.png new file mode 100644 index 000000000000..4b51443c73bc Binary files /dev/null and b/Resources/Textures/Mobs/Pets/corgi.rsi/real_mouse_dead.png differ diff --git a/Resources/Textures/Objects/Consumable/Food/skewer.rsi/meta.json b/Resources/Textures/Objects/Consumable/Food/skewer.rsi/meta.json index f99e5d77d45c..d4e98e98bd84 100644 --- a/Resources/Textures/Objects/Consumable/Food/skewer.rsi/meta.json +++ b/Resources/Textures/Objects/Consumable/Food/skewer.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation and modified by Swept at https://github.com/tgstation/tgstation/commit/40d75cc340c63582fb66ce15bf75a36115f6bdaa", + "copyright": "Taken from tgstation and modified by Swept at https://github.com/tgstation/tgstation/commit/40d75cc340c63582fb66ce15bf75a36115f6bdaa, skewer-holymelon edited from skewer-watermelon by slarticodefast", "size": { "x": 32, "y": 32 @@ -45,6 +45,9 @@ }, { "name": "skewer-watermelon" + }, + { + "name": "skewer-holymelon" } ] } diff --git a/Resources/Textures/Objects/Consumable/Food/skewer.rsi/skewer-holymelon.png b/Resources/Textures/Objects/Consumable/Food/skewer.rsi/skewer-holymelon.png new file mode 100644 index 000000000000..8bc88a45b8c5 Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Food/skewer.rsi/skewer-holymelon.png differ diff --git a/Resources/Textures/Objects/Fun/toys.rsi/meta.json b/Resources/Textures/Objects/Fun/toys.rsi/meta.json index 2cab48b87844..a9aeff633586 100644 --- a/Resources/Textures/Objects/Fun/toys.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/toys.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432, orb, orb-inhand-left and orb-inhand-right created by Pancake, plushie_diona and plushie_diona1 created by discord user Deos#5630, toy-mouse-equipped-HELMET is a resprited 1-equipped-HELMET in mouse.rsi by PuroSlavKing (Github), plushie_xeno by LinkUyx#6557, plushie_hampter by RenLou#4333, beachball taken from https://github.com/ss220-space/Paradise/commit/662c08272acd7be79531550919f56f846726eabb, beachb-inhand by ;3#1161, bee hat and in-hand sprites drawn by Ubaser, plushie_penguin by netwy, plushie_arachnid by PixelTheKermit (github), plushie human by TheShuEd, NanoTrasen Balloon by MACMAN2003, holoplush and magicplush modified by deltanedas (github), finfin by starypaws. Lizard hat sprite made by cinder, minor by carousel", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432, orb, orb-inhand-left and orb-inhand-right created by Pancake, plushie_diona and plushie_diona1 created by discord user Deos#5630, toy-mouse-equipped-HELMET is a resprited 1-equipped-HELMET in mouse.rsi by PuroSlavKing (Github), plushie_xeno by LinkUyx#6557, plushie_hampter by RenLou#4333, beachball taken from https://github.com/ss220-space/Paradise/commit/662c08272acd7be79531550919f56f846726eabb, beachb-inhand by ;3#1161, bee hat and in-hand sprites drawn by Ubaser, plushie_penguin by netwy, plushie_arachnid by PixelTheKermit (github), plushie human by TheShuEd, NanoTrasen Balloon by MACMAN2003, holoplush and magicplush modified by deltanedas (github), finfin by starypaws. Lizard hat sprite made by cinder, rubber_chicken by xprospero, minor by carousel", "size": { "x": 32, "y": 32 @@ -374,6 +374,9 @@ "name": "beachb-inhand-right", "directions": 4 }, + { + "name": "rubber_chicken" + }, { "name": "yarn-noodles" }, diff --git a/Resources/Textures/Objects/Fun/toys.rsi/rubber_chicken.png b/Resources/Textures/Objects/Fun/toys.rsi/rubber_chicken.png new file mode 100644 index 000000000000..543a3fbafea5 Binary files /dev/null and b/Resources/Textures/Objects/Fun/toys.rsi/rubber_chicken.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/dead.png b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/dead.png new file mode 100644 index 000000000000..b79722cbe5c6 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/dead.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/harvest.png b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/harvest.png new file mode 100644 index 000000000000..8bbddd83d85a Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/harvest.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/meta.json b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/meta.json new file mode 100644 index 000000000000..c2fd092c025f --- /dev/null +++ b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/meta.json @@ -0,0 +1,68 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/commit/1dbcf389b0ec6b2c51b002df5fef8dd1519f8068 and https://github.com/tgstation/tgstation/commit/ead6d8d59753ef033efdfad17f337df268038ff3 and modified by slarticodefast", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "dead" + }, + { + "name": "harvest" + }, + { + "name": "produce", + "delays": [ + [ + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08 + ] + ] + }, + { + "name": "seed" + }, + { + "name": "stage-1" + }, + { + "name": "stage-2" + }, + { + "name": "stage-3" + }, + { + "name": "stage-4" + }, + { + "name": "stage-5" + }, + { + "name": "stage-6" + } + ] +} diff --git a/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/produce.png b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/produce.png new file mode 100644 index 000000000000..be5b16262e51 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/produce.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/seed.png b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/seed.png new file mode 100644 index 000000000000..dad99e5b3a51 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/seed.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-1.png b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-1.png new file mode 100644 index 000000000000..484b4726603e Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-1.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-2.png b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-2.png new file mode 100644 index 000000000000..cfae4038297a Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-2.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-3.png b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-3.png new file mode 100644 index 000000000000..e4e2ac9a3994 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-3.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-4.png b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-4.png new file mode 100644 index 000000000000..31054ca2c5ce Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-4.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-5.png b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-5.png new file mode 100644 index 000000000000..91014722a87c Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-5.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-6.png b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-6.png new file mode 100644 index 000000000000..96853e979036 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/extradimensional_orange.rsi/stage-6.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/dead.png b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/dead.png new file mode 100644 index 000000000000..a3896d57c122 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/dead.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/harvest.png b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/harvest.png new file mode 100644 index 000000000000..1a2a7d37478e Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/harvest.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/meta.json b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/meta.json new file mode 100644 index 000000000000..65cf434c0a61 --- /dev/null +++ b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/meta.json @@ -0,0 +1,52 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/commit/b459ea3fdee965bdc3e93e7983ad7fa610d05c12 and https://github.com/tgstation/tgstation/commit/ead6d8d59753ef033efdfad17f337df268038ff3 and modified by slarticodefast", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "dead" + }, + { + "name": "harvest" + }, + { + "name": "produce", + "delays": [ + [ + 0.3, + 0.3, + 0.3, + 0.3 + ] + ] + }, + { + "name": "seed" + }, + { + "name": "stage-1" + }, + { + "name": "stage-2" + }, + { + "name": "stage-3" + }, + { + "name": "stage-4" + }, + { + "name": "stage-5" + }, + { + "name": "stage-6" + }, + { + "name": "slice" + } + ] +} diff --git a/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/produce.png b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/produce.png new file mode 100644 index 000000000000..73e458d8323e Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/produce.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/seed.png b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/seed.png new file mode 100644 index 000000000000..66d13dc00c68 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/seed.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/slice.png b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/slice.png new file mode 100644 index 000000000000..9ef34d39ae2b Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/slice.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-1.png b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-1.png new file mode 100644 index 000000000000..f926a279dc34 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-1.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-2.png b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-2.png new file mode 100644 index 000000000000..4213fc322530 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-2.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-3.png b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-3.png new file mode 100644 index 000000000000..69b583f4e413 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-3.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-4.png b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-4.png new file mode 100644 index 000000000000..c496143bf673 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-4.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-5.png b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-5.png new file mode 100644 index 000000000000..5f9dc48217da Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-5.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-6.png b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-6.png new file mode 100644 index 000000000000..a7a3cb45531a Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/holymelon.rsi/stage-6.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/dead.png b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/dead.png new file mode 100644 index 000000000000..3a1e5735cd8f Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/dead.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/harvest.png b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/harvest.png new file mode 100644 index 000000000000..b0417c69c0ec Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/harvest.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/meta.json b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/meta.json new file mode 100644 index 000000000000..cb53758e1cc0 --- /dev/null +++ b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/meta.json @@ -0,0 +1,41 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/commit/1dbcf389b0ec6b2c51b002df5fef8dd1519f8068 and https://github.com/tgstation/tgstation/commit/ead6d8d59753ef033efdfad17f337df268038ff3 and modified by slarticodefast", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "dead" + }, + { + "name": "harvest" + }, + { + "name": "produce" + }, + { + "name": "seed" + }, + { + "name": "stage-1" + }, + { + "name": "stage-2" + }, + { + "name": "stage-3" + }, + { + "name": "stage-4" + }, + { + "name": "stage-5" + }, + { + "name": "stage-6" + } + ] +} diff --git a/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/produce.png b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/produce.png new file mode 100644 index 000000000000..e6ab15f164ce Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/produce.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/seed.png b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/seed.png new file mode 100644 index 000000000000..de775fe7ac9e Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/seed.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-1.png b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-1.png new file mode 100644 index 000000000000..efdf35bb125a Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-1.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-2.png b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-2.png new file mode 100644 index 000000000000..1c9d20af4cb0 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-2.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-3.png b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-3.png new file mode 100644 index 000000000000..bcec67f1e4d4 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-3.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-4.png b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-4.png new file mode 100644 index 000000000000..8d471502ab5c Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-4.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-5.png b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-5.png new file mode 100644 index 000000000000..bee9bd6b93d4 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-5.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-6.png b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-6.png new file mode 100644 index 000000000000..502f27714c1e Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/meatwheat.rsi/stage-6.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/watermelon.rsi/meta.json b/Resources/Textures/Objects/Specific/Hydroponics/watermelon.rsi/meta.json index 37bc00be888f..16f3df4df6f1 100644 --- a/Resources/Textures/Objects/Specific/Hydroponics/watermelon.rsi/meta.json +++ b/Resources/Textures/Objects/Specific/Hydroponics/watermelon.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/commit/b459ea3fdee965bdc3e93e7983ad7fa610d05c12", + "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/commit/b459ea3fdee965bdc3e93e7983ad7fa610d05c12 and https://github.com/tgstation/tgstation/commit/ead6d8d59753ef033efdfad17f337df268038ff3", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Objects/Specific/Hydroponics/watermelon.rsi/produce.png b/Resources/Textures/Objects/Specific/Hydroponics/watermelon.rsi/produce.png index 61e3fb4eaf83..655628bc8819 100644 Binary files a/Resources/Textures/Objects/Specific/Hydroponics/watermelon.rsi/produce.png and b/Resources/Textures/Objects/Specific/Hydroponics/watermelon.rsi/produce.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/dead.png b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/dead.png new file mode 100644 index 000000000000..00d4f1dfc2c3 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/dead.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/harvest.png b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/harvest.png new file mode 100644 index 000000000000..cbedf8561161 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/harvest.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/meta.json b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/meta.json new file mode 100644 index 000000000000..01be1e7dc4c9 --- /dev/null +++ b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/meta.json @@ -0,0 +1,32 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/ead6d8d59753ef033efdfad17f337df268038ff3 and modified by slarticodefast", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "dead" + }, + { + "name": "harvest" + }, + { + "name": "produce" + }, + { + "name": "seed" + }, + { + "name": "stage-1" + }, + { + "name": "stage-2" + }, + { + "name": "stage-3" + } + ] +} diff --git a/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/produce.png b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/produce.png new file mode 100644 index 000000000000..aee68431aabb Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/produce.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/seed.png b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/seed.png new file mode 100644 index 000000000000..fe7c87282098 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/seed.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/stage-1.png b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/stage-1.png new file mode 100644 index 000000000000..abdff1afc7ba Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/stage-1.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/stage-2.png b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/stage-2.png new file mode 100644 index 000000000000..ab44f97970d6 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/stage-2.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/stage-3.png b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/stage-3.png new file mode 100644 index 000000000000..6ab196981f4d Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/world_pea.rsi/stage-3.png differ diff --git a/RobustToolbox b/RobustToolbox index dbc4e80e6186..0f60ad9018f5 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit dbc4e80e6186dd71f7b3f0cbde72606c2e986a75 +Subproject commit 0f60ad9018f54f9b49da1810bbffa01e2c5975f7 diff --git a/Tools/actions_changelogs_since_last_run.py b/Tools/actions_changelogs_since_last_run.py index d46e98b88ebc..1190297b9f34 100755 --- a/Tools/actions_changelogs_since_last_run.py +++ b/Tools/actions_changelogs_since_last_run.py @@ -146,7 +146,7 @@ def send_to_discord(entries: Iterable[ChangelogEntry]) -> None: message = change['message'] url = entry.get("url") if url and url.strip(): - group_content.write(f"{emoji} [-]({url}) {message}\n") + group_content.write(f"{emoji} - {message} [PR]({url}) \n") else: group_content.write(f"{emoji} - {message}\n")