diff --git a/Content.Client/Guidebook/GuidebookDataSystem.cs b/Content.Client/Guidebook/GuidebookDataSystem.cs new file mode 100644 index 0000000000..f47ad6ef1b --- /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 0000000000..a725fd4e4b --- /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 37ce9c4280..2d4033390c 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 8b26860332..8c9b4ae2ee 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 03df383eeb..c97110b208 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 5a082485a5..a6a20958f5 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 f90731bfa7..b96eae44e9 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 0000000000..223895eb29 --- /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 0000000000..5f77a66e53 --- /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 0000000000..9592ff779d --- /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 0000000000..7ee6cd13d7 --- /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 0000000000..52b9da3a85 --- /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 0000000000..e67176ee16 --- /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 0000000000..65bd59daa3 --- /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 0000000000..bc1cc062a3 --- /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 42512cb1fd..a3f5627cdb 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 5ab2dc8931..9d58b82d69 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 ae4d0ca2b8..7a1b875756 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 0000000000..86a6344156 --- /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 4766b89172..e110a42483 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 b738d28b46..9da96a76f8 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 834453fb19..f283fe5d19 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 367b16a268..ae8215ac6a 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 691d024ecd..cdcdbc02e5 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 19d58438b3..f927e717a9 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 9fd824a3c4..a33bddcaa3 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 82a38f5f00..46d2cd69b9 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 f30cab253a..e544c1538d 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 d143c25fdb..1b773f1a5a 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 c7d5665464..e941e65c41 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 3bd540a304..e19f736f06 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 cd312797ce..542d8bb84c 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 5a90d4ea35..8bdc548e35 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 83c24016ce..7f6c39eafc 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 d190f685ed..da1d111f97 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 1f2716356c..17a65ef1c1 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 fc25781005..c65ba0e80e 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 b2fa7574f7..6577b1942a 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 5a6d45d9ec..c0c274207b 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 c4e6e787a4..983b8a31ee 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/Ghost/SharedGhostSystem.cs b/Content.Shared/Ghost/SharedGhostSystem.cs index 2410704304..d2e2cd668a 100644 --- a/Content.Shared/Ghost/SharedGhostSystem.cs +++ b/Content.Shared/Ghost/SharedGhostSystem.cs @@ -69,7 +69,6 @@ public void SetCanReturnToBody(GhostComponent component, bool value) public void SetColor(GhostComponent component, Color value) { component.color = value; - Dirty(component); } } diff --git a/Content.Shared/Guidebook/Events.cs b/Content.Shared/Guidebook/Events.cs new file mode 100644 index 0000000000..e43bf4392c --- /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 0000000000..703940ed1e --- /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 0000000000..2b83892b88 --- /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 32e090c137..2d7781bae5 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 5f35adb333..6392956d63 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 c41db21b01..472d56b1d6 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 4dffb51805..2e7c8054b3 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 a3448715e4..931d8a3532 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 d4cfc4fc0d..6aeb245e27 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 cdf97fb085..1cdb22a109 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 0000000000..d95cf7bf42 --- /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 0000000000..84e3b9256c --- /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 e7156b34c3..1ca1600e77 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 0000000000..9ed7ee05a5 --- /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 0000000000..d48ba33bba --- /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/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index aa075ce331..ed365591de 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -706,8 +706,8 @@ protected HashSet ArcRayCast(Vector2 position, Angle angle, Angle arc for (var a = 0; a < res.Count; a++) // Space Stories - start { - if (Transform(ignore).ChildEntities.Contains(res[a].HitEntity)) // Мне просто нужно, чтобы он не бил своих детей, но его удары проходили дальше. - continue; + // if (Transform(ignore).ChildEntities.Contains(res[a].HitEntity)) // Мне просто нужно, чтобы он не бил своих детей, но его удары проходили дальше. + // continue; resSet.Add(res[a].HitEntity); break; } // Space Stories - end diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index d4d6e9e6ab..c5b567f69e 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 c2bd0c01dc..4aab16b46a 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,178 +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 @@ -3910,3 +3736,181 @@ 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 diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index c6ec284dc8..1190feea71 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 301c589449..f54cecf714 100644 --- a/Resources/Locale/en-US/accent/accents.ftl +++ b/Resources/Locale/en-US/accent/accents.ftl @@ -124,3 +124,10 @@ accent-words-nymph-1 = Chirp! accent-words-nymph-2 = Churr... accent-words-nymph-3 = Cheep? accent-words-nymph-4 = Chrrup! + +# 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... \ No newline at end of file diff --git a/Resources/Locale/en-US/accent/skeleton.ftl b/Resources/Locale/en-US/accent/skeleton.ftl new file mode 100644 index 0000000000..9a839676c6 --- /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 0d022ed5a6..a757f9e0d1 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 f56a6c36b8..3e7cde8449 100644 --- a/Resources/Locale/en-US/flavors/flavor-profiles.ftl +++ b/Resources/Locale/en-US/flavors/flavor-profiles.ftl @@ -174,6 +174,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 diff --git a/Resources/Locale/en-US/forensics/forensics.ftl b/Resources/Locale/en-US/forensics/forensics.ftl index 712e8511bb..80eea069fa 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 4570613081..debcf5bc10 100644 --- a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl +++ b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl @@ -252,7 +252,8 @@ ghost-role-information-medical-description = You are a medical professional, but ghost-role-information-cargo-name = Cargo ghost-role-information-cargo-description = You are part of a logistics mission, but seem to have found yourself in a strange situation... -ghost-role-information-engineering-name = Engineering +ghost-role-information-engineering-name = Engineering + ghost-role-information-engineering-description = You are on an engineering job, but seem to have found yourself in a strange situation... ghost-role-information-science-name = Science @@ -306,3 +307,7 @@ 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 + diff --git a/Resources/Locale/en-US/guardian/guardian.ftl b/Resources/Locale/en-US/guardian/guardian.ftl index 9e0966630d..141646087d 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 f8bedc3195..7ed35a90ba 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 9d4f7ef0e0..7fac0387ed 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 0000000000..4749cd6379 --- /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 6434311f21..9d0919d102 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 766145093e..97dd7ffcc6 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 = tobaco @@ -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 92c3bb9d61..79b4914092 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 4722f4c56b..72bedb75a3 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 138d3c9914..c8d524ba1d 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 @@ -109,7 +113,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 @@ -118,8 +124,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 28192d9663..02be271cdf 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/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index 8e3b189216..7f4dcfe2ec 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -53,3 +53,9 @@ trait-german-desc = You seem to come from space Germany. trait-italian-name = Italian accent trait-italian-desc = Mamma mia! You seem to have lived in space Italy! + +trait-french-name = French accent +trait-french-desc = Your accent seems to have a certain «je ne sais quoi». + +trait-spanish-name = Spanish accent +trait-spanish-desc = Hola señor, donde esta la biblioteca. diff --git a/Resources/Maps/Test/dev_map.yml b/Resources/Maps/Test/dev_map.yml index ce735e7318..75511b1760 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 1d1efdcad0..f495de250f 100644 --- a/Resources/Prototypes/Accents/full_replacements.yml +++ b/Resources/Prototypes/Accents/full_replacements.yml @@ -174,3 +174,12 @@ - accent-words-nymph-2 - 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 diff --git a/Resources/Prototypes/Accents/word_replacements.yml b/Resources/Prototypes/Accents/word_replacements.yml index 067c8016b6..9678cb313e 100644 --- a/Resources/Prototypes/Accents/word_replacements.yml +++ b/Resources/Prototypes/Accents/word_replacements.yml @@ -587,3 +587,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 e2ce25de9d..58513d7db1 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/Entities/Clothing/Hands/gloves.yml b/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml index 2de84b45a2..16777cd690 100644 --- a/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml +++ b/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml @@ -255,7 +255,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 ea6a4ea41c..625ef7a056 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. @@ -258,7 +258,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. @@ -291,7 +291,7 @@ #Chitinous Helmet - type: entity - parent: ClothingHeadBase + parent: [ ClothingHeadBase, BaseMajorContraband] id: ClothingHeadHelmetLing name: chitinous helmet description: An all-consuming chitinous mass of armor. @@ -389,7 +389,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. @@ -403,7 +403,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. @@ -415,7 +415,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. @@ -500,4 +500,4 @@ - type: InstantAction useDelay: 1 itemIconStyle: BigItem - event: !type:ToggleActionEvent \ No newline at end of file + event: !type:ToggleActionEvent diff --git a/Resources/Prototypes/Entities/Clothing/Head/hoods.yml b/Resources/Prototypes/Entities/Clothing/Head/hoods.yml index 82cff81c22..1330d38f40 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 a8dcbf99ca..90af169572 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 555887b01d..02a8ec62b1 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 56da0c3718..2412dd9d5c 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 e1dd567893..36f0faa1df 100644 --- a/Resources/Prototypes/Entities/Effects/puddle.yml +++ b/Resources/Prototypes/Entities/Effects/puddle.yml @@ -126,11 +126,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/mobs.yml b/Resources/Prototypes/Entities/Markers/Spawners/mobs.yml index cb06b39998..52c2c32689 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: Possum Morty Spawner id: SpawnMobPossumMorty diff --git a/Resources/Prototypes/Entities/Markers/shuttle.yml b/Resources/Prototypes/Entities/Markers/shuttle.yml index 0e9117951c..66e2bc39b7 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 2e3c9ddf46..870a7cf7d7 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 629f868d7e..495f534553 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 @@ -1879,6 +1899,8 @@ states: Alive: Base: lizard + Critical: + Base: dead Dead: Base: dead - type: Butcherable @@ -1933,6 +1955,8 @@ states: Alive: Base: slug + Critical: + Base: dead Dead: Base: dead - type: Butcherable @@ -1985,6 +2009,8 @@ states: Alive: Base: frog + Critical: + Base: dead Dead: Base: dead - type: Butcherable @@ -2035,6 +2061,8 @@ states: Alive: Base: parrot + Critical: + Base: dead Dead: Base: dead - type: Butcherable @@ -2086,6 +2114,8 @@ states: Alive: Base: penguin + Critical: + Base: penguin_dead Dead: Base: penguin_dead - type: Butcherable @@ -2155,6 +2185,8 @@ states: Alive: Base: penguin + Critical: + Base: dead Dead: Base: dead - type: MeleeWeapon @@ -2271,6 +2303,8 @@ states: Alive: Base: tarantula + Critical: + Base: tarantula_dead Dead: Base: tarantula_dead - type: Butcherable @@ -2396,6 +2430,8 @@ states: Alive: Base: clown + Critical: + Base: dead Dead: Base: dead - type: MobThresholds @@ -2465,6 +2501,8 @@ states: Alive: Base: possum + Critical: + Base: possum_dead Dead: Base: possum_dead - type: Butcherable @@ -2500,6 +2538,8 @@ states: Alive: Base: possum_old + Critical: + Base: possum_dead_old Dead: Base: possum_dead_old @@ -2534,6 +2574,8 @@ states: Alive: Base: raccoon + Critical: + Base: raccoon_dead Dead: Base: raccoon_dead - type: Butcherable @@ -2592,6 +2634,8 @@ states: Alive: Base: fox + Critical: + Base: fox_dead Dead: Base: fox_dead - type: Butcherable @@ -2663,6 +2707,8 @@ states: Alive: Base: corgi + Critical: + Base: corgi_dead Dead: Base: corgi_dead - type: Butcherable @@ -2704,6 +2750,8 @@ states: Alive: Base: narsian + Critical: + Base: narsian_dead Dead: Base: narsian_dead - type: MeleeWeapon @@ -2805,6 +2853,8 @@ states: Alive: Base: cat + Critical: + Base: cat_dead Dead: Base: cat_dead - type: Speech @@ -2855,6 +2905,8 @@ states: Alive: Base: cat2 + Critical: + Base: cat2_dead Dead: Base: cat2_dead @@ -2872,6 +2924,8 @@ states: Alive: Base: syndicat + Critical: + Base: syndicat_dead Dead: Base: syndicat_dead - type: GhostRole @@ -2917,6 +2971,8 @@ states: Alive: Base: spacecat + Critical: + Base: spacecat_dead Dead: Base: spacecat_dead - type: Temperature @@ -2954,6 +3010,8 @@ states: Alive: Base: caracal_flop + Critical: + Base: caracal_dead Dead: Base: caracal_dead @@ -3024,6 +3082,8 @@ states: Alive: Base: sloth + Critical: + Base: sloth_dead Dead: Base: sloth_dead - type: Butcherable @@ -3075,6 +3135,8 @@ states: Alive: Base: ferret + Critical: + Base: ferret_dead Dead: Base: ferret_dead - type: Butcherable @@ -3281,6 +3343,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 9f2f951662..f10d03886a 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml @@ -79,6 +79,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: @@ -175,4 +220,4 @@ types: Blunt: 0.11 - type: StaticPrice - price: 400 + price: 400 \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml index e517526594..037ab1d7c0 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 bca4a11402..ad5cf05f9d 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -116,10 +116,10 @@ description: View a communications interface. components: - type: InstantAction - icon: Stories/Interface/AdminActions/communications.png # Stories-Resprite - iconOn: Stories/Interface/AdminActions/communications.png # Stories-Resprite + 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 @@ -131,7 +131,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 @@ -155,7 +155,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 @@ -167,7 +167,7 @@ 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 } - type: entity diff --git a/Resources/Prototypes/Entities/Mobs/Player/observer.yml b/Resources/Prototypes/Entities/Mobs/Player/observer.yml index 5ceac9e773..c02629c4d6 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 d7d0cd0e65..62dbf3ee10 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 0ad425c710..000f21db3f 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 @@ -796,8 +799,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 15af78e78f..42fd0967ff 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. @@ -652,6 +674,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 @@ -1970,6 +2033,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 @@ -2132,6 +2294,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 b7fbe36589..54616724fb 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 a1ac5eca40..6902205c2c 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 b4e336b37d..19eab391ac 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/Misc/books.yml b/Resources/Prototypes/Entities/Objects/Misc/books.yml index 2942407494..15ecd5d2a8 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 762204701c..77ddcf0d98 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 e5a96f26e3..b0a466f891 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 56692f13cd..6015a8bf01 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 @@ -562,6 +584,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 @@ -614,6 +646,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 ee3664e16c..5cd19f6f91 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 b4e8e77c51..66e380c8d4 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/Piping/Atmospherics/binary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml index 24a8cb58da..ba86ca65a0 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 07b29b7225..ef3546981f 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 db26942623..895b710a06 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 c500229507..3f536c871a 100644 --- a/Resources/Prototypes/Flavors/flavors.yml +++ b/Resources/Prototypes/Flavors/flavors.yml @@ -1093,7 +1093,7 @@ id: bluepumpkin flavorType: Complex description: flavor-complex-blue-pumpkin - + - type: flavor id: violets flavorType: Complex @@ -1114,6 +1114,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 @@ -1128,4 +1143,3 @@ id: compressed-meat flavorType: Complex description: flavor-complex-compressed-meat - \ No newline at end of file diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 0e0e380967..e6a5946756 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -19,6 +19,7 @@ - id: RandomSentience - id: SlimesSpawn - id: SolarFlare + - id: SnakeSpawn - id: SpiderClownSpawn - id: SpiderSpawn - id: VentClog @@ -35,36 +36,35 @@ - id: SleeperAgents - id: ZombieOutbreak - - type: entity id: BaseStationEvent 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 @@ -298,7 +298,7 @@ startAudio: path: /Audio/Announcements/power_off.ogg params: - volume: -4 + volume: -4 duration: 60 maxDuration: 120 - type: PowerGridCheckRule @@ -370,6 +370,27 @@ - id: MobAdultSlimesYellowAngry prob: 0.02 +- type: entity + id: SnakeSpawn + parent: BaseStationEventShortDelay + components: + - type: StationEvent + startAnnouncement: station-event-vent-creatures-start-announcement + startAudio: + path: /Audio/Announcements/attention.ogg + 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 @@ -459,6 +480,9 @@ max: 1 pickPlayer: false startingGear: SyndicateLoneOperativeGearFull + roleLoadout: + - RoleSurvivalNukie + components: - type: NukeOperative - type: RandomMetadata @@ -531,9 +555,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 1659290ff3..caf2ca7945 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -73,7 +73,7 @@ parent: BaseGameRule id: BaseNukeopsRule components: - - type: RandomMetadata #this generates the random operation name cuz it's cool. + - type: RandomMetadata #this generates the random operation name cuz it's cool. nameSegments: - operationPrefix - operationSuffix @@ -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 @@ -319,8 +319,8 @@ tableId: SpaceTrafficControlTable - type: entity - id: SpaceTrafficControlFriendlyEventScheduler - parent: BaseGameRule + id: SpaceTrafficControlFriendlyEventScheduler + parent: BaseGameRule components: - type: BasicStationEventScheduler minimumTimeUntilFirstEvent: 1200 # 20 mins 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 0000000000..50f6845ec3 --- /dev/null +++ b/Resources/Prototypes/Hydroponics/randomMutations.yml @@ -0,0 +1,178 @@ +- type: RandomPlantMutationList + id: RandomPlantMutations + mutations: + - name: Bioluminescent + baseOdds: 0.036 + effect: !type:Glow + - name: Sentient + baseOdds: 0.0072 + appliesToPlant: false # makes the botany tray sentient if true + 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 7407a6b75f..54c5c29d55 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,34 @@ packetPrototype: OrangeSeeds productPrototypes: - FoodOrange + mutationPrototypes: + - extradimensionalOrange + harvestRepeat: Repeat + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + idealLight: 8 + chemicals: + Nutriment: + Min: 1 + Max: 5 + PotencyDivisor: 20 + Vitamin: + Min: 1 + 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 @@ -266,6 +324,10 @@ potency: 10 idealLight: 8 chemicals: + Haloperidol: + Min: 1 + Max: 5 + PotencyDivisor: 20 Nutriment: Min: 1 Max: 5 @@ -719,7 +781,7 @@ DoctorsDelight: Min: 3 Max: 13 - PotencyDivisor: 10 + PotencyDivisor: 10 - type: seed id: corn @@ -1556,6 +1618,8 @@ packetPrototype: WatermelonSeeds productPrototypes: - FoodWatermelon + mutationPrototypes: + - holymelon lifespan: 55 maturation: 12 production: 3 @@ -1576,6 +1640,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 @@ -1690,6 +1783,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 c84caeb99d..10543e6ea7 100644 --- a/Resources/Prototypes/Loadouts/Miscellaneous/survival.yml +++ b/Resources/Prototypes/Loadouts/Miscellaneous/survival.yml @@ -188,6 +188,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 1f023fbd97..c632ec98c3 100644 --- a/Resources/Prototypes/Loadouts/loadout_groups.yml +++ b/Resources/Prototypes/Loadouts/loadout_groups.yml @@ -81,6 +81,14 @@ - LoadoutSpeciesEVANitrogen - LoadoutSpeciesEVAOxygen +- type: loadoutGroup + id: GroupPocketTankDouble + name: loadout-group-pocket-tank-double + hidden: true + loadouts: + - LoadoutSpeciesPocketDoubleNitrogen + - LoadoutSpeciesPocketDoubleOxygen + # Command - type: loadoutGroup id: CaptainHead @@ -664,7 +672,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 45c2d0e0ad..2b04f9a72d 100644 --- a/Resources/Prototypes/Loadouts/role_loadouts.yml +++ b/Resources/Prototypes/Loadouts/role_loadouts.yml @@ -570,6 +570,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 500b5f59bf..88a99451cf 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 23d80f0e79..e96d5be30c 100644 --- a/Resources/Prototypes/Reagents/botany.yml +++ b/Resources/Prototypes/Reagents/botany.yml @@ -238,8 +238,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 d843e7b985..43d6fe8852 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 @@ -276,7 +276,7 @@ tags: - Cooked - Meat - + # Snail meat - type: foodSequenceElement @@ -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 100cbe8f38..0db72f3663 100644 --- a/Resources/Prototypes/Recipes/Lathes/clothing.yml +++ b/Resources/Prototypes/Recipes/Lathes/clothing.yml @@ -1,1263 +1,964 @@ +# 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 - 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 diff --git a/Resources/Prototypes/Recipes/Lathes/electronics.yml b/Resources/Prototypes/Recipes/Lathes/electronics.yml index 67651dd373..818ba0fe37 100644 --- a/Resources/Prototypes/Recipes/Lathes/electronics.yml +++ b/Resources/Prototypes/Recipes/Lathes/electronics.yml @@ -166,6 +166,7 @@ - type: latheRecipe parent: BaseGoldCircuitboardRecipe id: ChemMasterMachineCircuitboard + result: ChemMasterMachineCircuitboard - type: latheRecipe parent: BaseGoldCircuitboardRecipe @@ -431,6 +432,7 @@ - type: latheRecipe parent: BaseCircuitboardRecipe id: MicrowaveMachineCircuitboard + result: MicrowaveMachineCircuitboard - type: latheRecipe parent: BaseCircuitboardRecipe @@ -540,6 +542,7 @@ - type: latheRecipe parent: BaseGoldCircuitboardRecipe id: MiniGravityGeneratorCircuitboard + result: MiniGravityGeneratorCircuitboard - type: latheRecipe parent: BaseCircuitboardRecipe diff --git a/Resources/Prototypes/Recipes/Lathes/robotics.yml b/Resources/Prototypes/Recipes/Lathes/robotics.yml index 44a9e2f0f2..bf8deba984 100644 --- a/Resources/Prototypes/Recipes/Lathes/robotics.yml +++ b/Resources/Prototypes/Recipes/Lathes/robotics.yml @@ -1,340 +1,260 @@ +# 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: 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 @@ -346,9 +266,9 @@ Gold: 200 - type: latheRecipe + parent: BaseRoboticsRecipe id: PositronicBrain result: PositronicBrain - category: Robotics completetime: 3 materials: Steel: 500 @@ -357,258 +277,141 @@ 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: BorgModuleService result: BorgModuleService - 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 diff --git a/Resources/Prototypes/Recipes/Lathes/security.yml b/Resources/Prototypes/Recipes/Lathes/security.yml index a54d5b6235..29227fb9db 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 bc643219f0..14f76e6dca 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 fffeaff39c..e35270d57d 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/Traits/speech.yml b/Resources/Prototypes/Traits/speech.yml index e5869a4afc..98d0368ed6 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 17e2e90309..2f72dcd143 100644 --- a/Resources/Prototypes/game_presets.yml +++ b/Resources/Prototypes/game_presets.yml @@ -52,7 +52,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 ac3c2bd671..72d057c5d0 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/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 0000000000..ba4a4d8b7f 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 0000000000..e955145ae7 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 0000000000..2a8e91145b --- /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 0000000000..713ae3d4bc 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 8d82ccab51..bd7d1ed4eb 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 3a54093164..0e36d32316 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 0000000000..2983063c4f 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 0000000000..4b51443c73 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 f99e5d77d4..d4e98e98bd 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 0000000000..8bc88a45b8 Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Food/skewer.rsi/skewer-holymelon.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 0000000000..b79722cbe5 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 0000000000..8bbddd83d8 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 0000000000..c2fd092c02 --- /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 0000000000..be5b16262e 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 0000000000..dad99e5b3a 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 0000000000..484b472660 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 0000000000..cfae403829 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 0000000000..e4e2ac9a39 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 0000000000..31054ca2c5 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 0000000000..91014722a8 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 0000000000..96853e9790 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 0000000000..a3896d57c1 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 0000000000..1a2a7d3747 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 0000000000..65cf434c0a --- /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 0000000000..73e458d832 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 0000000000..66d13dc00c 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 0000000000..9ef34d39ae 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 0000000000..f926a279dc 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 0000000000..4213fc3225 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 0000000000..69b583f4e4 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 0000000000..c496143bf6 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 0000000000..5f9dc48217 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 0000000000..a7a3cb4553 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 0000000000..3a1e5735cd 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 0000000000..b0417c69c0 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 0000000000..cb53758e1c --- /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 0000000000..e6ab15f164 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 0000000000..de775fe7ac 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 0000000000..efdf35bb12 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 0000000000..1c9d20af4c 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 0000000000..bcec67f1e4 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 0000000000..8d471502ab 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 0000000000..bee9bd6b93 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 0000000000..502f27714c 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 37bc00be88..16f3df4df6 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 61e3fb4eaf..655628bc88 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 0000000000..00d4f1dfc2 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 0000000000..cbedf85611 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 0000000000..01be1e7dc4 --- /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 0000000000..aee68431aa 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 0000000000..fe7c872820 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 0000000000..abdff1afc7 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 0000000000..ab44f97970 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 0000000000..6ab196981f 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 dbc4e80e61..0f60ad9018 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 0cb27ae741..89eca69b33 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")