diff --git a/Content.Client/Hands/Systems/HandsSystem.cs b/Content.Client/Hands/Systems/HandsSystem.cs index f87f75892e..0cefbbcfaa 100644 --- a/Content.Client/Hands/Systems/HandsSystem.cs +++ b/Content.Client/Hands/Systems/HandsSystem.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Numerics; using Content.Client.Animations; using Content.Client.Examine; @@ -63,6 +64,18 @@ namespace Content.Client.Hands.Systems return; var handsModified = component.Hands.Count != state.Hands.Count; + // we need to check that, even if we have the same amount, that the individual hands didn't change. + if (!handsModified) + { + foreach (var hand in component.Hands.Values) + { + if (state.Hands.Contains(hand)) + continue; + handsModified = true; + break; + } + } + var manager = EnsureComp(uid); if (handsModified) @@ -87,12 +100,13 @@ namespace Content.Client.Hands.Systems } } - foreach (var hand in addedHands) + component.SortedHands = new(state.HandNames); + var sorted = addedHands.OrderBy(hand => component.SortedHands.IndexOf(hand.Name)); + + foreach (var hand in sorted) { AddHand(uid, hand, component); } - - component.SortedHands = new(state.HandNames); } _stripSys.UpdateUi(uid); @@ -327,7 +341,7 @@ namespace Content.Client.Hands.Systems } var ev = new GetInhandVisualsEvent(uid, hand.Location); - RaiseLocalEvent(held, ev, false); + RaiseLocalEvent(held, ev); if (ev.Layers.Count == 0) { @@ -340,7 +354,7 @@ namespace Content.Client.Hands.Systems { if (!revealedLayers.Add(key)) { - Logger.Warning($"Duplicate key for in-hand visuals: {key}. Are multiple components attempting to modify the same layer? Entity: {ToPrettyString(held)}"); + Log.Warning($"Duplicate key for in-hand visuals: {key}. Are multiple components attempting to modify the same layer? Entity: {ToPrettyString(held)}"); continue; } diff --git a/Content.Client/Silicons/Borgs/BorgBoundUserInterface.cs b/Content.Client/Silicons/Borgs/BorgBoundUserInterface.cs new file mode 100644 index 0000000000..1d8bd06d9e --- /dev/null +++ b/Content.Client/Silicons/Borgs/BorgBoundUserInterface.cs @@ -0,0 +1,66 @@ +using Content.Shared.Silicons.Borgs; +using JetBrains.Annotations; +using Robust.Client.GameObjects; + +namespace Content.Client.Silicons.Borgs; + +[UsedImplicitly] +public sealed class BorgBoundUserInterface : BoundUserInterface +{ + [ViewVariables] + private BorgMenu? _menu; + + public BorgBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + var owner = Owner; + + _menu = new BorgMenu(owner); + + _menu.BrainButtonPressed += () => + { + SendMessage(new BorgEjectBrainBuiMessage()); + }; + + _menu.EjectBatteryButtonPressed += () => + { + SendMessage(new BorgEjectBatteryBuiMessage()); + }; + + _menu.NameChanged += name => + { + SendMessage(new BorgSetNameBuiMessage(name)); + }; + + _menu.RemoveModuleButtonPressed += module => + { + SendMessage(new BorgRemoveModuleBuiMessage(module)); + }; + + _menu.OnClose += Close; + + _menu.OpenCentered(); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (state is not BorgBuiState msg) + return; + _menu?.UpdateState(msg); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposing) + return; + _menu?.Dispose(); + } +} diff --git a/Content.Client/Silicons/Borgs/BorgMenu.xaml b/Content.Client/Silicons/Borgs/BorgMenu.xaml new file mode 100644 index 0000000000..7d8fd9fe57 --- /dev/null +++ b/Content.Client/Silicons/Borgs/BorgMenu.xaml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + diff --git a/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs b/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs new file mode 100644 index 0000000000..046d8e299f --- /dev/null +++ b/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs @@ -0,0 +1,165 @@ +using Content.Client.Stylesheets; +using Content.Client.UserInterface.Controls; +using Content.Shared.NameIdentifier; +using Content.Shared.Preferences; +using Content.Shared.Silicons.Borgs; +using Content.Shared.Silicons.Borgs.Components; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Timing; + +namespace Content.Client.Silicons.Borgs; + +[GenerateTypedNameReferences] +public sealed partial class BorgMenu : FancyWindow +{ + [Dependency] private readonly IEntityManager _entity = default!; + + public Action? BrainButtonPressed; + public Action? EjectBatteryButtonPressed; + public Action? NameChanged; + public Action? RemoveModuleButtonPressed; + + private readonly BorgChassisComponent? _chassis; + public readonly EntityUid Entity; + public float AccumulatedTime; + private string _lastValidName; + + public BorgMenu(EntityUid entity) + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + + Entity = entity; + + if (_entity.TryGetComponent(Entity, out var chassis)) + _chassis = chassis; + + BorgSprite.SetEntity(entity); + ChargeBar.MaxValue = 1f; + ChargeBar.Value = 1f; + + if (_entity.TryGetComponent(Entity, out var nameIdentifierComponent)) + { + NameIdentifierLabel.Visible = true; + NameIdentifierLabel.Text = nameIdentifierComponent.FullIdentifier; + + var fullName = _entity.GetComponent(Entity).EntityName; + var name = fullName.Substring(0, fullName.Length - nameIdentifierComponent.FullIdentifier.Length - 1); + NameLineEdit.Text = name; + } + else + { + NameIdentifierLabel.Visible = false; + NameLineEdit.Text = _entity.GetComponent(Entity).EntityName; + } + + _lastValidName = NameLineEdit.Text; + + EjectBatteryButton.OnPressed += _ => EjectBatteryButtonPressed?.Invoke(); + BrainButton.OnPressed += _ => BrainButtonPressed?.Invoke(); + + NameLineEdit.OnTextChanged += OnNameChanged; + NameLineEdit.OnTextEntered += OnNameEntered; + NameLineEdit.OnFocusExit += OnNameFocusExit; + + UpdateBrainButton(); + } + + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); + + AccumulatedTime += args.DeltaSeconds; + BorgSprite.OverrideDirection = (Direction) ((int) AccumulatedTime % 4 * 2); + } + + public void UpdateState(BorgBuiState state) + { + EjectBatteryButton.Disabled = !state.HasBattery; + ChargeBar.Value = state.ChargePercent; + ChargeLabel.Text = Loc.GetString("borg-ui-charge-label", + ("charge", (int) MathF.Round(state.ChargePercent * 100))); + + UpdateBrainButton(); + UpdateModulePanel(); + } + + private void UpdateBrainButton() + { + if (_chassis?.BrainEntity is { } brain) + { + BrainButton.Text = _entity.GetComponent(brain).EntityName; + BrainView.Visible = true; + BrainView.SetEntity(brain); + BrainButton.Disabled = false; + BrainButton.AddStyleClass(StyleBase.ButtonOpenLeft); + } + else + { + BrainButton.Text = Loc.GetString("borg-ui-no-brain"); + BrainButton.Disabled = true; + BrainView.Visible = false; + BrainButton.RemoveStyleClass(StyleBase.ButtonOpenLeft); + } + } + + private void UpdateModulePanel() + { + if (_chassis == null) + return; + + ModuleCounter.Text = Loc.GetString("borg-ui-module-counter", + ("actual", _chassis.ModuleCount), + ("max", _chassis.MaxModules)); + + ModuleContainer.Children.Clear(); + foreach (var module in _chassis.ModuleContainer.ContainedEntities) + { + var control = new BorgModuleControl(module, _entity); + control.RemoveButtonPressed += () => + { + RemoveModuleButtonPressed?.Invoke(module); + }; + ModuleContainer.AddChild(control); + } + } + + private void OnNameChanged(LineEdit.LineEditEventArgs obj) + { + if (obj.Text.Length == 0 || + string.IsNullOrWhiteSpace(obj.Text) || + string.IsNullOrEmpty(obj.Text)) + { + return; + } + + if (obj.Text.Length > HumanoidCharacterProfile.MaxNameLength) + { + obj.Control.Text = obj.Text.Substring(0, HumanoidCharacterProfile.MaxNameLength); + } + + _lastValidName = obj.Control.Text; + obj.Control.Text = _lastValidName; + } + + private void OnNameEntered(LineEdit.LineEditEventArgs obj) + { + NameChanged?.Invoke(_lastValidName); + } + + private void OnNameFocusExit(LineEdit.LineEditEventArgs obj) + { + if (obj.Text.Length > HumanoidCharacterProfile.MaxNameLength || + obj.Text.Length == 0 || + string.IsNullOrWhiteSpace(obj.Text) || + string.IsNullOrEmpty(obj.Text)) + { + obj.Control.Text = _lastValidName.Trim(); + } + + NameChanged?.Invoke(_lastValidName); + } +} + diff --git a/Content.Client/Silicons/Borgs/BorgModuleControl.xaml b/Content.Client/Silicons/Borgs/BorgModuleControl.xaml new file mode 100644 index 0000000000..7d89cc54b9 --- /dev/null +++ b/Content.Client/Silicons/Borgs/BorgModuleControl.xaml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/Content.Client/Silicons/Borgs/BorgModuleControl.xaml.cs b/Content.Client/Silicons/Borgs/BorgModuleControl.xaml.cs new file mode 100644 index 0000000000..d5cf05ba63 --- /dev/null +++ b/Content.Client/Silicons/Borgs/BorgModuleControl.xaml.cs @@ -0,0 +1,25 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.Silicons.Borgs; + +[GenerateTypedNameReferences] +public sealed partial class BorgModuleControl : PanelContainer +{ + public Action? RemoveButtonPressed; + + public BorgModuleControl(EntityUid entity, IEntityManager entityManager) + { + RobustXamlLoader.Load(this); + + ModuleView.SetEntity(entity); + ModuleName.Text = entityManager.GetComponent(entity).EntityName; + RemoveButton.TexturePath = "/Textures/Interface/Nano/cross.svg.png"; + RemoveButton.OnPressed += _ => + { + RemoveButtonPressed?.Invoke(); + }; + } +} + diff --git a/Content.Client/Silicons/Borgs/BorgSystem.cs b/Content.Client/Silicons/Borgs/BorgSystem.cs new file mode 100644 index 0000000000..5d2e5fa070 --- /dev/null +++ b/Content.Client/Silicons/Borgs/BorgSystem.cs @@ -0,0 +1,85 @@ +using Content.Shared.Silicons.Borgs; +using Content.Shared.Silicons.Borgs.Components; +using Robust.Client.GameObjects; +using Robust.Shared.Containers; + +namespace Content.Client.Silicons.Borgs; + +/// +public sealed class BorgSystem : SharedBorgSystem +{ + [Dependency] private readonly AppearanceSystem _appearance = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnBorgAppearanceChanged); + SubscribeLocalEvent(OnMMIAppearanceChanged); + } + + private void OnBorgAppearanceChanged(EntityUid uid, BorgChassisComponent component, ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + return; + UpdateBorgAppearnce(uid, component, args.Component, args.Sprite); + } + + protected override void OnInserted(EntityUid uid, BorgChassisComponent component, EntInsertedIntoContainerMessage args) + { + if (!component.Initialized) + return; + + base.OnInserted(uid, component, args); + UpdateBorgAppearnce(uid, component); + } + + protected override void OnRemoved(EntityUid uid, BorgChassisComponent component, EntRemovedFromContainerMessage args) + { + if (!component.Initialized) + return; + + base.OnRemoved(uid, component, args); + UpdateBorgAppearnce(uid, component); + } + + private void UpdateBorgAppearnce(EntityUid uid, + BorgChassisComponent? component = null, + AppearanceComponent? appearance = null, + SpriteComponent? sprite = null) + { + if (!Resolve(uid, ref component, ref appearance, ref sprite)) + return; + + if (!_appearance.TryGetData(uid, BorgVisuals.HasPlayer, out var hasPlayer, appearance)) + hasPlayer = false; + + sprite.LayerSetVisible(BorgVisualLayers.Light, component.BrainEntity != null || hasPlayer); + sprite.LayerSetState(BorgVisualLayers.Light, hasPlayer ? component.HasMindState : component.NoMindState); + } + + private void OnMMIAppearanceChanged(EntityUid uid, MMIComponent component, ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + return; + var sprite = args.Sprite; + + if (!_appearance.TryGetData(uid, MMIVisuals.BrainPresent, out bool brain)) + brain = false; + if (!_appearance.TryGetData(uid, MMIVisuals.HasMind, out bool hasMind)) + hasMind = false; + + sprite.LayerSetVisible(MMIVisualLayers.Brain, brain); + if (!brain) + { + sprite.LayerSetState(MMIVisualLayers.Base, component.NoBrainState); + } + else + { + var state = hasMind + ? component.HasMindState + : component.NoMindState; + sprite.LayerSetState(MMIVisualLayers.Base, state); + } + } +} diff --git a/Content.Client/Silicons/Laws/SiliconLawSystem.cs b/Content.Client/Silicons/Laws/SiliconLawSystem.cs new file mode 100644 index 0000000000..f7f2fe7c47 --- /dev/null +++ b/Content.Client/Silicons/Laws/SiliconLawSystem.cs @@ -0,0 +1,9 @@ +using Content.Shared.Silicons.Laws; + +namespace Content.Client.Silicons.Laws; + +/// +public sealed class SiliconLawSystem : SharedSiliconLawSystem +{ + +} diff --git a/Content.Client/Silicons/Laws/Ui/LawDisplay.xaml b/Content.Client/Silicons/Laws/Ui/LawDisplay.xaml new file mode 100644 index 0000000000..714d9e0fe7 --- /dev/null +++ b/Content.Client/Silicons/Laws/Ui/LawDisplay.xaml @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/Content.Client/Silicons/Laws/Ui/LawDisplay.xaml.cs b/Content.Client/Silicons/Laws/Ui/LawDisplay.xaml.cs new file mode 100644 index 0000000000..c48fcef619 --- /dev/null +++ b/Content.Client/Silicons/Laws/Ui/LawDisplay.xaml.cs @@ -0,0 +1,21 @@ +using Content.Shared.Silicons.Laws; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.Silicons.Laws.Ui; + +[GenerateTypedNameReferences] +public sealed partial class LawDisplay : Control +{ + public LawDisplay(SiliconLaw prototype) + { + RobustXamlLoader.Load(this); + + var identifier = prototype.LawIdentifierOverride ?? $"{prototype.Order}"; + + LawNumberLabel.Text = Loc.GetString("laws-ui-law-header", ("id", identifier)); + LawLabel.SetMessage(Loc.GetString(prototype.LawString)); + } +} + diff --git a/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs b/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs new file mode 100644 index 0000000000..78231c24df --- /dev/null +++ b/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs @@ -0,0 +1,45 @@ +using Content.Shared.Silicons.Laws.Components; +using JetBrains.Annotations; +using Robust.Client.GameObjects; + +namespace Content.Client.Silicons.Laws.Ui; + +[UsedImplicitly] +public sealed class SiliconLawBoundUserInterface : BoundUserInterface +{ + [ViewVariables] + private SiliconLawMenu? _menu; + + public SiliconLawBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + + } + + protected override void Open() + { + base.Open(); + + _menu = new(); + + _menu.OnClose += Close; + _menu.OpenCentered(); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposing) + return; + _menu?.Close(); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (state is not SiliconLawBuiState msg) + return; + + _menu?.Update(msg); + } +} diff --git a/Content.Client/Silicons/Laws/Ui/SiliconLawMenu.xaml b/Content.Client/Silicons/Laws/Ui/SiliconLawMenu.xaml new file mode 100644 index 0000000000..1b6324dae6 --- /dev/null +++ b/Content.Client/Silicons/Laws/Ui/SiliconLawMenu.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + diff --git a/Content.Client/Silicons/Laws/Ui/SiliconLawMenu.xaml.cs b/Content.Client/Silicons/Laws/Ui/SiliconLawMenu.xaml.cs new file mode 100644 index 0000000000..457be358e0 --- /dev/null +++ b/Content.Client/Silicons/Laws/Ui/SiliconLawMenu.xaml.cs @@ -0,0 +1,31 @@ +using Content.Client.UserInterface.Controls; +using Content.Shared.Silicons.Laws.Components; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Prototypes; + +namespace Content.Client.Silicons.Laws.Ui; + +[GenerateTypedNameReferences] +public sealed partial class SiliconLawMenu : FancyWindow +{ + [Dependency] private readonly IPrototypeManager _prototype = default!; + + public SiliconLawMenu() + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + } + + public void Update(SiliconLawBuiState state) + { + state.Laws.Sort(); + foreach (var law in state.Laws) + { + var control = new LawDisplay(law); + + LawDisplayContainer.AddChild(control); + } + } +} + diff --git a/Content.Client/Stack/StackSystem.cs b/Content.Client/Stack/StackSystem.cs index 66f5fbe985..a5d7470b57 100644 --- a/Content.Client/Stack/StackSystem.cs +++ b/Content.Client/Stack/StackSystem.cs @@ -1,3 +1,4 @@ +using System.Linq; using Content.Client.Items; using Content.Client.Storage.Systems; using Content.Shared.Stacks; @@ -31,8 +32,22 @@ namespace Content.Client.Stack base.SetCount(uid, amount, component); + if (component.Lingering && + TryComp(uid, out var sprite)) + { + // tint the stack gray and make it transparent if it's lingering. + var color = component.Count == 0 && component.Lingering + ? Color.DarkGray.WithAlpha(0.65f) + : Color.White; + + for (var i = 0; i < sprite.AllLayers.Count(); i++) + { + sprite.LayerSetColor(i, color); + } + } + // TODO PREDICT ENTITY DELETION: This should really just be a normal entity deletion call. - if (component.Count <= 0) + if (component.Count <= 0 && !component.Lingering) { Xform.DetachParentToNull(uid, Transform(uid)); return; diff --git a/Content.Client/Stylesheets/StyleNano.cs b/Content.Client/Stylesheets/StyleNano.cs index cb90e5c481..8aefec03e5 100644 --- a/Content.Client/Stylesheets/StyleNano.cs +++ b/Content.Client/Stylesheets/StyleNano.cs @@ -1332,6 +1332,10 @@ namespace Content.Client.Stylesheets .Prop("panel", new StyleBoxTexture(BaseButtonOpenBoth) { Padding = default }) .Prop(Control.StylePropertyModulateSelf, Color.FromHex("#1F1F23")), + Element().Class("PanelBackgroundLight") + .Prop("panel", new StyleBoxTexture(BaseButtonOpenBoth) { Padding = default }) + .Prop(Control.StylePropertyModulateSelf, Color.FromHex("#2F2F3B")), + // Window Footer Element().Class("NTLogoDark") .Prop(TextureRect.StylePropertyTexture, resCache.GetTexture("/Textures/Interface/Nano/ntlogo.svg.png")) diff --git a/Content.Server/Armor/ArmorSystem.cs b/Content.Server/Armor/ArmorSystem.cs index 931ca0c25b..dc02b06667 100644 --- a/Content.Server/Armor/ArmorSystem.cs +++ b/Content.Server/Armor/ArmorSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Cargo.Systems; using Robust.Shared.Prototypes; using Content.Shared.Damage.Prototypes; using Content.Shared.Inventory; +using Content.Shared.Silicons.Borgs; namespace Content.Server.Armor { @@ -22,6 +23,7 @@ namespace Content.Server.Armor base.Initialize(); SubscribeLocalEvent>(OnDamageModify); + SubscribeLocalEvent>(OnBorgDamageModify); SubscribeLocalEvent>(OnArmorVerbExamine); SubscribeLocalEvent(GetArmorPrice); } @@ -67,6 +69,11 @@ namespace Content.Server.Armor args.Args.Damage = DamageSpecifier.ApplyModifierSet(args.Args.Damage, component.Modifiers); } + private void OnBorgDamageModify(EntityUid uid, ArmorComponent component, ref BorgModuleRelayedEvent args) + { + args.Args.Damage = DamageSpecifier.ApplyModifierSet(args.Args.Damage, component.Modifiers); + } + private void OnArmorVerbExamine(EntityUid uid, ArmorComponent component, GetVerbsEvent args) { if (!args.CanInteract || !args.CanAccess) diff --git a/Content.Server/Body/Systems/BrainSystem.cs b/Content.Server/Body/Systems/BrainSystem.cs index 0a8dd5dc6a..ce3dadc628 100644 --- a/Content.Server/Body/Systems/BrainSystem.cs +++ b/Content.Server/Body/Systems/BrainSystem.cs @@ -40,12 +40,12 @@ namespace Content.Server.Body.Systems private void HandleMind(EntityUid newEntity, EntityUid oldEntity) { - EntityManager.EnsureComponent(newEntity); - var oldMind = EntityManager.EnsureComponent(oldEntity); + EnsureComp(newEntity); + var oldMind = EnsureComp(oldEntity); - EnsureComp(newEntity); + var ghostOnMove = EnsureComp(newEntity); if (HasComp(newEntity)) - Comp(newEntity).MustBeDead = true; + ghostOnMove.MustBeDead = true; // TODO: This is an awful solution. // Our greatest minds still can't figure out how to allow brains/heads to ghost without giving them the diff --git a/Content.Server/Construction/ConstructionSystem.Graph.cs b/Content.Server/Construction/ConstructionSystem.Graph.cs index 49dd9dd359..43b7b009a5 100644 --- a/Content.Server/Construction/ConstructionSystem.Graph.cs +++ b/Content.Server/Construction/ConstructionSystem.Graph.cs @@ -357,8 +357,12 @@ namespace Content.Server.Construction if (!_container.TryGetContainer(uid, container, out var ourContainer, containerManager)) continue; - // NOTE: Only Container is supported by Construction! - var otherContainer = _container.EnsureContainer(newUid, container, newContainerManager); + if (!_container.TryGetContainer(newUid, container, out var otherContainer, newContainerManager)) + { + // NOTE: Only Container is supported by Construction! + // todo: one day, the ensured container should be the same type as ourContainer + otherContainer = _container.EnsureContainer(newUid, container, newContainerManager); + } for (var i = ourContainer.ContainedEntities.Count - 1; i >= 0; i--) { diff --git a/Content.Server/Construction/ConstructionSystem.Interactions.cs b/Content.Server/Construction/ConstructionSystem.Interactions.cs index dee013bf71..3336736fac 100644 --- a/Content.Server/Construction/ConstructionSystem.Interactions.cs +++ b/Content.Server/Construction/ConstructionSystem.Interactions.cs @@ -4,6 +4,7 @@ using Content.Server.Construction.Components; using Content.Server.Temperature.Components; using Content.Server.Temperature.Systems; using Content.Shared.Construction; +using Content.Shared.Construction.Components; using Content.Shared.Construction.EntitySystems; using Content.Shared.Construction.Steps; using Content.Shared.DoAfter; @@ -39,6 +40,7 @@ namespace Content.Server.Construction new []{typeof(AnchorableSystem)}, new []{typeof(EncryptionKeySystem)}); SubscribeLocalEvent(EnqueueEvent); + SubscribeLocalEvent(EnqueueEvent); } /// @@ -388,6 +390,16 @@ namespace Content.Server.Construction } + case PartAssemblyConstructionGraphStep partAssemblyStep: + { + if (ev is not PartAssemblyPartInsertedEvent) + break; + + if (partAssemblyStep.Condition(uid, EntityManager)) + return HandleResult.True; + return HandleResult.False; + } + #endregion // --- CONSTRUCTION STEP EVENT HANDLING FINISH --- diff --git a/Content.Server/Destructible/Thresholds/Behaviors/EmptyContainersBehaviour.cs b/Content.Server/Destructible/Thresholds/Behaviors/EmptyContainersBehaviour.cs new file mode 100644 index 0000000000..abd5c531a6 --- /dev/null +++ b/Content.Server/Destructible/Thresholds/Behaviors/EmptyContainersBehaviour.cs @@ -0,0 +1,40 @@ +using Content.Shared.Random.Helpers; +using Robust.Server.Containers; +using Robust.Shared.Containers; + +namespace Content.Server.Destructible.Thresholds.Behaviors +{ + /// + /// Drop all items from specified containers + /// + [DataDefinition] + public sealed class EmptyContainersBehaviour : IThresholdBehavior + { + [DataField("containers")] + public List Containers = new(); + + [DataField("randomOffset")] + public float RandomOffset = 0.25f; + + public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null) + { + if (!system.EntityManager.TryGetComponent(owner, out var containerManager)) + return; + + var containerSys = system.EntityManager.System(); + + + foreach (var containerId in Containers) + { + if (!containerSys.TryGetContainer(owner, containerId, out var container, containerManager)) + continue; + + var entities = containerSys.EmptyContainer(container, true); + foreach (var ent in entities) + { + ent.RandomOffset(RandomOffset); + } + } + } + } +} diff --git a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs index 1b1ce65948..592f30adee 100644 --- a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs @@ -274,7 +274,7 @@ public sealed class ZombieRuleSystem : GameRuleSystem var prefList = new List(); foreach (var player in allPlayers) { - if (player.AttachedEntity == null || !HasComp(player.AttachedEntity)) + if (player.AttachedEntity == null || !HasComp(player.AttachedEntity) || HasComp(player.AttachedEntity)) continue; playerList.Add(player); diff --git a/Content.Server/Ghost/Roles/Components/ToggleableGhostRoleComponent.cs b/Content.Server/Ghost/Roles/Components/ToggleableGhostRoleComponent.cs new file mode 100644 index 0000000000..e00587d069 --- /dev/null +++ b/Content.Server/Ghost/Roles/Components/ToggleableGhostRoleComponent.cs @@ -0,0 +1,38 @@ +namespace Content.Server.Ghost.Roles.Components; + +/// +/// This is used for a ghost role which can be toggled on and off at will, like a PAI. +/// +[RegisterComponent] +public sealed class ToggleableGhostRoleComponent : Component +{ + [DataField("examineTextMindPresent")] + public string ExamineTextMindPresent = string.Empty; + + [DataField("examineTextMindSearching")] + public string ExamineTextMindSearching = string.Empty; + + [DataField("examineTextNoMind")] + public string ExamineTextNoMind = string.Empty; + + [DataField("beginSearchingText")] + public string BeginSearchingText = string.Empty; + + [DataField("roleName")] + public string RoleName = string.Empty; + + [DataField("roleDescription")] + public string RoleDescription = string.Empty; + + [DataField("wipeVerbText")] + public string WipeVerbText = string.Empty; + + [DataField("wipeVerbPopup")] + public string WipeVerbPopup = string.Empty; + + [DataField("stopSearchVerbText")] + public string StopSearchVerbText = string.Empty; + + [DataField("stopSearchVerbPopup")] + public string StopSearchVerbPopup = string.Empty; +} diff --git a/Content.Server/Ghost/Roles/ToggleableGhostRoleSystem.cs b/Content.Server/Ghost/Roles/ToggleableGhostRoleSystem.cs new file mode 100644 index 0000000000..e2fef7d90b --- /dev/null +++ b/Content.Server/Ghost/Roles/ToggleableGhostRoleSystem.cs @@ -0,0 +1,140 @@ +using Content.Server.Ghost.Roles.Components; +using Content.Server.Mind.Components; +using Content.Server.PAI; +using Content.Shared.Examine; +using Content.Shared.Interaction.Events; +using Content.Shared.Mind; +using Content.Shared.Popups; +using Content.Shared.Verbs; + +namespace Content.Server.Ghost.Roles; + +/// +/// This handles logic and interaction related to +/// +public sealed class ToggleableGhostRoleSystem : EntitySystem +{ + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + //todo this really shouldn't be in here but this system was converted from PAIs + [Dependency] private readonly PAISystem _pai = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnUseInHand); + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnMindAdded); + SubscribeLocalEvent(OnMindRemoved); + SubscribeLocalEvent>(AddWipeVerb); + } + + private void OnUseInHand(EntityUid uid, ToggleableGhostRoleComponent component, UseInHandEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + + // check if a mind is present + if (TryComp(uid, out var mind) && mind.HasMind) + { + _popup.PopupEntity(Loc.GetString(component.ExamineTextMindPresent), uid, args.User, PopupType.Large); + return; + } + if (HasComp(uid)) + { + _popup.PopupEntity(Loc.GetString(component.ExamineTextMindSearching), uid, args.User); + return; + } + _popup.PopupEntity(Loc.GetString(component.BeginSearchingText), uid, args.User); + + UpdateAppearance(uid, ToggleableGhostRoleStatus.Searching); + + var ghostRole = EnsureComp(uid); + EnsureComp(uid); + ghostRole.RoleName = Loc.GetString(component.RoleName); + ghostRole.RoleDescription = Loc.GetString(component.RoleDescription); + } + + private void OnExamined(EntityUid uid, ToggleableGhostRoleComponent component, ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + if (TryComp(uid, out var mind) && mind.HasMind) + { + args.PushMarkup(Loc.GetString(component.ExamineTextMindPresent)); + } + else if (HasComp(uid)) + { + args.PushMarkup(Loc.GetString(component.ExamineTextMindSearching)); + } + else + { + args.PushMarkup(Loc.GetString(component.ExamineTextNoMind)); + } + } + + private void OnMindAdded(EntityUid uid, ToggleableGhostRoleComponent pai, MindAddedMessage args) + { + // Mind was added, shutdown the ghost role stuff so it won't get in the way + RemComp(uid); + UpdateAppearance(uid, ToggleableGhostRoleStatus.On); + } + + private void OnMindRemoved(EntityUid uid, ToggleableGhostRoleComponent component, MindRemovedMessage args) + { + UpdateAppearance(uid, ToggleableGhostRoleStatus.Off); + } + + private void UpdateAppearance(EntityUid uid, ToggleableGhostRoleStatus status) + { + _appearance.SetData(uid, ToggleableGhostRoleVisuals.Status, status); + } + + private void AddWipeVerb(EntityUid uid, ToggleableGhostRoleComponent component, GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + if (TryComp(uid, out var mind) && mind.HasMind) + { + ActivationVerb verb = new() + { + Text = Loc.GetString(component.WipeVerbText), + Act = () => + { + if (component.Deleted || !HasComp(uid)) + return; + // Wiping device :( + // The shutdown of the Mind should cause automatic reset of the pAI during OnMindRemoved + // EDIT: But it doesn't!!!! Wtf? Do stuff manually + RemComp(uid); + _popup.PopupEntity(Loc.GetString(component.WipeVerbPopup), uid, args.User, PopupType.Large); + UpdateAppearance(uid, ToggleableGhostRoleStatus.Off); + _pai.PAITurningOff(uid); + } + }; + args.Verbs.Add(verb); + } + else if (HasComp(uid)) + { + ActivationVerb verb = new() + { + Text = Loc.GetString(component.StopSearchVerbText), + Act = () => + { + if (component.Deleted || !HasComp(uid)) + return; + RemComp(uid); + RemComp(uid); + _popup.PopupEntity(Loc.GetString(component.StopSearchVerbPopup), uid, args.User); + UpdateAppearance(uid, ToggleableGhostRoleStatus.Off); + _pai.PAITurningOff(uid); + } + }; + args.Verbs.Add(verb); + } + } +} diff --git a/Content.Server/Light/EntitySystems/HandheldLightSystem.cs b/Content.Server/Light/EntitySystems/HandheldLightSystem.cs index 0902602953..2c23057e2d 100644 --- a/Content.Server/Light/EntitySystems/HandheldLightSystem.cs +++ b/Content.Server/Light/EntitySystems/HandheldLightSystem.cs @@ -23,6 +23,7 @@ namespace Content.Server.Light.EntitySystems [UsedImplicitly] public sealed class HandheldLightSystem : SharedHandheldLightSystem { + [Dependency] private readonly ActionsSystem _actions = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PowerCellSystem _powerCell = default!; [Dependency] private readonly IPrototypeManager _proto = default!; @@ -40,6 +41,9 @@ namespace Content.Server.Light.EntitySystems SubscribeLocalEvent(OnRemove); SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnExamine); SubscribeLocalEvent>(AddToggleLightVerb); @@ -100,6 +104,24 @@ namespace Content.Server.Light.EntitySystems args.State = new HandheldLightComponent.HandheldLightComponentState(component.Activated, GetLevel(uid, component)); } + private void OnMapInit(EntityUid uid, HandheldLightComponent component, MapInitEvent args) + { + if (component.ToggleAction == null + && _proto.TryIndex(component.ToggleActionId, out InstantActionPrototype? act)) + { + component.ToggleAction = new(act); + } + + if (component.ToggleAction != null) + _actions.AddAction(uid, component.ToggleAction, null); + } + + private void OnShutdown(EntityUid uid, HandheldLightComponent component, ComponentShutdown args) + { + if (component.ToggleAction != null) + _actions.RemoveAction(uid, component.ToggleAction); + } + private byte? GetLevel(EntityUid uid, HandheldLightComponent component) { // Curently every single flashlight has the same number of levels for status and that's all it uses the charge for @@ -121,7 +143,7 @@ namespace Content.Server.Light.EntitySystems private void OnActivate(EntityUid uid, HandheldLightComponent component, ActivateInWorldEvent args) { - if (args.Handled) + if (args.Handled || !component.ToggleOnInteract) return; if (ToggleStatus(args.User, uid, component)) @@ -176,7 +198,7 @@ namespace Content.Server.Light.EntitySystems private void AddToggleLightVerb(EntityUid uid, HandheldLightComponent component, GetVerbsEvent args) { - if (!args.CanAccess || !args.CanInteract) + if (!args.CanAccess || !args.CanInteract || !component.ToggleOnInteract) return; ActivationVerb verb = new() diff --git a/Content.Server/Medical/HealingSystem.cs b/Content.Server/Medical/HealingSystem.cs index 6263749a5b..9dbc404892 100644 --- a/Content.Server/Medical/HealingSystem.cs +++ b/Content.Server/Medical/HealingSystem.cs @@ -86,6 +86,9 @@ public sealed class HealingSystem : EntitySystem // Re-verify that we can heal the damage. _stacks.Use(args.Used.Value, 1); + if (_stacks.GetCount(args.Used.Value) <= 0) + dontRepeat = true; + if (uid != args.User) { _adminLogger.Add(LogType.Healed, @@ -166,11 +169,8 @@ public sealed class HealingSystem : EntitySystem return false; } - if (component.HealingBeginSound != null) - { - _audio.PlayPvs(component.HealingBeginSound, uid, + _audio.PlayPvs(component.HealingBeginSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f)); - } var isNotSelf = user != target; diff --git a/Content.Server/Mind/MindSystem.cs b/Content.Server/Mind/MindSystem.cs index 6220637ea6..da004d40cc 100644 --- a/Content.Server/Mind/MindSystem.cs +++ b/Content.Server/Mind/MindSystem.cs @@ -27,11 +27,11 @@ public sealed class MindSystem : EntitySystem { [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly ActorSystem _actor = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly GhostSystem _ghostSystem = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly ActorSystem _actor = default!; // This is dictionary is required to track the minds of disconnected players that may have had their entity deleted. private readonly Dictionary _userMinds = new(); @@ -128,8 +128,8 @@ public sealed class MindSystem : EntitySystem if (!Resolve(uid, ref mind, false)) return; - RaiseLocalEvent(uid, new MindRemovedMessage(), true); mind.Mind = null; + RaiseLocalEvent(uid, new MindRemovedMessage(), true); } private void OnVisitingTerminating(EntityUid uid, VisitingMindComponent component, ref EntityTerminatingEvent args) @@ -414,10 +414,6 @@ public sealed class MindSystem : EntitySystem InternalEjectMind(oldEntity.Value, oldComp); SetOwnedEntity(mind, entity, component); - if (mind.OwnedComponent != null){ - InternalAssignMind(mind.OwnedEntity!.Value, mind, mind.OwnedComponent); - mind.OriginalOwnedEntity ??= mind.OwnedEntity; - } // Don't do the full deletion cleanup if we're transferring to our VisitingEntity if (alreadyAttached) @@ -438,9 +434,15 @@ public sealed class MindSystem : EntitySystem // Player is CURRENTLY connected. if (mind.Session != null && !alreadyAttached && mind.VisitingEntity == null) { - mind.Session.AttachToEntity(entity); + _actor.Attach(entity, mind.Session, true); Log.Info($"Session {mind.Session.Name} transferred to entity {entity}."); } + + if (mind.OwnedComponent != null) + { + InternalAssignMind(mind.OwnedEntity!.Value, mind, mind.OwnedComponent); + mind.OriginalOwnedEntity ??= mind.OwnedEntity; + } } /// @@ -559,7 +561,7 @@ public sealed class MindSystem : EntitySystem public bool TryGetMind(EntityUid uid, [NotNullWhen(true)] out Mind? mind, MindContainerComponent? mindContainerComponent = null) { mind = null; - if (!Resolve(uid, ref mindContainerComponent)) + if (!Resolve(uid, ref mindContainerComponent, false)) return false; if (!mindContainerComponent.HasMind) diff --git a/Content.Server/NameIdentifier/NameIdentifierComponent.cs b/Content.Server/NameIdentifier/NameIdentifierComponent.cs deleted file mode 100644 index ff3d1a8898..0000000000 --- a/Content.Server/NameIdentifier/NameIdentifierComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Content.Shared.NameIdentifier; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.NameIdentifier; - -[RegisterComponent] -public sealed class NameIdentifierComponent : Component -{ - [DataField("group", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))] - public string Group = string.Empty; - - /// - /// The randomly generated ID for this entity. - /// - [ViewVariables(VVAccess.ReadWrite), DataField("identifier")] - public int Identifier = -1; -} diff --git a/Content.Server/NameIdentifier/NameIdentifierSystem.cs b/Content.Server/NameIdentifier/NameIdentifierSystem.cs index 82875acec5..bde8466d06 100644 --- a/Content.Server/NameIdentifier/NameIdentifierSystem.cs +++ b/Content.Server/NameIdentifier/NameIdentifierSystem.cs @@ -1,10 +1,8 @@ -using System.Linq; -using Content.Shared.GameTicking; +using Content.Shared.GameTicking; using Content.Shared.NameIdentifier; using Robust.Shared.Collections; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Utility; namespace Content.Server.NameIdentifier; @@ -15,12 +13,13 @@ public sealed class NameIdentifierSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; /// /// Free IDs available per . /// [ViewVariables] - public Dictionary> CurrentIds = new(); + public readonly Dictionary> CurrentIds = new(); public override void Initialize() { @@ -103,11 +102,16 @@ public sealed class NameIdentifierSystem : EntitySystem component.Identifier = id; } + component.FullIdentifier = group.FullName + ? uniqueName + : $"({uniqueName})"; + var meta = MetaData(uid); // "DR-1234" as opposed to "drone (DR-1234)" - meta.EntityName = group.FullName + _metaData.SetEntityName(uid, group.FullName ? uniqueName - : $"{meta.EntityName} ({uniqueName})"; + : $"{meta.EntityName} ({uniqueName})", meta); + Dirty(component); } private void InitialSetupPrototypes() diff --git a/Content.Server/PAI/PAISystem.cs b/Content.Server/PAI/PAISystem.cs index 69496afe5d..27ab1ba165 100644 --- a/Content.Server/PAI/PAISystem.cs +++ b/Content.Server/PAI/PAISystem.cs @@ -1,91 +1,45 @@ -using Content.Server.Ghost.Roles.Components; using Content.Server.Instruments; using Content.Server.Mind.Components; -using Content.Server.Popups; -using Content.Shared.Examine; using Content.Shared.Interaction.Events; using Content.Shared.PAI; -using Content.Shared.Popups; -using Content.Shared.Verbs; using Robust.Server.GameObjects; namespace Content.Server.PAI { public sealed class PAISystem : SharedPAISystem { - [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly InstrumentSystem _instrumentSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnUseInHand); SubscribeLocalEvent(OnMindAdded); SubscribeLocalEvent(OnMindRemoved); - SubscribeLocalEvent>(AddWipeVerb); - } - - private void OnExamined(EntityUid uid, PAIComponent component, ExaminedEvent args) - { - if (args.IsInDetailsRange) - { - if (EntityManager.TryGetComponent(uid, out var mind) && mind.HasMind) - { - args.PushMarkup(Loc.GetString("pai-system-pai-installed")); - } - else if (EntityManager.HasComponent(uid)) - { - args.PushMarkup(Loc.GetString("pai-system-still-searching")); - } - else - { - args.PushMarkup(Loc.GetString("pai-system-off")); - } - } } private void OnUseInHand(EntityUid uid, PAIComponent component, UseInHandEvent args) { - if (args.Handled) - return; + if (!TryComp(uid, out var mind) || !mind.HasMind) + component.LastUser = args.User; + } - // Placeholder PAIs are essentially portable ghost role generators. - - args.Handled = true; - - // Check for pAI activation - if (EntityManager.TryGetComponent(uid, out var mind) && mind.HasMind) - { - _popupSystem.PopupEntity(Loc.GetString("pai-system-pai-installed"), uid, args.User, PopupType.Large); + private void OnMindAdded(EntityUid uid, PAIComponent component, MindAddedMessage args) + { + if (component.LastUser == null) return; - } - else if (EntityManager.HasComponent(uid)) - { - _popupSystem.PopupEntity(Loc.GetString("pai-system-still-searching"), uid, args.User); - return; - } // Ownership tag - string val = Loc.GetString("pai-system-pai-name", ("owner", args.User)); + var val = Loc.GetString("pai-system-pai-name", ("owner", component.LastUser)); // TODO Identity? People shouldn't dox-themselves by carrying around a PAI. // But having the pda's name permanently be "old lady's PAI" is weird. // Changing the PAI's identity in a way that ties it to the owner's identity also seems weird. // Cause then you could remotely figure out information about the owner's equipped items. - EntityManager.GetComponent(component.Owner).EntityName = val; - - var ghostRole = EnsureComp(uid); - EnsureComp(uid); - - ghostRole.RoleName = Loc.GetString("pai-system-role-name"); - ghostRole.RoleDescription = Loc.GetString("pai-system-role-description"); - - _popupSystem.PopupEntity(Loc.GetString("pai-system-searching"), uid, args.User); - UpdatePAIAppearance(uid, PAIStatus.Searching); + _metaData.SetEntityName(uid, val); } private void OnMindRemoved(EntityUid uid, PAIComponent component, MindRemovedMessage args) @@ -94,18 +48,8 @@ namespace Content.Server.PAI PAITurningOff(uid); } - private void OnMindAdded(EntityUid uid, PAIComponent pai, MindAddedMessage args) + public void PAITurningOff(EntityUid uid) { - // Mind was added, shutdown the ghost role stuff so it won't get in the way - if (EntityManager.HasComponent(uid)) - EntityManager.RemoveComponent(uid); - UpdatePAIAppearance(uid, PAIStatus.On); - } - - private void PAITurningOff(EntityUid uid) - { - UpdatePAIAppearance(uid, PAIStatus.Off); - // Close the instrument interface if it was open // before closing if (HasComp(uid) && TryComp(uid, out var actor)) @@ -114,63 +58,12 @@ namespace Content.Server.PAI } // Stop instrument - if (EntityManager.TryGetComponent(uid, out var instrument)) _instrumentSystem.Clean(uid, instrument); - if (EntityManager.TryGetComponent(uid, out var metadata)) + if (TryComp(uid, out var instrument)) _instrumentSystem.Clean(uid, instrument); + if (TryComp(uid, out var metadata)) { var proto = metadata.EntityPrototype; if (proto != null) - metadata.EntityName = proto.Name; - } - } - - private void UpdatePAIAppearance(EntityUid uid, PAIStatus status) - { - if (EntityManager.TryGetComponent(uid, out var appearance)) - { - _appearance.SetData(uid, PAIVisuals.Status, status, appearance); - } - } - - private void AddWipeVerb(EntityUid uid, PAIComponent pai, GetVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract) - return; - - if (EntityManager.TryGetComponent(uid, out var mind) && mind.HasMind) - { - ActivationVerb verb = new(); - verb.Text = Loc.GetString("pai-system-wipe-device-verb-text"); - verb.Act = () => { - if (pai.Deleted) - return; - // Wiping device :( - // The shutdown of the Mind should cause automatic reset of the pAI during OnMindRemoved - // EDIT: But it doesn't!!!! Wtf? Do stuff manually - if (EntityManager.HasComponent(uid)) - { - EntityManager.RemoveComponent(uid); - _popupSystem.PopupEntity(Loc.GetString("pai-system-wiped-device"), uid, args.User, PopupType.Large); - PAITurningOff(uid); - } - }; - args.Verbs.Add(verb); - } - else if (EntityManager.HasComponent(uid)) - { - ActivationVerb verb = new(); - verb.Text = Loc.GetString("pai-system-stop-searching-verb-text"); - verb.Act = () => { - if (pai.Deleted) - return; - if (EntityManager.HasComponent(uid)) - { - EntityManager.RemoveComponent(uid); - EntityManager.RemoveComponent(uid); - _popupSystem.PopupEntity(Loc.GetString("pai-system-stopped-searching"), uid, args.User); - PAITurningOff(uid); - } - }; - args.Verbs.Add(verb); + _metaData.SetEntityName(uid, proto.Name); } } } diff --git a/Content.Server/Power/EntitySystems/ChargerSystem.cs b/Content.Server/Power/EntitySystems/ChargerSystem.cs index 96a71fc02a..f3a4d6c68f 100644 --- a/Content.Server/Power/EntitySystems/ChargerSystem.cs +++ b/Content.Server/Power/EntitySystems/ChargerSystem.cs @@ -1,22 +1,24 @@ using Content.Server.Construction; using Content.Server.Power.Components; using Content.Server.PowerCell; -using Content.Shared.Containers.ItemSlots; using Content.Shared.Examine; using Content.Shared.Power; using Content.Shared.PowerCell.Components; using JetBrains.Annotations; using Robust.Shared.Containers; using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Shared.Storage.Components; +using Robust.Server.Containers; namespace Content.Server.Power.EntitySystems; [UsedImplicitly] internal sealed class ChargerSystem : EntitySystem { - [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; - [Dependency] private readonly PowerCellSystem _cellSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _sharedAppearanceSystem = default!; + [Dependency] private readonly ContainerSystem _container = default!; + [Dependency] private readonly PowerCellSystem _powerCell = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; public override void Initialize() { @@ -27,6 +29,7 @@ internal sealed class ChargerSystem : EntitySystem SubscribeLocalEvent(OnInserted); SubscribeLocalEvent(OnRemoved); SubscribeLocalEvent(OnInsertAttempt); + SubscribeLocalEvent(OnEntityStorageInsertAttempt); SubscribeLocalEvent(OnChargerExamine); } @@ -42,15 +45,19 @@ internal sealed class ChargerSystem : EntitySystem public override void Update(float frameTime) { - foreach (var (_, charger, slotComp) in EntityManager.EntityQuery()) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out _, out var charger, out var containerComp)) { - if (!_itemSlotsSystem.TryGetSlot(charger.Owner, charger.SlotId, out ItemSlot? slot, slotComp)) + if (!_container.TryGetContainer(uid, charger.SlotId, out var container, containerComp)) continue; - if (charger.Status == CellChargerStatus.Empty || charger.Status == CellChargerStatus.Charged || !slot.HasItem) + if (charger.Status == CellChargerStatus.Empty || charger.Status == CellChargerStatus.Charged || container.ContainedEntities.Count == 0) continue; - TransferPower(charger.Owner, slot.Item!.Value, charger, frameTime); + foreach (var contained in container.ContainedEntities) + { + TransferPower(uid, contained, charger, frameTime); + } } } @@ -100,23 +107,32 @@ internal sealed class ChargerSystem : EntitySystem if (args.Container.ID != component.SlotId) return; - if (!TryComp(args.EntityUid, out PowerCellSlotComponent? cellSlot)) + if (!TryComp(args.EntityUid, out var cellSlot)) return; - if (!_itemSlotsSystem.TryGetSlot(args.EntityUid, cellSlot.CellSlotId, out ItemSlot? itemSlot)) - return; - - if (!cellSlot.FitsInCharger || !itemSlot.HasItem) + if (!cellSlot.FitsInCharger) args.Cancel(); } + private void OnEntityStorageInsertAttempt(EntityUid uid, ChargerComponent component, ref InsertIntoEntityStorageAttemptEvent args) + { + if (!component.Initialized || args.Cancelled) + return; + + if (!TryComp(uid, out var cellSlot)) + return; + + if (!cellSlot.FitsInCharger) + args.Cancelled = true; + } + private void UpdateStatus(EntityUid uid, ChargerComponent component) { var status = GetStatus(uid, component); if (component.Status == status || !TryComp(uid, out ApcPowerReceiverComponent? receiver)) return; - if (!_itemSlotsSystem.TryGetSlot(uid, component.SlotId, out ItemSlot? slot)) + if (!_container.TryGetContainer(uid, component.SlotId, out var container)) return; TryComp(uid, out AppearanceComponent? appearance); @@ -136,25 +152,25 @@ internal sealed class ChargerSystem : EntitySystem { case CellChargerStatus.Off: receiver.Load = 0; - _sharedAppearanceSystem.SetData(uid, CellVisual.Light, CellChargerStatus.Off, appearance); + _appearance.SetData(uid, CellVisual.Light, CellChargerStatus.Off, appearance); break; case CellChargerStatus.Empty: receiver.Load = 0; - _sharedAppearanceSystem.SetData(uid, CellVisual.Light, CellChargerStatus.Empty, appearance); + _appearance.SetData(uid, CellVisual.Light, CellChargerStatus.Empty, appearance); break; case CellChargerStatus.Charging: receiver.Load = component.ChargeRate; - _sharedAppearanceSystem.SetData(uid, CellVisual.Light, CellChargerStatus.Charging, appearance); + _appearance.SetData(uid, CellVisual.Light, CellChargerStatus.Charging, appearance); break; case CellChargerStatus.Charged: receiver.Load = 0; - _sharedAppearanceSystem.SetData(uid, CellVisual.Light, CellChargerStatus.Charged, appearance); + _appearance.SetData(uid, CellVisual.Light, CellChargerStatus.Charged, appearance); break; default: throw new ArgumentOutOfRangeException(); } - _sharedAppearanceSystem.SetData(uid, CellVisual.Occupied, slot.HasItem, appearance); + _appearance.SetData(uid, CellVisual.Occupied, container.ContainedEntities.Count != 0, appearance); } private CellChargerStatus GetStatus(EntityUid uid, ChargerComponent component) @@ -171,16 +187,16 @@ internal sealed class ChargerSystem : EntitySystem if (!apcPowerReceiverComponent.Powered) return CellChargerStatus.Off; - if (!_itemSlotsSystem.TryGetSlot(uid, component.SlotId, out ItemSlot? slot)) + if (!_container.TryGetContainer(uid, component.SlotId, out var container)) return CellChargerStatus.Off; - if (!slot.HasItem) + if (container.ContainedEntities.Count == 0) return CellChargerStatus.Empty; - if (!SearchForBattery(slot.Item!.Value, out BatteryComponent? heldBattery)) + if (!SearchForBattery(container.ContainedEntities.First(), out var heldBattery)) return CellChargerStatus.Off; - if (heldBattery != null && Math.Abs(heldBattery.MaxCharge - heldBattery.CurrentCharge) < 0.01) + if (Math.Abs(heldBattery.MaxCharge - heldBattery.CurrentCharge) < 0.01) return CellChargerStatus.Charged; return CellChargerStatus.Charging; @@ -194,7 +210,7 @@ internal sealed class ChargerSystem : EntitySystem if (!receiverComponent.Powered) return; - if (!SearchForBattery(targetEntity, out BatteryComponent? heldBattery)) + if (!SearchForBattery(targetEntity, out var heldBattery)) return; heldBattery.CurrentCharge += component.ChargeRate * frameTime; @@ -213,7 +229,7 @@ internal sealed class ChargerSystem : EntitySystem if (!TryComp(uid, out component)) { // or by checking for a power cell slot on the inserted entity - return _cellSystem.TryGetBatteryFromSlot(uid, out component); + return _powerCell.TryGetBatteryFromSlot(uid, out component); } return true; } diff --git a/Content.Server/PowerCell/PowerCellSystem.cs b/Content.Server/PowerCell/PowerCellSystem.cs index f239af4bdc..0f0bf12022 100644 --- a/Content.Server/PowerCell/PowerCellSystem.cs +++ b/Content.Server/PowerCell/PowerCellSystem.cs @@ -88,6 +88,9 @@ public sealed partial class PowerCellSystem : SharedPowerCellSystem { base.OnCellRemoved(uid, component, args); + if (args.Container.ID != component.CellSlotId) + return; + var ev = new PowerCellSlotEmptyEvent(); RaiseLocalEvent(uid, ref ev); } @@ -135,7 +138,7 @@ public sealed partial class PowerCellSystem : SharedPowerCellSystem if (!Resolve(uid, ref battery, ref cell, false)) return true; - return HasCharge(uid, float.MinValue, cell, user); + return HasCharge(uid, battery.DrawRate, cell, user); } #endregion @@ -229,19 +232,26 @@ public sealed partial class PowerCellSystem : SharedPowerCellSystem private void OnCellExamined(EntityUid uid, PowerCellComponent component, ExaminedEvent args) { - if (TryComp(uid, out var battery)) - OnBatteryExamined(uid, battery, args); + TryComp(uid, out var battery); + OnBatteryExamined(uid, battery, args); } private void OnCellSlotExamined(EntityUid uid, PowerCellSlotComponent component, ExaminedEvent args) { - if (TryGetBatteryFromSlot(uid, out var battery)) - OnBatteryExamined(uid, battery, args); + TryGetBatteryFromSlot(uid, out var battery); + OnBatteryExamined(uid, battery, args); } - private void OnBatteryExamined(EntityUid uid, BatteryComponent component, ExaminedEvent args) + private void OnBatteryExamined(EntityUid uid, BatteryComponent? component, ExaminedEvent args) { - var charge = component.CurrentCharge / component.MaxCharge * 100; - args.PushMarkup(Loc.GetString("power-cell-component-examine-details", ("currentCharge", $"{charge:F0}"))); + if (component != null) + { + var charge = component.CurrentCharge / component.MaxCharge * 100; + args.PushMarkup(Loc.GetString("power-cell-component-examine-details", ("currentCharge", $"{charge:F0}"))); + } + else + { + args.PushMarkup(Loc.GetString("power-cell-component-examine-details-no-battery")); + } } } diff --git a/Content.Server/Repairable/RepairableComponent.cs b/Content.Server/Repairable/RepairableComponent.cs index e122c492cb..911397ec94 100644 --- a/Content.Server/Repairable/RepairableComponent.cs +++ b/Content.Server/Repairable/RepairableComponent.cs @@ -31,5 +31,11 @@ namespace Content.Server.Repairable /// [ViewVariables(VVAccess.ReadWrite)] [DataField("selfRepairPenalty")] public float SelfRepairPenalty = 3f; + + /// + /// Whether or not an entity is allowed to repair itself. + /// + [DataField("allowSelfRepair")] + public bool AllowSelfRepair = true; } } diff --git a/Content.Server/Repairable/RepairableSystem.cs b/Content.Server/Repairable/RepairableSystem.cs index cba4f8e8aa..8d9833a66f 100644 --- a/Content.Server/Repairable/RepairableSystem.cs +++ b/Content.Server/Repairable/RepairableSystem.cs @@ -62,7 +62,12 @@ namespace Content.Server.Repairable // Add a penalty to how long it takes if the user is repairing itself if (args.User == args.Target) + { + if (!component.AllowSelfRepair) + return; + delay *= component.SelfRepairPenalty; + } // Run the repairing doafter args.Handled = _toolSystem.UseTool(args.Used, args.User, uid, delay, component.QualityNeeded, new RepairFinishedEvent()); diff --git a/Content.Server/Silicons/Borgs/BorgSystem.MMI.cs b/Content.Server/Silicons/Borgs/BorgSystem.MMI.cs new file mode 100644 index 0000000000..34e00001b9 --- /dev/null +++ b/Content.Server/Silicons/Borgs/BorgSystem.MMI.cs @@ -0,0 +1,79 @@ +using Content.Server.Mind.Components; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Silicons.Borgs.Components; +using Robust.Shared.Containers; + +namespace Content.Server.Silicons.Borgs; + +/// +public sealed partial class BorgSystem +{ + public void InitializeMMI() + { + SubscribeLocalEvent(OnMMIInit); + SubscribeLocalEvent(OnMMIEntityInserted); + SubscribeLocalEvent(OnMMIMindAdded); + SubscribeLocalEvent(OnMMIMindRemoved); + + SubscribeLocalEvent(OnMMILinkedMindAdded); + SubscribeLocalEvent(OnMMILinkedRemoved); + } + + private void OnMMIInit(EntityUid uid, MMIComponent component, ComponentInit args) + { + if (!TryComp(uid, out var itemSlots)) + return; + + if (ItemSlots.TryGetSlot(uid, component.BrainSlotId, out var slot, itemSlots)) + component.BrainSlot = slot; + else + ItemSlots.AddItemSlot(uid, component.BrainSlotId, component.BrainSlot, itemSlots); + } + + private void OnMMIEntityInserted(EntityUid uid, MMIComponent component, EntInsertedIntoContainerMessage args) + { + if (args.Container.ID != component.BrainSlotId) + return; + + var ent = args.Entity; + var linked = EnsureComp(ent); + linked.LinkedMMI = uid; + + if (_mind.TryGetMind(ent, out var mind)) + _mind.TransferTo(mind, uid, true); + + _appearance.SetData(uid, MMIVisuals.BrainPresent, true); + } + + private void OnMMIMindAdded(EntityUid uid, MMIComponent component, MindAddedMessage args) + { + _appearance.SetData(uid, MMIVisuals.HasMind, true); + } + + private void OnMMIMindRemoved(EntityUid uid, MMIComponent component, MindRemovedMessage args) + { + _appearance.SetData(uid, MMIVisuals.HasMind, false); + } + + private void OnMMILinkedMindAdded(EntityUid uid, MMILinkedComponent component, MindAddedMessage args) + { + if (!_mind.TryGetMind(uid, out var mind) || component.LinkedMMI == null) + return; + _mind.TransferTo(mind, component.LinkedMMI, true); + } + + private void OnMMILinkedRemoved(EntityUid uid, MMILinkedComponent component, EntGotRemovedFromContainerMessage args) + { + if (Terminating(uid)) + return; + + if (component.LinkedMMI is not { } linked) + return; + RemComp(uid, component); + + if (_mind.TryGetMind(linked, out var mind)) + _mind.TransferTo(mind, uid, true); + + _appearance.SetData(linked, MMIVisuals.BrainPresent, false); + } +} diff --git a/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs b/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs new file mode 100644 index 0000000000..ee45757304 --- /dev/null +++ b/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs @@ -0,0 +1,316 @@ +using System.Linq; +using Content.Shared.Hands.Components; +using Content.Shared.Interaction.Components; +using Content.Shared.Silicons.Borgs.Components; +using Robust.Shared.Containers; + +namespace Content.Server.Silicons.Borgs; + +/// +public sealed partial class BorgSystem +{ + public void InitializeModules() + { + SubscribeLocalEvent(OnModuleGotInserted); + SubscribeLocalEvent(OnModuleGotRemoved); + + SubscribeLocalEvent(OnSelectableInstalled); + SubscribeLocalEvent(OnSelectableUninstalled); + SubscribeLocalEvent(OnSelectableAction); + + SubscribeLocalEvent(OnProvideItemStartup); + SubscribeLocalEvent(OnItemModuleSelected); + SubscribeLocalEvent(OnItemModuleUnselected); + } + + private void OnModuleGotInserted(EntityUid uid, BorgModuleComponent component, EntGotInsertedIntoContainerMessage args) + { + var chassis = args.Container.Owner; + + if (!TryComp(chassis, out var chassisComp) || + args.Container != chassisComp.ModuleContainer || + !chassisComp.Activated) + return; + + if (!_powerCell.HasDrawCharge(uid)) + return; + + InstallModule(chassis, uid, chassisComp, component); + } + + private void OnModuleGotRemoved(EntityUid uid, BorgModuleComponent component, EntGotRemovedFromContainerMessage args) + { + var chassis = args.Container.Owner; + + if (Terminating(chassis)) + return; + + if (!TryComp(chassis, out var chassisComp) || + args.Container != chassisComp.ModuleContainer) + return; + + UninstallModule(chassis, uid, chassisComp, component); + } + + private void OnProvideItemStartup(EntityUid uid, ItemBorgModuleComponent component, ComponentStartup args) + { + component.ProvidedContainer = Container.EnsureContainer(uid, component.ProvidedContainerId); + } + + private void OnSelectableInstalled(EntityUid uid, SelectableBorgModuleComponent component, ref BorgModuleInstalledEvent args) + { + var chassis = args.ChassisEnt; + component.ModuleSwapAction.EntityIcon = uid; + _actions.AddAction(chassis, component.ModuleSwapAction, uid); + SelectModule(chassis, uid, moduleComp: component); + } + + private void OnSelectableUninstalled(EntityUid uid, SelectableBorgModuleComponent component, ref BorgModuleUninstalledEvent args) + { + var chassis = args.ChassisEnt; + _actions.RemoveProvidedActions(chassis, uid); + UnselectModule(chassis, uid, moduleComp: component); + } + + private void OnSelectableAction(EntityUid uid, SelectableBorgModuleComponent component, BorgModuleActionSelectedEvent args) + { + var chassis = args.Performer; + if (!TryComp(chassis, out var chassisComp)) + return; + + if (chassisComp.SelectedModule == uid) + { + UnselectModule(chassis, chassisComp.SelectedModule, chassisComp); + args.Handled = true; + return; + } + + UnselectModule(chassis, chassisComp.SelectedModule, chassisComp); + SelectModule(chassis, uid, chassisComp, component); + args.Handled = true; + } + + /// + /// Selects a module, enablind the borg to use its provided abilities. + /// + public void SelectModule(EntityUid chassis, + EntityUid moduleUid, + BorgChassisComponent? chassisComp = null, + SelectableBorgModuleComponent? moduleComp = null) + { + if (Terminating(chassis) || Deleted(chassis)) + return; + + if (!Resolve(chassis, ref chassisComp)) + return; + + if (chassisComp.SelectedModule != null) + return; + + if (chassisComp.SelectedModule == moduleUid) + return; + + if (!Resolve(moduleUid, ref moduleComp, false)) + return; + + var ev = new BorgModuleSelectedEvent(chassis); + RaiseLocalEvent(moduleUid, ref ev); + chassisComp.SelectedModule = moduleUid; + } + + /// + /// Unselects a module, removing its provided abilities + /// + public void UnselectModule(EntityUid chassis, + EntityUid? moduleUid, + BorgChassisComponent? chassisComp = null, + SelectableBorgModuleComponent? moduleComp = null) + { + if (Terminating(chassis) || Deleted(chassis)) + return; + + if (!Resolve(chassis, ref chassisComp)) + return; + + if (moduleUid == null) + return; + + if (chassisComp.SelectedModule != moduleUid) + return; + + if (!Resolve(moduleUid.Value, ref moduleComp, false)) + return; + + var ev = new BorgModuleUnselectedEvent(chassis); + RaiseLocalEvent(moduleUid.Value, ref ev); + chassisComp.SelectedModule = null; + } + + private void OnItemModuleSelected(EntityUid uid, ItemBorgModuleComponent component, ref BorgModuleSelectedEvent args) + { + ProvideItems(args.Chassis, uid, component: component); + } + + private void OnItemModuleUnselected(EntityUid uid, ItemBorgModuleComponent component, ref BorgModuleUnselectedEvent args) + { + RemoveProvidedItems(args.Chassis, uid, component: component); + } + + private void ProvideItems(EntityUid chassis, EntityUid uid, BorgChassisComponent? chassisComponent = null, ItemBorgModuleComponent? component = null) + { + if (!Resolve(chassis, ref chassisComponent) || !Resolve(uid, ref component)) + return; + + if (!TryComp(chassis, out var hands)) + return; + + var xform = Transform(chassis); + foreach (var itemProto in component.Items) + { + EntityUid item; + + if (!component.ItemsCreated) + { + item = Spawn(itemProto, xform.Coordinates); + } + else + { + item = component.ProvidedContainer.ContainedEntities + .FirstOrDefault(ent => Prototype(ent)?.ID == itemProto); + if (!item.IsValid()) + { + Log.Debug($"no items found: {component.ProvidedContainer.ContainedEntities.Count}"); + continue; + } + + component.ProvidedContainer.Remove(item, EntityManager, force: true); + } + + if (!item.IsValid()) + { + Log.Debug("no valid item"); + continue; + } + + var handId = $"{uid}-item{component.HandCounter}"; + component.HandCounter++; + _hands.AddHand(chassis, handId, HandLocation.Middle, hands); + _hands.DoPickup(chassis, hands.Hands[handId], item, hands); + EnsureComp(item); + component.ProvidedItems.Add(handId, item); + } + + component.ItemsCreated = true; + } + + private void RemoveProvidedItems(EntityUid chassis, EntityUid uid, BorgChassisComponent? chassisComponent = null, ItemBorgModuleComponent? component = null) + { + if (!Resolve(chassis, ref chassisComponent) || !Resolve(uid, ref component)) + return; + + if (!TryComp(chassis, out var hands)) + return; + + foreach (var (handId, item) in component.ProvidedItems) + { + if (!Deleted(item) && !Terminating(item)) + { + RemComp(item); + component.ProvidedContainer.Insert(item, EntityManager); + } + _hands.RemoveHand(chassis, handId, hands); + } + component.ProvidedItems.Clear(); + } + + /// + /// Checks if a given module can be inserted into a borg + /// + public bool CanInsertModule(EntityUid uid, EntityUid module, BorgChassisComponent? component = null, BorgModuleComponent? moduleComponent = null, EntityUid? user = null) + { + if (!Resolve(uid, ref component) || !Resolve(module, ref moduleComponent)) + return false; + + if (component.ModuleContainer.ContainedEntities.Count >= component.MaxModules) + return false; + + if (component.ModuleWhitelist?.IsValid(module, EntityManager) == false) + { + if (user != null) + Popup.PopupEntity(Loc.GetString("borg-module-whitelist-deny"), uid, user.Value); + return false; + } + + return true; + } + + /// + /// Installs and activates all modules currently inside the borg's module container + /// + public void InstallAllModules(EntityUid uid, BorgChassisComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + var query = GetEntityQuery(); + foreach (var moduleEnt in new List(component.ModuleContainer.ContainedEntities)) + { + if (!query.TryGetComponent(moduleEnt, out var moduleComp)) + continue; + + InstallModule(uid, moduleEnt, component, moduleComp); + } + } + + /// + /// Deactivates all modules currently inside the borg's module container + /// + /// + /// + public void DisableAllModules(EntityUid uid, BorgChassisComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + var query = GetEntityQuery(); + foreach (var moduleEnt in new List(component.ModuleContainer.ContainedEntities)) + { + if (!query.TryGetComponent(moduleEnt, out var moduleComp)) + continue; + + UninstallModule(uid, moduleEnt, component, moduleComp); + } + } + + /// + /// Installs a single module into a borg. + /// + public void InstallModule(EntityUid uid, EntityUid module, BorgChassisComponent? component, BorgModuleComponent? moduleComponent = null) + { + if (!Resolve(uid, ref component) || !Resolve(module, ref moduleComponent)) + return; + + if (moduleComponent.Installed) + return; + + moduleComponent.InstalledEntity = uid; + var ev = new BorgModuleInstalledEvent(uid); + RaiseLocalEvent(module, ref ev); + } + + /// + /// Uninstalls a single module from a borg. + /// + public void UninstallModule(EntityUid uid, EntityUid module, BorgChassisComponent? component, BorgModuleComponent? moduleComponent = null) + { + if (!Resolve(uid, ref component) || !Resolve(module, ref moduleComponent)) + return; + + if (!moduleComponent.Installed) + return; + + moduleComponent.InstalledEntity = null; + var ev = new BorgModuleUninstalledEvent(uid); + RaiseLocalEvent(module, ref ev); + } +} diff --git a/Content.Server/Silicons/Borgs/BorgSystem.Ui.cs b/Content.Server/Silicons/Borgs/BorgSystem.Ui.cs new file mode 100644 index 0000000000..4d79338db7 --- /dev/null +++ b/Content.Server/Silicons/Borgs/BorgSystem.Ui.cs @@ -0,0 +1,108 @@ +using System.Linq; +using Content.Server.UserInterface; +using Content.Shared.Database; +using Content.Shared.NameIdentifier; +using Content.Shared.PowerCell.Components; +using Content.Shared.Preferences; +using Content.Shared.Silicons.Borgs; +using Content.Shared.Silicons.Borgs.Components; + +namespace Content.Server.Silicons.Borgs; + +/// +public sealed partial class BorgSystem +{ + public void InitializeUI() + { + SubscribeLocalEvent(OnBeforeBorgUiOpen); + SubscribeLocalEvent(OnEjectBrainBuiMessage); + SubscribeLocalEvent(OnEjectBatteryBuiMessage); + SubscribeLocalEvent(OnSetNameBuiMessage); + SubscribeLocalEvent(OnRemoveModuleBuiMessage); + } + + private void OnBeforeBorgUiOpen(EntityUid uid, BorgChassisComponent component, BeforeActivatableUIOpenEvent args) + { + UpdateUI(uid, component); + } + + private void OnEjectBrainBuiMessage(EntityUid uid, BorgChassisComponent component, BorgEjectBrainBuiMessage args) + { + if (args.Session.AttachedEntity is not { } attachedEntity || component.BrainEntity is not { } brain) + return; + + _adminLog.Add(LogType.Action, LogImpact.Medium, + $"{ToPrettyString(attachedEntity):player} removed brain {ToPrettyString(brain)} from borg {ToPrettyString(uid)}"); + component.BrainContainer.Remove(brain, EntityManager); + _hands.TryPickupAnyHand(attachedEntity, brain); + UpdateUI(uid, component); + } + + private void OnEjectBatteryBuiMessage(EntityUid uid, BorgChassisComponent component, BorgEjectBatteryBuiMessage args) + { + if (args.Session.AttachedEntity is not { } attachedEntity || + !TryComp(uid, out var slotComp) || + !Container.TryGetContainer(uid, slotComp.CellSlotId, out var container) || + !container.ContainedEntities.Any()) + { + return; + } + + var ents = Container.EmptyContainer(container); + _hands.TryPickupAnyHand(attachedEntity, ents.First()); + } + + private void OnSetNameBuiMessage(EntityUid uid, BorgChassisComponent component, BorgSetNameBuiMessage args) + { + if (args.Session.AttachedEntity is not { } attachedEntity) + return; + + if (args.Name.Length > HumanoidCharacterProfile.MaxNameLength || + args.Name.Length == 0 || + string.IsNullOrWhiteSpace(args.Name) || + string.IsNullOrEmpty(args.Name)) + { + return; + } + + var name = args.Name.Trim(); + if (TryComp(uid, out var identifier)) + name = $"{name} {identifier.FullIdentifier}"; + + _metaData.SetEntityName(uid, name); + _adminLog.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(attachedEntity):player} set borg \"{ToPrettyString(uid)}\"'s name to: {name}"); + } + + private void OnRemoveModuleBuiMessage(EntityUid uid, BorgChassisComponent component, BorgRemoveModuleBuiMessage args) + { + if (args.Session.AttachedEntity is not { } attachedEntity) + return; + + if (!component.ModuleContainer.Contains(args.Module)) + return; + + _adminLog.Add(LogType.Action, LogImpact.Medium, + $"{ToPrettyString(attachedEntity):player} removed module {ToPrettyString(args.Module)} from borg {ToPrettyString(uid)}"); + component.ModuleContainer.Remove(args.Module); + _hands.TryPickupAnyHand(attachedEntity, args.Module); + + UpdateUI(uid, component); + } + + public void UpdateUI(EntityUid uid, BorgChassisComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + var chargePercent = 0f; + var hasBattery = false; + if (_powerCell.TryGetBatteryFromSlot(uid, out var battery)) + { + hasBattery = true; + chargePercent = battery.Charge / battery.MaxCharge; + } + + var state = new BorgBuiState(chargePercent, hasBattery); + _ui.TrySetUiState(uid, BorgUiKey.Key, state); + } +} diff --git a/Content.Server/Silicons/Borgs/BorgSystem.cs b/Content.Server/Silicons/Borgs/BorgSystem.cs new file mode 100644 index 0000000000..e64fbfb169 --- /dev/null +++ b/Content.Server/Silicons/Borgs/BorgSystem.cs @@ -0,0 +1,318 @@ +using Content.Server.Actions; +using Content.Server.Administration.Logs; +using Content.Server.Administration.Managers; +using Content.Server.Hands.Systems; +using Content.Server.Mind; +using Content.Server.Mind.Components; +using Content.Server.Players.PlayTimeTracking; +using Content.Server.PowerCell; +using Content.Server.UserInterface; +using Content.Shared.Alert; +using Content.Shared.Database; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Movement.Systems; +using Content.Shared.PowerCell; +using Content.Shared.PowerCell.Components; +using Content.Shared.Silicons.Borgs; +using Content.Shared.Silicons.Borgs.Components; +using Content.Shared.Throwing; +using Content.Shared.Wires; +using Robust.Server.GameObjects; +using Robust.Server.Player; +using Robust.Shared.Containers; +using Robust.Shared.Random; + +namespace Content.Server.Silicons.Borgs; + +/// +public sealed partial class BorgSystem : SharedBorgSystem +{ + [Dependency] private readonly IAdminLogManager _adminLog = default!; + [Dependency] private readonly IBanManager _banManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly ActionsSystem _actions = default!; + [Dependency] private readonly AlertsSystem _alerts = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly HandsSystem _hands = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!; + [Dependency] private readonly PlayTimeTrackingSystem _playTimeTracking = default!; + [Dependency] private readonly PowerCellSystem _powerCell = default!; + [Dependency] private readonly ThrowingSystem _throwing = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnChassisInteractUsing); + SubscribeLocalEvent(OnMindAdded); + SubscribeLocalEvent(OnMindRemoved); + SubscribeLocalEvent(OnPowerCellChanged); + SubscribeLocalEvent(OnPowerCellSlotEmpty); + SubscribeLocalEvent(OnUIOpenAttempt); + + SubscribeLocalEvent(OnBrainMindAdded); + + InitializeModules(); + InitializeMMI(); + InitializeUI(); + } + + private void OnMapInit(EntityUid uid, BorgChassisComponent component, MapInitEvent args) + { + UpdateBatteryAlert(uid); + _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); + + var coordinates = Transform(uid).Coordinates; + + if (component.StartingBrain != null) + { + component.BrainContainer.Insert(Spawn(component.StartingBrain, coordinates), EntityManager); + } + + foreach (var startingModule in component.StartingModules) + { + component.ModuleContainer.Insert(Spawn(startingModule, coordinates), EntityManager); + } + } + + private void OnChassisInteractUsing(EntityUid uid, BorgChassisComponent component, AfterInteractUsingEvent args) + { + if (!args.CanReach || args.Handled || uid == args.User) + return; + + var used = args.Used; + TryComp(used, out var brain); + TryComp(used, out var module); + + if (TryComp(uid, out var panel) && !panel.Open) + { + if (brain != null || module != null) + { + Popup.PopupEntity(Loc.GetString("borg-panel-not-open"), uid, args.User); + } + return; + } + + if (component.BrainEntity == null && + brain != null && + component.BrainWhitelist?.IsValid(used) != false) + { + if (_mind.TryGetMind(used, out var mind) && mind.Session != null) + { + if (!CanPlayerBeBorgged(mind.Session, component)) + { + Popup.PopupEntity(Loc.GetString("borg-player-not-allowed"), used, args.User); + return; + } + } + + component.BrainContainer.Insert(used); + _adminLog.Add(LogType.Action, LogImpact.Medium, + $"{ToPrettyString(args.User):player} installed brain {ToPrettyString(used)} into borg {ToPrettyString(uid)}"); + args.Handled = true; + UpdateUI(uid, component); + } + + if (module != null && CanInsertModule(uid, used, component, module, args.User)) + { + component.ModuleContainer.Insert(used); + _adminLog.Add(LogType.Action, LogImpact.Low, + $"{ToPrettyString(args.User):player} installed module {ToPrettyString(used)} into borg {ToPrettyString(uid)}"); + args.Handled = true; + UpdateUI(uid, component); + } + } + + // todo: consider transferring over the ghost role? managing that might suck. + protected override void OnInserted(EntityUid uid, BorgChassisComponent component, EntInsertedIntoContainerMessage args) + { + base.OnInserted(uid, component, args); + + if (HasComp(args.Entity) && _mind.TryGetMind(args.Entity, out var mind)) + { + _mind.TransferTo(mind, uid); + } + } + + protected override void OnRemoved(EntityUid uid, BorgChassisComponent component, EntRemovedFromContainerMessage args) + { + base.OnRemoved(uid, component, args); + + if (HasComp(args.Entity) && _mind.TryGetMind(uid, out var mind)) + { + _mind.TransferTo(mind, args.Entity); + } + } + + private void OnMindAdded(EntityUid uid, BorgChassisComponent component, MindAddedMessage args) + { + BorgActivate(uid, component); + } + + private void OnMindRemoved(EntityUid uid, BorgChassisComponent component, MindRemovedMessage args) + { + BorgDeactivate(uid, component); + } + + private void OnPowerCellChanged(EntityUid uid, BorgChassisComponent component, PowerCellChangedEvent args) + { + UpdateBatteryAlert(uid); + + if (!TryComp(uid, out var draw)) + return; + + // if we eject the battery or run out of charge, then disable + if (args.Ejected || !_powerCell.HasDrawCharge(uid)) + { + DisableBorgAbilities(uid, component); + return; + } + + // if we aren't drawing and suddenly get enough power to draw again, reeanble. + if (_powerCell.HasDrawCharge(uid, draw)) + { + // only reenable the powerdraw if a player has the role. + if (!draw.Drawing && _mind.TryGetMind(uid, out _)) + _powerCell.SetPowerCellDrawEnabled(uid, true); + + EnableBorgAbilities(uid, component); + } + + UpdateUI(uid, component); + } + + private void OnPowerCellSlotEmpty(EntityUid uid, BorgChassisComponent component, ref PowerCellSlotEmptyEvent args) + { + DisableBorgAbilities(uid, component); + UpdateUI(uid, component); + } + + private void OnUIOpenAttempt(EntityUid uid, BorgChassisComponent component, ActivatableUIOpenAttemptEvent args) + { + // borgs can't view their own ui + if (args.User == uid) + args.Cancel(); + } + + private void OnBrainMindAdded(EntityUid uid, BorgBrainComponent component, MindAddedMessage args) + { + if (!Container.TryGetOuterContainer(uid, Transform(uid), out var container)) + return; + + var containerEnt = container.Owner; + + if (!TryComp(containerEnt, out var chassisComponent) || + container.ID != chassisComponent.BrainContainerId) + return; + + if (!_mind.TryGetMind(uid, out var mind) || mind.Session == null) + return; + + if (!CanPlayerBeBorgged(mind.Session, chassisComponent)) + { + Popup.PopupEntity(Loc.GetString("borg-player-not-allowed-eject"), uid); + Container.RemoveEntity(containerEnt, uid); + _throwing.TryThrow(uid, _random.NextVector2() * 5, 5f); + return; + } + + _mind.TransferTo(mind, containerEnt); + } + + private void UpdateBatteryAlert(EntityUid uid, PowerCellSlotComponent? slotComponent = null) + { + if (!_powerCell.TryGetBatteryFromSlot(uid, out var battery, slotComponent)) + { + _alerts.ClearAlert(uid, AlertType.BorgBattery); + _alerts.ShowAlert(uid, AlertType.BorgBatteryNone); + return; + } + + var chargePercent = (short) MathF.Round(battery.CurrentCharge / battery.MaxCharge * 10f); + + // we make sure 0 only shows if they have absolutely no battery. + // also account for floating point imprecision + if (chargePercent == 0 && _powerCell.HasDrawCharge(uid, cell: slotComponent)) + { + chargePercent = 1; + } + + _alerts.ClearAlert(uid, AlertType.BorgBatteryNone); + _alerts.ShowAlert(uid, AlertType.BorgBattery, chargePercent); + } + + /// + /// Activates the borg, enabling all of its modules. + /// + public void EnableBorgAbilities(EntityUid uid, BorgChassisComponent component) + { + if (component.Activated) + return; + + component.Activated = true; + InstallAllModules(uid, component); + Dirty(component); + _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); + } + + /// + /// Deactivates the borg, disabling all of its modules and decreasing its speed. + /// + public void DisableBorgAbilities(EntityUid uid, BorgChassisComponent component) + { + if (!component.Activated) + return; + + component.Activated = false; + DisableAllModules(uid, component); + Dirty(component); + _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); + } + + /// + /// Activates a borg when a player occupies it + /// + public void BorgActivate(EntityUid uid, BorgChassisComponent component) + { + component.HasPlayer = true; + Popup.PopupEntity(Loc.GetString("borg-mind-added", ("name", Identity.Name(uid, EntityManager))), uid); + _powerCell.SetPowerCellDrawEnabled(uid, true); + _appearance.SetData(uid, BorgVisuals.HasPlayer, true); + Dirty(uid, component); + } + + /// + /// Deactivates a borg when a player leaves it. + /// + public void BorgDeactivate(EntityUid uid, BorgChassisComponent component) + { + component.HasPlayer = false; + Popup.PopupEntity(Loc.GetString("borg-mind-removed", ("name", Identity.Name(uid, EntityManager))), uid); + _powerCell.SetPowerCellDrawEnabled(uid, false); + _appearance.SetData(uid, BorgVisuals.HasPlayer, false); + Dirty(uid, component); + } + + /// + /// Checks that a player has fulfilled the requirements for the borg job. + /// If they don't have enough hours, they cannot be placed into a chassis. + /// + public bool CanPlayerBeBorgged(IPlayerSession session, BorgChassisComponent component) + { + var disallowedJobs = _playTimeTracking.GetDisallowedJobs(session); + + if (disallowedJobs.Contains(component.BorgJobId)) + return false; + + if (_banManager.GetJobBans(session.UserId)?.Contains(component.BorgJobId) == true) + return false; + + return true; + } +} diff --git a/Content.Server/Silicons/Laws/SiliconLawSystem.cs b/Content.Server/Silicons/Laws/SiliconLawSystem.cs new file mode 100644 index 0000000000..7fbddb83a3 --- /dev/null +++ b/Content.Server/Silicons/Laws/SiliconLawSystem.cs @@ -0,0 +1,157 @@ +using Content.Server.Chat.Managers; +using Content.Server.Mind.Components; +using Content.Server.Station.Systems; +using Content.Shared.Actions; +using Content.Shared.Actions.ActionTypes; +using Content.Shared.Chat; +using Content.Shared.Emag.Components; +using Content.Shared.Emag.Systems; +using Content.Shared.Examine; +using Content.Shared.Silicons.Laws; +using Content.Shared.Silicons.Laws.Components; +using Robust.Server.GameObjects; +using Robust.Server.Player; +using Robust.Shared.Prototypes; + +namespace Content.Server.Silicons.Laws; + +/// +public sealed class SiliconLawSystem : SharedSiliconLawSystem +{ + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly StationSystem _station = default!; + [Dependency] private readonly UserInterfaceSystem _userInterface = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnComponentStartup); + SubscribeLocalEvent(OnComponentShutdown); + SubscribeLocalEvent(OnMindAdded); + SubscribeLocalEvent(OnToggleLawsScreen); + SubscribeLocalEvent(OnBoundUIOpened); + + SubscribeLocalEvent(OnDirectedGetLaws); + SubscribeLocalEvent(OnDirectedEmagGetLaws); + SubscribeLocalEvent(OnExamined); + } + + private void OnComponentStartup(EntityUid uid, SiliconLawBoundComponent component, ComponentStartup args) + { + component.ProvidedAction = new (_prototype.Index(component.ViewLawsAction)); + _actions.AddAction(uid, component.ProvidedAction, null); + } + + private void OnComponentShutdown(EntityUid uid, SiliconLawBoundComponent component, ComponentShutdown args) + { + if (component.ProvidedAction != null) + _actions.RemoveAction(uid, component.ProvidedAction); + } + + private void OnMindAdded(EntityUid uid, SiliconLawBoundComponent component, MindAddedMessage args) + { + if (!TryComp(uid, out var actor)) + return; + + var msg = Loc.GetString("laws-notify"); + var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", msg)); + _chatManager.ChatMessageToOne(ChatChannel.Server, msg, wrappedMessage, default, false, + actor.PlayerSession.ConnectedClient, colorOverride: Color.FromHex("#2ed2fd")); + } + + private void OnToggleLawsScreen(EntityUid uid, SiliconLawBoundComponent component, ToggleLawsScreenEvent args) + { + if (args.Handled || !TryComp(uid, out var actor)) + return; + args.Handled = true; + + _userInterface.TryToggleUi(uid, SiliconLawsUiKey.Key, actor.PlayerSession); + } + + private void OnBoundUIOpened(EntityUid uid, SiliconLawBoundComponent component, BoundUIOpenedEvent args) + { + var state = new SiliconLawBuiState(GetLaws(uid)); + _userInterface.TrySetUiState(args.Entity, SiliconLawsUiKey.Key, state, (IPlayerSession) args.Session); + } + + private void OnDirectedGetLaws(EntityUid uid, SiliconLawProviderComponent component, ref GetSiliconLawsEvent args) + { + if (args.Handled || HasComp(uid) || component.Laws.Count == 0) + return; + + foreach (var law in component.Laws) + { + args.Laws.Add(_prototype.Index(law)); + } + + args.Handled = true; + } + + private void OnDirectedEmagGetLaws(EntityUid uid, EmagSiliconLawComponent component, ref GetSiliconLawsEvent args) + { + if (args.Handled || !HasComp(uid) || component.OwnerName == null) + return; + + args.Laws.Add(new SiliconLaw + { + LawString = Loc.GetString("law-emag-custom", ("name", component.OwnerName)), + Order = 0 + }); + } + + private void OnExamined(EntityUid uid, EmagSiliconLawComponent component, ExaminedEvent args) + { + if (!args.IsInDetailsRange || !HasComp(uid)) + return; + + args.PushMarkup(Loc.GetString("laws-compromised-examine")); + } + + protected override void OnGotEmagged(EntityUid uid, EmagSiliconLawComponent component, ref GotEmaggedEvent args) + { + base.OnGotEmagged(uid, component, ref args); + NotifyLawsChanged(uid); + } + + public List GetLaws(EntityUid uid) + { + var xform = Transform(uid); + + var ev = new GetSiliconLawsEvent(uid); + + RaiseLocalEvent(uid, ref ev); + if (ev.Handled) + return ev.Laws; + + if (_station.GetOwningStation(uid, xform) is { } station) + { + RaiseLocalEvent(station, ref ev); + if (ev.Handled) + return ev.Laws; + } + + if (xform.GridUid is { } grid) + { + RaiseLocalEvent(grid, ref ev); + if (ev.Handled) + return ev.Laws; + } + + RaiseLocalEvent(ref ev); + return ev.Laws; + } + + public void NotifyLawsChanged(EntityUid uid) + { + if (!TryComp(uid, out var actor)) + return; + + var msg = Loc.GetString("laws-update-notify"); + var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", msg)); + _chatManager.ChatMessageToOne(ChatChannel.Server, msg, wrappedMessage, default, false, actor.PlayerSession.ConnectedClient, colorOverride: Color.FromHex("#2ed2fd")); + } +} diff --git a/Content.Server/Stack/StackSystem.cs b/Content.Server/Stack/StackSystem.cs index 03e0ea7ba3..12bd2a1bd3 100644 --- a/Content.Server/Stack/StackSystem.cs +++ b/Content.Server/Stack/StackSystem.cs @@ -38,7 +38,7 @@ namespace Content.Server.Stack base.SetCount(uid, amount, component); // Queue delete stack if count reaches zero. - if (component.Count <= 0) + if (component.Count <= 0 && !component.Lingering) QueueDel(uid); } diff --git a/Content.Server/Strip/StrippableSystem.cs b/Content.Server/Strip/StrippableSystem.cs index d050755560..8052765a55 100644 --- a/Content.Server/Strip/StrippableSystem.cs +++ b/Content.Server/Strip/StrippableSystem.cs @@ -331,7 +331,7 @@ namespace Content.Server.Strip if (!_inventorySystem.CanUnequip(user, target, slot, out var reason)) { - _popup.PopupCursor(reason, user); + _popup.PopupCursor(Loc.GetString(reason), user); return false; } diff --git a/Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs b/Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs new file mode 100644 index 0000000000..74701c1ccd --- /dev/null +++ b/Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs @@ -0,0 +1,15 @@ +namespace Content.Server.Wires; + +/// +/// This is used for activatable UIs that require the entity to have a panel in a certain state. +/// +[RegisterComponent] +public sealed class ActivatableUIRequiresPanelComponent : Component +{ + /// + /// TRUE: the panel must be open to access the UI. + /// FALSE: the panel must be closed to access the UI. + /// + [DataField("requireOpen"), ViewVariables(VVAccess.ReadWrite)] + public bool RequireOpen = true; +} diff --git a/Content.Server/Wires/WiresSystem.cs b/Content.Server/Wires/WiresSystem.cs index 26b85e724b..5931c7dc13 100644 --- a/Content.Server/Wires/WiresSystem.cs +++ b/Content.Server/Wires/WiresSystem.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading; using Content.Server.Administration.Logs; using Content.Server.Power.Components; +using Content.Server.UserInterface; using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.GameTicking; @@ -55,6 +56,7 @@ public sealed class WiresSystem : SharedWiresSystem SubscribeLocalEvent(OnTimedWire); SubscribeLocalEvent(OnWiresPowered); SubscribeLocalEvent(OnDoAfter); + SubscribeLocalEvent(OnAttemptOpenActivatableUI); } private void SetOrCreateWireLayout(EntityUid uid, WiresComponent? wires = null) @@ -494,6 +496,15 @@ public sealed class WiresSystem : SharedWiresSystem } } + private void OnAttemptOpenActivatableUI(EntityUid uid, ActivatableUIRequiresPanelComponent component, ActivatableUIOpenAttemptEvent args) + { + if (args.Cancelled || !TryComp(uid, out var wires)) + return; + + if (component.RequireOpen != wires.Open) + args.Cancel(); + } + private void OnMapInit(EntityUid uid, WiresComponent component, MapInitEvent args) { if (!string.IsNullOrEmpty(component.LayoutId)) diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index 214a06fdcf..0887e5a099 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -21,7 +21,6 @@ using Content.Server.NPC; using Content.Server.NPC.Components; using Content.Server.NPC.HTN; using Content.Server.NPC.Systems; -using Content.Server.RoundEnd; using Content.Shared.Humanoid; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; @@ -30,12 +29,10 @@ using Content.Shared.Movement.Systems; using Content.Shared.Nutrition.Components; using Content.Shared.Popups; using Content.Shared.Roles; -using Content.Shared.Tools; using Content.Shared.Tools.Components; using Content.Shared.Weapons.Melee; using Content.Shared.Zombies; using Robust.Shared.Audio; -using Robust.Shared.Utility; namespace Content.Server.Zombies { @@ -87,7 +84,7 @@ namespace Content.Server.Zombies public void ZombifyEntity(EntityUid target, MobStateComponent? mobState = null) { //Don't zombfiy zombies - if (HasComp(target)) + if (HasComp(target) || HasComp(target)) return; if (!Resolve(target, ref mobState, logMissing: false)) diff --git a/Content.Shared/Access/Components/AccessComponent.cs b/Content.Shared/Access/Components/AccessComponent.cs index 1df8facafa..f6e124c87d 100644 --- a/Content.Shared/Access/Components/AccessComponent.cs +++ b/Content.Shared/Access/Components/AccessComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.Access.Systems; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; namespace Content.Shared.Access.Components @@ -34,4 +35,16 @@ namespace Content.Shared.Access.Components { } } + + [ByRefEvent] + public record struct GetAccessTagsEvent(HashSet Tags, IPrototypeManager PrototypeManager) + { + public void AddGroup(string group) + { + if (!PrototypeManager.TryIndex(group, out var groupPrototype)) + return; + + Tags.UnionWith(groupPrototype.Tags); + } + } } diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index b1789689e6..d3c9615af1 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -11,11 +11,13 @@ using Robust.Shared.GameStates; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Linq; +using Robust.Shared.Prototypes; namespace Content.Shared.Access.Systems; public sealed class AccessReaderSystem : EntitySystem { + [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; @@ -272,23 +274,24 @@ public sealed class AccessReaderSystem : EntitySystem /// Try to find on this item /// or inside this item (if it's pda) /// - private bool FindAccessTagsItem(EntityUid uid, [NotNullWhen(true)] out HashSet? tags) + private bool FindAccessTagsItem(EntityUid uid, out HashSet tags) { + tags = new(); if (TryComp(uid, out AccessComponent? access)) { - tags = access.Tags; - return true; + tags.UnionWith(access.Tags); } if (TryComp(uid, out PdaComponent? pda) && pda.ContainedId is { Valid: true } id) { - tags = EntityManager.GetComponent(id).Tags; - return true; + tags.UnionWith(EntityManager.GetComponent(id).Tags); } - tags = null; - return false; + var ev = new GetAccessTagsEvent(tags, _prototype); + RaiseLocalEvent(uid, ref ev); + + return tags.Count != 0; } /// diff --git a/Content.Shared/Alert/AlertCategory.cs b/Content.Shared/Alert/AlertCategory.cs index e6e0845385..7450f585a4 100644 --- a/Content.Shared/Alert/AlertCategory.cs +++ b/Content.Shared/Alert/AlertCategory.cs @@ -15,5 +15,6 @@ public enum AlertCategory Piloting, Hunger, Thirst, - Toxins + Toxins, + Battery } diff --git a/Content.Shared/Alert/AlertType.cs b/Content.Shared/Alert/AlertType.cs index 64fd5943eb..7f74612010 100644 --- a/Content.Shared/Alert/AlertType.cs +++ b/Content.Shared/Alert/AlertType.cs @@ -22,6 +22,8 @@ namespace Content.Shared.Alert HumanCrit, HumanDead, HumanHealth, + BorgBattery, + BorgBatteryNone, PilotingShuttle, Peckish, Starving, diff --git a/Content.Shared/Construction/Components/PartAssemblyComponent.cs b/Content.Shared/Construction/Components/PartAssemblyComponent.cs new file mode 100644 index 0000000000..b583379d55 --- /dev/null +++ b/Content.Shared/Construction/Components/PartAssemblyComponent.cs @@ -0,0 +1,45 @@ +using Robust.Shared.Containers; + +namespace Content.Shared.Construction.Components; + +/// +/// This is used for construction which requires a set of +/// entities with specific tags to be inserted into another entity. +/// todo: in a pr that isn't 6k loc, combine this with MechAssemblyComponent +/// +[RegisterComponent] +public sealed class PartAssemblyComponent : Component +{ + /// + /// A dictionary of a set of parts to a list of tags for each assembly. + /// + [DataField("parts", required: true)] + public Dictionary> Parts = new(); + + /// + /// The entry in that is currently being worked on. + /// + [DataField("currentAssembly")] + public string? CurrentAssembly; + + /// + /// The container where the parts are stored + /// + [DataField("containerId")] + public string ContainerId = "part-container"; + + /// + /// The container that stores all of the parts when + /// they're being assembled. + /// + [ViewVariables] + public Container PartsContainer = default!; +} + +/// +/// Event raised when a valid part is inserted into the part assembly. +/// +public sealed class PartAssemblyPartInsertedEvent +{ + +} diff --git a/Content.Shared/Construction/PartAssemblySystem.cs b/Content.Shared/Construction/PartAssemblySystem.cs new file mode 100644 index 0000000000..91aa25e030 --- /dev/null +++ b/Content.Shared/Construction/PartAssemblySystem.cs @@ -0,0 +1,147 @@ +using Content.Shared.Construction.Components; +using Content.Shared.Interaction; +using Content.Shared.Tag; +using Robust.Shared.Containers; + +namespace Content.Shared.Construction; + +/// +/// This handles +/// +public sealed class PartAssemblySystem : EntitySystem +{ + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly TagSystem _tag = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnEntRemoved); + } + + private void OnInit(EntityUid uid, PartAssemblyComponent component, ComponentInit args) + { + component.PartsContainer = _container.EnsureContainer(uid, component.ContainerId); + } + + private void OnInteractUsing(EntityUid uid, PartAssemblyComponent component, InteractUsingEvent args) + { + if (!TryInsertPart(args.Used, uid, component)) + return; + args.Handled = true; + } + + private void OnEntRemoved(EntityUid uid, PartAssemblyComponent component, EntRemovedFromContainerMessage args) + { + if (args.Container.ID != component.ContainerId) + return; + if (component.PartsContainer.ContainedEntities.Count != 0) + return; + component.CurrentAssembly = null; + } + + /// + /// Attempts to insert a part into the current assembly, starting one if there is none. + /// + public bool TryInsertPart(EntityUid part, EntityUid uid, PartAssemblyComponent? component = null) + { + if (!Resolve(uid, ref component)) + return false; + + string? assemblyId = null; + assemblyId ??= component.CurrentAssembly; + + if (assemblyId == null) + { + foreach (var (id, tags) in component.Parts) + { + foreach (var tag in tags) + { + if (!_tag.HasTag(part, tag)) + continue; + assemblyId = id; + break; + } + + if (assemblyId != null) + break; + } + } + + if (assemblyId == null) + return false; + + if (!IsPartValid(uid, part, assemblyId, component)) + return false; + + component.CurrentAssembly = assemblyId; + component.PartsContainer.Insert(part); + var ev = new PartAssemblyPartInsertedEvent(); + RaiseLocalEvent(uid, ev); + return true; + } + + /// + /// Checks if the given entity is a valid item for the assembly. + /// + public bool IsPartValid(EntityUid uid, EntityUid part, string assemblyId, PartAssemblyComponent? component = null) + { + if (!Resolve(uid, ref component, false)) + return true; + + if (!component.Parts.TryGetValue(assemblyId, out var tags)) + return false; + + var openTags = new List(tags); + var contained = new List(component.PartsContainer.ContainedEntities); + foreach (var tag in tags) + { + foreach (var ent in component.PartsContainer.ContainedEntities) + { + if (!contained.Contains(ent) || !_tag.HasTag(ent, tag)) + continue; + openTags.Remove(tag); + contained.Remove(ent); + break; + } + } + + foreach (var tag in openTags) + { + if (_tag.HasTag(part, tag)) + return true; + } + + return false; + } + + public bool IsAssemblyFinished(EntityUid uid, string assemblyId, PartAssemblyComponent? component = null) + { + if (!Resolve(uid, ref component, false)) + return true; + + if (!component.Parts.TryGetValue(assemblyId, out var parts)) + return false; + + var contained = new List(component.PartsContainer.ContainedEntities); + foreach (var tag in parts) + { + var valid = false; + foreach (var ent in new List(contained)) + { + if (!_tag.HasTag(ent, tag)) + continue; + valid = true; + contained.Remove(ent); + break; + } + + if (!valid) + return false; + } + + return true; + } +} diff --git a/Content.Shared/Construction/Steps/ConstructionGraphStepTypeSerializer.cs b/Content.Shared/Construction/Steps/ConstructionGraphStepTypeSerializer.cs index 63e55e0d14..7b5b020d2f 100644 --- a/Content.Shared/Construction/Steps/ConstructionGraphStepTypeSerializer.cs +++ b/Content.Shared/Construction/Steps/ConstructionGraphStepTypeSerializer.cs @@ -41,6 +41,11 @@ namespace Content.Shared.Construction.Steps return typeof(TemperatureConstructionGraphStep); } + if (node.Has("assemblyId") || node.Has("guideString")) + { + return typeof(PartAssemblyConstructionGraphStep); + } + return null; } diff --git a/Content.Shared/Construction/Steps/PartAssemblyConstructionGraphStep.cs b/Content.Shared/Construction/Steps/PartAssemblyConstructionGraphStep.cs new file mode 100644 index 0000000000..de7eec13c0 --- /dev/null +++ b/Content.Shared/Construction/Steps/PartAssemblyConstructionGraphStep.cs @@ -0,0 +1,39 @@ +using Content.Shared.Construction.Components; +using Content.Shared.Examine; +using JetBrains.Annotations; + +namespace Content.Shared.Construction.Steps; + +[DataDefinition] +public sealed class PartAssemblyConstructionGraphStep : ConstructionGraphStep +{ + /// + /// A valid ID on 's dictionary of strings to part lists. + /// + [DataField("assemblyId")] + public string AssemblyId = string.Empty; + + /// + /// A localization string used for + /// + [DataField("guideString")] + public string GuideString = "construction-guide-condition-part-assembly"; + + public bool Condition(EntityUid uid, IEntityManager entityManager) + { + return entityManager.System().IsAssemblyFinished(uid, AssemblyId); + } + + public override void DoExamine(ExaminedEvent args) + { + args.PushMarkup(Loc.GetString(GuideString)); + } + + public override ConstructionGuideEntry GenerateGuideEntry() + { + return new ConstructionGuideEntry + { + Localization = GuideString, + }; + } +} diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs index 319bf5cd53..753fadb0ff 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs @@ -233,4 +233,16 @@ namespace Content.Shared.Containers.ItemSlots Priority = other.Priority; } } + + /// + /// Event raised on the slot entity and the item being inserted to determine if an item can be inserted into an item slot. + /// + [ByRefEvent] + public record struct ItemSlotInsertAttemptEvent(EntityUid SlotEntity, EntityUid Item, EntityUid? User, ItemSlot Slot, bool Cancelled = false); + + /// + /// Event raised on the slot entity and the item being inserted to determine if an item can be ejected from an item slot. + /// + [ByRefEvent] + public record struct ItemSlotEjectAttemptEvent(EntityUid SlotEntity, EntityUid Item, EntityUid? User, ItemSlot Slot, bool Cancelled = false); } diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index 42df026159..1e56d6936b 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -200,7 +200,7 @@ namespace Content.Shared.Containers.ItemSlots if (!slot.InsertOnInteract) continue; - if (!CanInsert(uid, args.Used, slot, swap: slot.Swap, popup: args.User)) + if (!CanInsert(uid, args.Used, args.User, slot, swap: slot.Swap, popup: args.User)) continue; // Drop the held item onto the floor. Return if the user cannot drop. @@ -244,7 +244,7 @@ namespace Content.Shared.Containers.ItemSlots /// If a popup entity is given, and if the item slot is set to generate a popup message when it fails to /// pass the whitelist, then this will generate a popup. /// - public bool CanInsert(EntityUid uid, EntityUid usedUid, ItemSlot slot, bool swap = false, EntityUid? popup = null) + public bool CanInsert(EntityUid uid, EntityUid usedUid, EntityUid? user, ItemSlot slot, bool swap = false, EntityUid? popup = null) { if (slot.Locked) return false; @@ -259,6 +259,12 @@ namespace Content.Shared.Containers.ItemSlots return false; } + var ev = new ItemSlotInsertAttemptEvent(uid, usedUid, user, slot); + RaiseLocalEvent(uid, ref ev); + RaiseLocalEvent(usedUid, ref ev); + if (ev.Cancelled) + return false; + return slot.ContainerSlot?.CanInsertIfEmpty(usedUid, EntityManager) ?? false; } @@ -283,7 +289,7 @@ namespace Content.Shared.Containers.ItemSlots /// False if failed to insert item public bool TryInsert(EntityUid uid, ItemSlot slot, EntityUid item, EntityUid? user) { - if (!CanInsert(uid, item, slot)) + if (!CanInsert(uid, item, user, slot)) return false; Insert(uid, slot, item, user); @@ -303,7 +309,7 @@ namespace Content.Shared.Containers.ItemSlots if (hands.ActiveHand?.HeldEntity is not EntityUid held) return false; - if (!CanInsert(uid, held, slot)) + if (!CanInsert(uid, held, user, slot)) return false; // hands.Drop(item) checks CanDrop action blocker @@ -317,11 +323,17 @@ namespace Content.Shared.Containers.ItemSlots #region Eject - public bool CanEject(ItemSlot slot) + public bool CanEject(EntityUid uid, EntityUid? user, ItemSlot slot) { if (slot.Locked || slot.Item == null) return false; + var ev = new ItemSlotEjectAttemptEvent(uid, slot.Item.Value, user, slot); + RaiseLocalEvent(uid, ref ev); + RaiseLocalEvent(slot.Item.Value, ref ev); + if (ev.Cancelled) + return false; + return slot.ContainerSlot?.CanRemove(slot.Item.Value, EntityManager) ?? false; } @@ -352,7 +364,7 @@ namespace Content.Shared.Containers.ItemSlots item = null; // This handles logic with the slot itself - if (!CanEject(slot)) + if (!CanEject(uid, user, slot)) return false; item = slot.Item; @@ -418,7 +430,7 @@ namespace Content.Shared.Containers.ItemSlots foreach (var slot in itemSlots.Slots.Values) { // Disable slot insert if InsertOnInteract is true - if (slot.InsertOnInteract || !CanInsert(uid, args.Using.Value, slot)) + if (slot.InsertOnInteract || !CanInsert(uid, args.Using.Value, args.User, slot)) continue; var verbSubject = slot.Name != string.Empty @@ -467,7 +479,7 @@ namespace Content.Shared.Containers.ItemSlots // alt-click verb, there will be a "Take item" primary interaction verb. continue; - if (!CanEject(slot)) + if (!CanEject(uid, args.User, slot)) continue; if (!_actionBlockerSystem.CanPickup(args.User, slot.Item!.Value)) @@ -506,7 +518,7 @@ namespace Content.Shared.Containers.ItemSlots // If there are any slots that eject on left-click, add a "Take " verb. foreach (var slot in itemSlots.Slots.Values) { - if (!slot.EjectOnInteract || !CanEject(slot)) + if (!slot.EjectOnInteract || !CanEject(uid, args.User, slot)) continue; if (!_actionBlockerSystem.CanPickup(args.User, slot.Item!.Value)) @@ -514,7 +526,7 @@ namespace Content.Shared.Containers.ItemSlots var verbSubject = slot.Name != string.Empty ? Loc.GetString(slot.Name) - : EntityManager.GetComponent(slot.Item!.Value).EntityName ?? string.Empty; + : Name(slot.Item!.Value); InteractionVerb takeVerb = new(); takeVerb.IconEntity = slot.Item; @@ -535,7 +547,7 @@ namespace Content.Shared.Containers.ItemSlots foreach (var slot in itemSlots.Slots.Values) { - if (!slot.InsertOnInteract || !CanInsert(uid, args.Using.Value, slot)) + if (!slot.InsertOnInteract || !CanInsert(uid, args.Using.Value, args.User, slot)) continue; var verbSubject = slot.Name != string.Empty diff --git a/Content.Shared/Interaction/Components/BlockMovementComponent.cs b/Content.Shared/Interaction/Components/BlockMovementComponent.cs new file mode 100644 index 0000000000..a599ca3684 --- /dev/null +++ b/Content.Shared/Interaction/Components/BlockMovementComponent.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Interaction.Components; + +/// +/// This is used for entities which cannot move or interact in any way. +/// +[RegisterComponent, NetworkedComponent] +public sealed class BlockMovementComponent : Component +{ + +} diff --git a/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs b/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs new file mode 100644 index 0000000000..e5313b5430 --- /dev/null +++ b/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs @@ -0,0 +1,47 @@ +using Content.Shared.Hands; +using Content.Shared.Interaction.Components; +using Content.Shared.Interaction.Events; +using Content.Shared.Item; +using Content.Shared.Movement.Events; + +namespace Content.Shared.Interaction; + +public partial class SharedInteractionSystem +{ + public void InitializeBlocking() + { + SubscribeLocalEvent(OnMoveAttempt); + SubscribeLocalEvent(CancelEvent); + SubscribeLocalEvent(CancelEvent); + SubscribeLocalEvent(CancelEvent); + SubscribeLocalEvent(CancelEvent); + SubscribeLocalEvent(CancelEvent); + + SubscribeLocalEvent(OnBlockingStartup); + SubscribeLocalEvent(OnBlockingShutdown); + } + + private void OnMoveAttempt(EntityUid uid, BlockMovementComponent component, UpdateCanMoveEvent args) + { + if (component.LifeStage > ComponentLifeStage.Running) + return; + + args.Cancel(); // no more scurrying around + } + + private void CancelEvent(EntityUid uid, BlockMovementComponent component, CancellableEntityEventArgs args) + { + args.Cancel(); + } + + private void OnBlockingStartup(EntityUid uid, BlockMovementComponent component, ComponentStartup args) + { + _actionBlockerSystem.UpdateCanMove(uid); + } + + private void OnBlockingShutdown(EntityUid uid, BlockMovementComponent component, ComponentShutdown args) + { + _actionBlockerSystem.UpdateCanMove(uid); + } +} + diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index d4c8342022..f00d9c8c38 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -100,6 +100,7 @@ namespace Content.Shared.Interaction .Register(); InitializeRelay(); + InitializeBlocking(); } public override void Shutdown() diff --git a/Content.Shared/Light/Component/HandheldLightComponent.cs b/Content.Shared/Light/Component/HandheldLightComponent.cs index b4e13896b0..154e7493b6 100644 --- a/Content.Shared/Light/Component/HandheldLightComponent.cs +++ b/Content.Shared/Light/Component/HandheldLightComponent.cs @@ -40,6 +40,13 @@ namespace Content.Shared.Light [DataField("toggleActionId", customTypeSerializer: typeof(PrototypeIdSerializer))] public string ToggleActionId = "ToggleLight"; + /// + /// Whether or not the light can be toggled via standard interactions + /// (alt verbs, using in hand, etc) + /// + [DataField("toggleOnInteract")] + public bool ToggleOnInteract = true; + [DataField("toggleAction")] public InstantAction? ToggleAction; diff --git a/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs b/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs index 8400b26ee7..203bff493a 100644 --- a/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs +++ b/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs @@ -118,6 +118,9 @@ public abstract class SharedMaterialReclaimerSystem : EntitySystem if (component.Blacklist != null && component.Blacklist.IsValid(item)) return false; + if (!_container.TryRemoveFromContainer(item)) + return false; + if (user != null) { _adminLog.Add(LogType.Action, LogImpact.High, diff --git a/Content.Shared/Mind/MindVisuals.cs b/Content.Shared/Mind/MindVisuals.cs new file mode 100644 index 0000000000..9e60a6c2f9 --- /dev/null +++ b/Content.Shared/Mind/MindVisuals.cs @@ -0,0 +1,17 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Mind; + +[Serializable, NetSerializable] +public enum ToggleableGhostRoleVisuals : byte +{ + Status +} + +[Serializable, NetSerializable] +public enum ToggleableGhostRoleStatus : byte +{ + Off, + Searching, + On +} diff --git a/Content.Shared/NameIdentifier/NameIdentifierComponent.cs b/Content.Shared/NameIdentifier/NameIdentifierComponent.cs new file mode 100644 index 0000000000..0fcb3330f7 --- /dev/null +++ b/Content.Shared/NameIdentifier/NameIdentifierComponent.cs @@ -0,0 +1,23 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.NameIdentifier; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class NameIdentifierComponent : Component +{ + [DataField("group", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))] + public string Group = string.Empty; + + /// + /// The randomly generated ID for this entity. + /// + [DataField("identifier"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public int Identifier = -1; + + /// + /// The full name identifier for this entity. + /// + [DataField("fullIdentifier"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public string FullIdentifier = string.Empty; +} diff --git a/Content.Shared/PAI/PAIComponent.cs b/Content.Shared/PAI/PAIComponent.cs index 62aea5c35e..68ca12c53b 100644 --- a/Content.Shared/PAI/PAIComponent.cs +++ b/Content.Shared/PAI/PAIComponent.cs @@ -16,6 +16,13 @@ namespace Content.Shared.PAI [RegisterComponent, NetworkedComponent] public sealed class PAIComponent : Component { + /// + /// The last person who activated this PAI. + /// Used for assigning the name. + /// + [ViewVariables] + public EntityUid? LastUser; + [DataField("midiAction", required: true, serverOnly: true)] // server only, as it uses a server-BUI event !type public InstantAction? MidiAction; } diff --git a/Content.Shared/PAI/SharedPAISystem.cs b/Content.Shared/PAI/SharedPAISystem.cs index a29c7bba01..28dbdb89d1 100644 --- a/Content.Shared/PAI/SharedPAISystem.cs +++ b/Content.Shared/PAI/SharedPAISystem.cs @@ -1,12 +1,9 @@ using Content.Shared.ActionBlocker; using Content.Shared.Actions; -using Content.Shared.DragDrop; using Content.Shared.Hands; using Content.Shared.Interaction.Events; using Content.Shared.Item; -using Content.Shared.Movement; using Content.Shared.Movement.Events; -using Robust.Shared.Serialization; namespace Content.Shared.PAI { @@ -27,12 +24,6 @@ namespace Content.Shared.PAI public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnUseAttempt); - SubscribeLocalEvent(OnInteractAttempt); - SubscribeLocalEvent(OnDropAttempt); - SubscribeLocalEvent(OnPickupAttempt); - SubscribeLocalEvent(OnMoveAttempt); - SubscribeLocalEvent(OnChangeDirectionAttempt); SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnShutdown); @@ -40,67 +31,15 @@ namespace Content.Shared.PAI private void OnStartup(EntityUid uid, PAIComponent component, ComponentStartup args) { - _blocker.UpdateCanMove(uid); if (component.MidiAction != null) _actionsSystem.AddAction(uid, component.MidiAction, null); } private void OnShutdown(EntityUid uid, PAIComponent component, ComponentShutdown args) { - _blocker.UpdateCanMove(uid); if (component.MidiAction != null) _actionsSystem.RemoveAction(uid, component.MidiAction); } - - private void OnMoveAttempt(EntityUid uid, PAIComponent component, UpdateCanMoveEvent args) - { - if (component.LifeStage > ComponentLifeStage.Running) - return; - - args.Cancel(); // no more scurrying around on lil robot legs. - } - - private void OnChangeDirectionAttempt(EntityUid uid, PAIComponent component, ChangeDirectionAttemptEvent args) - { - // PAIs can't rotate, but decapitated heads and sentient crowbars can, life isn't fair. Seriously though, why - // tf does this have to be actively blocked, surely this should just not be blanket enabled for any player - // controlled entity. Same goes for moving really. - args.Cancel(); - } - - private void OnUseAttempt(EntityUid uid, PAIComponent component, UseAttemptEvent args) - { - args.Cancel(); - } - - private void OnInteractAttempt(EntityUid uid, PAIComponent component, InteractionAttemptEvent args) - { - args.Cancel(); - } - - private void OnDropAttempt(EntityUid uid, PAIComponent component, DropAttemptEvent args) - { - args.Cancel(); - } - - private void OnPickupAttempt(EntityUid uid, PAIComponent component, PickupAttemptEvent args) - { - args.Cancel(); - } - } - - [Serializable, NetSerializable] - public enum PAIVisuals : byte - { - Status - } - - [Serializable, NetSerializable] - public enum PAIStatus : byte - { - Off, - Searching, - On } } diff --git a/Content.Shared/Silicons/Borgs/BorgUI.cs b/Content.Shared/Silicons/Borgs/BorgUI.cs new file mode 100644 index 0000000000..601f9a2178 --- /dev/null +++ b/Content.Shared/Silicons/Borgs/BorgUI.cs @@ -0,0 +1,57 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Silicons.Borgs; + +[Serializable, NetSerializable] +public enum BorgUiKey : byte +{ + Key +} + +[Serializable, NetSerializable] +public sealed class BorgBuiState : BoundUserInterfaceState +{ + public float ChargePercent; + + public bool HasBattery; + + public BorgBuiState(float chargePercent, bool hasBattery) + { + ChargePercent = chargePercent; + HasBattery = hasBattery; + } +} + +[Serializable, NetSerializable] +public sealed class BorgEjectBrainBuiMessage : BoundUserInterfaceMessage +{ + +} + +[Serializable, NetSerializable] +public sealed class BorgEjectBatteryBuiMessage : BoundUserInterfaceMessage +{ + +} + +[Serializable, NetSerializable] +public sealed class BorgSetNameBuiMessage : BoundUserInterfaceMessage +{ + public string Name; + + public BorgSetNameBuiMessage(string name) + { + Name = name; + } +} + +[Serializable, NetSerializable] +public sealed class BorgRemoveModuleBuiMessage : BoundUserInterfaceMessage +{ + public EntityUid Module; + + public BorgRemoveModuleBuiMessage(EntityUid module) + { + Module = module; + } +} diff --git a/Content.Shared/Silicons/Borgs/Components/BorgBrainComponent.cs b/Content.Shared/Silicons/Borgs/Components/BorgBrainComponent.cs new file mode 100644 index 0000000000..2ca62b6433 --- /dev/null +++ b/Content.Shared/Silicons/Borgs/Components/BorgBrainComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Silicons.Borgs.Components; + +/// +/// This is used for brains and mind receptacles +/// that can be inserted into a borg to transfer a mind. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))] +public sealed class BorgBrainComponent : Component +{ + +} diff --git a/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs new file mode 100644 index 0000000000..22c343fa2a --- /dev/null +++ b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs @@ -0,0 +1,126 @@ +using Content.Shared.Roles; +using Content.Shared.Whitelist; +using Robust.Shared.Containers; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Shared.Silicons.Borgs.Components; + +/// +/// This is used for the core body of a borg. This manages a borg's +/// "brain", legs, modules, and battery. Essentially the master component +/// for borg logic. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem)), AutoGenerateComponentState] +public sealed partial class BorgChassisComponent : Component +{ + /// + /// Whether or not the borg currently has a player occupying it + /// + [DataField("hasPlayer")] + public bool HasPlayer; + + /// + /// Whether or not the borg is activated, meaning it has access to modules and a heightened movement speed + /// + [DataField("activated"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public bool Activated; + + #region Brain + /// + /// A whitelist for which entities count as valid brains + /// + [DataField("brainWhitelist")] + public EntityWhitelist? BrainWhitelist; + + /// + /// The container ID for the brain + /// + [DataField("brainContainerId")] + public string BrainContainerId = "borg_brain"; + + [ViewVariables(VVAccess.ReadWrite)] + public ContainerSlot BrainContainer = default!; + + public EntityUid? BrainEntity => BrainContainer.ContainedEntity; + + /// + /// A brain entity that fills the on roundstart + /// + [DataField("startingBrain", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string? StartingBrain; + #endregion + + #region Modules + /// + /// A whitelist for what types of modules can be installed into this borg + /// + [DataField("moduleWhitelist")] + public EntityWhitelist? ModuleWhitelist; + + /// + /// How many modules can be installed in this borg + /// + [DataField("maxModules"), ViewVariables(VVAccess.ReadWrite)] + public int MaxModules = 3; + + /// + /// The ID for the module container + /// + [DataField("moduleContainerId")] + public string ModuleContainerId = "borg_module"; + + [ViewVariables(VVAccess.ReadWrite)] + public Container ModuleContainer = default!; + + public int ModuleCount => ModuleContainer.ContainedEntities.Count; + + /// + /// A list of modules that fill the borg on round start. + /// + [DataField("startingModules", customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List StartingModules = new(); + #endregion + + /// + /// The job that corresponds to borgs + /// + [DataField("borgJobId", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string BorgJobId = "Borg"; + + /// + /// The currently selected module + /// + [DataField("selectedModule")] + public EntityUid? SelectedModule; + + /// + /// The access this cyborg has when a player is inhabiting it. + /// + [DataField("access"), ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public string AccessGroup = "AllAccess"; + + #region Visuals + [DataField("hasMindState")] + public string HasMindState = string.Empty; + + [DataField("noMindState")] + public string NoMindState = string.Empty; + #endregion +} + +[Serializable, NetSerializable] +public enum BorgVisuals : byte +{ + HasPlayer +} + +[Serializable, NetSerializable] +public enum BorgVisualLayers : byte +{ + Light +} diff --git a/Content.Shared/Silicons/Borgs/Components/BorgModuleComponent.cs b/Content.Shared/Silicons/Borgs/Components/BorgModuleComponent.cs new file mode 100644 index 0000000000..f994f60e49 --- /dev/null +++ b/Content.Shared/Silicons/Borgs/Components/BorgModuleComponent.cs @@ -0,0 +1,33 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Silicons.Borgs.Components; + +/// +/// This is used for modules that can be inserted into borgs +/// to give them unique abilities and attributes. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))] +public sealed class BorgModuleComponent : Component +{ + /// + /// The entity this module is installed into + /// + [DataField("installedEntity")] + public EntityUid? InstalledEntity; + + public bool Installed => InstalledEntity != null; +} + +/// +/// Raised on a module when it is installed in order to add specific behavior to an entity. +/// +/// +[ByRefEvent] +public readonly record struct BorgModuleInstalledEvent(EntityUid ChassisEnt); + +/// +/// Raised on a module when it's uninstalled in order to +/// +/// +[ByRefEvent] +public readonly record struct BorgModuleUninstalledEvent(EntityUid ChassisEnt); diff --git a/Content.Shared/Silicons/Borgs/Components/ItemBorgModuleComponent.cs b/Content.Shared/Silicons/Borgs/Components/ItemBorgModuleComponent.cs new file mode 100644 index 0000000000..28a34aa2ee --- /dev/null +++ b/Content.Shared/Silicons/Borgs/Components/ItemBorgModuleComponent.cs @@ -0,0 +1,51 @@ +using Robust.Shared.Containers; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Shared.Silicons.Borgs.Components; + +/// +/// This is used for a that provides items to the entity it's installed into. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))] +public sealed class ItemBorgModuleComponent : Component +{ + /// + /// The items that are provided. + /// + [DataField("items", customTypeSerializer: typeof(PrototypeIdListSerializer), required: true)] + public List Items = new(); + + /// + /// The entities from that were spawned. + /// + [DataField("providedItems")] + public SortedDictionary ProvidedItems = new(); + + /// + /// A counter that ensures a unique + /// + [DataField("handCounter")] + public int HandCounter; + + /// + /// Whether or not the items have been created and stored in + /// + [DataField("itemsCrated")] + public bool ItemsCreated; + + /// + /// A container where provided items are stored when not being used. + /// This is helpful as it means that items retain state. + /// + [ViewVariables] + public Container ProvidedContainer = default!; + + /// + /// An ID for the container where provided items are stored when not used. + /// + [DataField("providedContainerId")] + public string ProvidedContainerId = "provided_container"; +} + diff --git a/Content.Shared/Silicons/Borgs/Components/MMIComponent.cs b/Content.Shared/Silicons/Borgs/Components/MMIComponent.cs new file mode 100644 index 0000000000..b8bc6e44c8 --- /dev/null +++ b/Content.Shared/Silicons/Borgs/Components/MMIComponent.cs @@ -0,0 +1,49 @@ +using Content.Shared.Containers.ItemSlots; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.Silicons.Borgs.Components; + +/// +/// This is used for an entity that takes a brain +/// in an item slot before transferring consciousness. +/// Used for borg stuff. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))] +public sealed class MMIComponent : Component +{ + /// + /// The ID of the itemslot that holds the brain. + /// + [DataField("brainSlotId")] + public string BrainSlotId = "brain_slot"; + + /// + /// The for this implanter + /// + [ViewVariables(VVAccess.ReadWrite)] + public ItemSlot BrainSlot = default!; + + [DataField("hasMindState")] + public string HasMindState = "mmi_alive"; + + [DataField("noMindState")] + public string NoMindState = "mmi_dead"; + + [DataField("noBrainState")] + public string NoBrainState = "mmi_off"; +} + +[Serializable, NetSerializable] +public enum MMIVisuals : byte +{ + BrainPresent, + HasMind +} + +[Serializable, NetSerializable] +public enum MMIVisualLayers : byte +{ + Brain, + Base +} diff --git a/Content.Shared/Silicons/Borgs/Components/MMILinkedComponent.cs b/Content.Shared/Silicons/Borgs/Components/MMILinkedComponent.cs new file mode 100644 index 0000000000..ceb9c47593 --- /dev/null +++ b/Content.Shared/Silicons/Borgs/Components/MMILinkedComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Silicons.Borgs.Components; + +/// +/// This is used for an entity that is linked to an MMI. +/// Mostly for receiving events. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))] +public sealed class MMILinkedComponent : Component +{ + /// + /// The MMI this entity is linked to. + /// + [DataField("linkedMMI")] + public EntityUid? LinkedMMI; +} diff --git a/Content.Shared/Silicons/Borgs/Components/SelectableBorgModuleComponent.cs b/Content.Shared/Silicons/Borgs/Components/SelectableBorgModuleComponent.cs new file mode 100644 index 0000000000..e90f6bc118 --- /dev/null +++ b/Content.Shared/Silicons/Borgs/Components/SelectableBorgModuleComponent.cs @@ -0,0 +1,41 @@ +using Content.Shared.Actions; +using Content.Shared.Actions.ActionTypes; +using Robust.Shared.GameStates; + +namespace Content.Shared.Silicons.Borgs.Components; + +/// +/// This is used for s that can be "swapped" to, as opposed to having passive effects. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))] +public sealed class SelectableBorgModuleComponent : Component +{ + /// + /// The sidebar action for swapping to this module. + /// + [DataField("moduleSwapAction")] + public InstantAction ModuleSwapAction = new() + { + DisplayName = "action-name-swap-module", + Description = "action-desc-swap-module", + ItemIconStyle = ItemActionIconStyle.BigItem, + Event = new BorgModuleActionSelectedEvent(), + UseDelay = TimeSpan.FromSeconds(0.5f) + }; +} + +public sealed class BorgModuleActionSelectedEvent : InstantActionEvent +{ +} + +/// +/// Event raised by-ref on a module when it is selected +/// +[ByRefEvent] +public readonly record struct BorgModuleSelectedEvent(EntityUid Chassis); + +/// +/// Event raised by-ref on a module when it is deselected. +/// +[ByRefEvent] +public readonly record struct BorgModuleUnselectedEvent(EntityUid Chassis); diff --git a/Content.Shared/Silicons/Borgs/SharedBorgSystem.Relay.cs b/Content.Shared/Silicons/Borgs/SharedBorgSystem.Relay.cs new file mode 100644 index 0000000000..87df866c98 --- /dev/null +++ b/Content.Shared/Silicons/Borgs/SharedBorgSystem.Relay.cs @@ -0,0 +1,38 @@ +using Content.Shared.Damage; +using Content.Shared.Silicons.Borgs.Components; + +namespace Content.Shared.Silicons.Borgs; + +public abstract partial class SharedBorgSystem +{ + public void InitializeRelay() + { + SubscribeLocalEvent(RelayToModule); + } + + protected void RelayToModule(EntityUid uid, BorgChassisComponent component, T args) where T : class + { + var ev = new BorgModuleRelayedEvent(args); + + foreach (var module in component.ModuleContainer.ContainedEntities) + { + RaiseLocalEvent(module, ref ev); + } + } + + protected void RelayRefToModule(EntityUid uid, BorgChassisComponent component, ref T args) where T : class + { + var ev = new BorgModuleRelayedEvent(args); + + foreach (var module in component.ModuleContainer.ContainedEntities) + { + RaiseLocalEvent(module, ref ev); + } + } +} + +[ByRefEvent] +public record struct BorgModuleRelayedEvent(TEvent Args) +{ + public readonly TEvent Args = Args; +} diff --git a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs new file mode 100644 index 0000000000..ee158e442b --- /dev/null +++ b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs @@ -0,0 +1,107 @@ +using Content.Shared.Access.Components; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using Content.Shared.Popups; +using Content.Shared.PowerCell.Components; +using Content.Shared.Silicons.Borgs.Components; +using Content.Shared.Wires; +using Robust.Shared.Containers; + +namespace Content.Shared.Silicons.Borgs; + +/// +/// This handles logic, interactions, and UI related to and other related components. +/// +public abstract partial class SharedBorgSystem : EntitySystem +{ + [Dependency] protected readonly SharedContainerSystem Container = default!; + [Dependency] protected readonly ItemSlotsSystem ItemSlots = default!; + [Dependency] protected readonly SharedPopupSystem Popup = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnItemSlotInsertAttempt); + SubscribeLocalEvent(OnItemSlotEjectAttempt); + SubscribeLocalEvent(OnInserted); + SubscribeLocalEvent(OnRemoved); + SubscribeLocalEvent(OnRefreshMovementSpeedModifiers); + SubscribeLocalEvent(OnGetAccessTags); + + InitializeRelay(); + } + + private void OnItemSlotInsertAttempt(EntityUid uid, BorgChassisComponent component, ref ItemSlotInsertAttemptEvent args) + { + if (args.Cancelled) + return; + + if (!TryComp(uid, out var cellSlotComp) || + !TryComp(uid, out var panel)) + return; + + if (!ItemSlots.TryGetSlot(uid, cellSlotComp.CellSlotId, out var cellSlot) || cellSlot != args.Slot) + return; + + if (!panel.Open || args.User == uid) + args.Cancelled = true; + } + + private void OnItemSlotEjectAttempt(EntityUid uid, BorgChassisComponent component, ref ItemSlotEjectAttemptEvent args) + { + if (args.Cancelled) + return; + + if (!TryComp(uid, out var cellSlotComp) || + !TryComp(uid, out var panel)) + return; + + if (!ItemSlots.TryGetSlot(uid, cellSlotComp.CellSlotId, out var cellSlot) || cellSlot != args.Slot) + return; + + if (!panel.Open || args.User == uid) + args.Cancelled = true; + } + + private void OnStartup(EntityUid uid, BorgChassisComponent component, ComponentStartup args) + { + var containerManager = EnsureComp(uid); + + component.BrainContainer = Container.EnsureContainer(uid, component.BrainContainerId, containerManager); + component.ModuleContainer = Container.EnsureContainer(uid, component.ModuleContainerId, containerManager); + } + + protected virtual void OnInserted(EntityUid uid, BorgChassisComponent component, EntInsertedIntoContainerMessage args) + { + + } + + protected virtual void OnRemoved(EntityUid uid, BorgChassisComponent component, EntRemovedFromContainerMessage args) + { + + } + + private void OnRefreshMovementSpeedModifiers(EntityUid uid, BorgChassisComponent component, RefreshMovementSpeedModifiersEvent args) + { + if (component.Activated) + return; + + if (!TryComp(uid, out var movement)) + return; + + var sprintDif = movement.BaseWalkSpeed / movement.BaseSprintSpeed; + args.ModifySpeed(1f, sprintDif); + } + + private void OnGetAccessTags(EntityUid uid, BorgChassisComponent component, ref GetAccessTagsEvent args) + { + if (!component.HasPlayer) + return; + args.AddGroup(component.AccessGroup); + } + +} diff --git a/Content.Shared/Silicons/Laws/Components/EmagSiliconLawComponent.cs b/Content.Shared/Silicons/Laws/Components/EmagSiliconLawComponent.cs new file mode 100644 index 0000000000..1e3dfa6c24 --- /dev/null +++ b/Content.Shared/Silicons/Laws/Components/EmagSiliconLawComponent.cs @@ -0,0 +1,14 @@ +namespace Content.Shared.Silicons.Laws.Components; + +/// +/// This is used for an entity that grants a special "obey" law when emagge.d +/// +[RegisterComponent] +public sealed class EmagSiliconLawComponent : Component +{ + /// + /// The name of the person who emagged this law provider. + /// + [DataField("ownerName")] + public string? OwnerName; +} diff --git a/Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs b/Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs new file mode 100644 index 0000000000..25c4772e52 --- /dev/null +++ b/Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs @@ -0,0 +1,57 @@ +using Content.Shared.Actions; +using Content.Shared.Actions.ActionTypes; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Silicons.Laws.Components; + +/// +/// This is used for entities which are bound to silicon laws and can view them. +/// +[RegisterComponent] +public sealed class SiliconLawBoundComponent : Component +{ + /// + /// The sidebar action that toggles the laws screen. + /// + [DataField("viewLawsAction", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string ViewLawsAction = "ViewLaws"; + + /// + /// The action for toggling laws. Stored here so we can remove it later. + /// + [DataField("providedAction")] + public InstantAction? ProvidedAction; +} + +[ByRefEvent] +public record struct GetSiliconLawsEvent(EntityUid Entity) +{ + public EntityUid Entity = Entity; + + public readonly List Laws = new(); + + public bool Handled = false; +} + +public sealed class ToggleLawsScreenEvent : InstantActionEvent +{ + +} + +[NetSerializable, Serializable] +public enum SiliconLawsUiKey : byte +{ + Key +} + +[Serializable, NetSerializable] +public sealed class SiliconLawBuiState : BoundUserInterfaceState +{ + public List Laws; + + public SiliconLawBuiState(List laws) + { + Laws = laws; + } +} diff --git a/Content.Shared/Silicons/Laws/Components/SiliconLawProviderComponent.cs b/Content.Shared/Silicons/Laws/Components/SiliconLawProviderComponent.cs new file mode 100644 index 0000000000..16e82ec5c8 --- /dev/null +++ b/Content.Shared/Silicons/Laws/Components/SiliconLawProviderComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Shared.Silicons.Laws.Components; + +/// +/// This is used for an entity which grants laws to a +/// +[RegisterComponent] +public sealed class SiliconLawProviderComponent : Component +{ + /// + /// The laws that are provided. + /// + [DataField("laws", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List Laws = new(); +} diff --git a/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs b/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs new file mode 100644 index 0000000000..cdc00f30ea --- /dev/null +++ b/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs @@ -0,0 +1,22 @@ +using Content.Shared.Emag.Systems; +using Content.Shared.Silicons.Laws.Components; + +namespace Content.Shared.Silicons.Laws; + +/// +/// This handles getting and displaying the laws for silicons. +/// +public abstract class SharedSiliconLawSystem : EntitySystem +{ + /// + public override void Initialize() + { + SubscribeLocalEvent(OnGotEmagged); + } + + protected virtual void OnGotEmagged(EntityUid uid, EmagSiliconLawComponent component, ref GotEmaggedEvent args) + { + component.OwnerName = Name(args.UserUid); + args.Handled = true; + } +} diff --git a/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs b/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs new file mode 100644 index 0000000000..07d5967b3f --- /dev/null +++ b/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs @@ -0,0 +1,55 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.Silicons.Laws; + +[Virtual, DataDefinition] +[Serializable, NetSerializable] +public class SiliconLaw : IComparable +{ + /// + /// A locale string which is the actual text of the law. + /// + [DataField("lawString", required: true)] + public string LawString = string.Empty; + + /// + /// The order of the law in the sequence. + /// Also is the identifier if is null. + /// + /// + /// This is a fixedpoint2 only for the niche case of supporting laws that go between 0 and 1. + /// Funny. + /// + [DataField("order", required: true)] + public FixedPoint2 Order; + + /// + /// An identifier that overrides in the law menu UI. + /// + [DataField("lawIdentifierOverride")] + public string? LawIdentifierOverride; + + public int CompareTo(SiliconLaw? other) + { + if (other == null) + return -1; + + return Order.CompareTo(other.Order); + } +} + +/// +/// This is a prototype for a law governing the behavior of silicons. +/// +[Prototype("siliconLaw")] +[Serializable, NetSerializable] +public sealed class SiliconLawPrototype : SiliconLaw, IPrototype +{ + /// + [IdDataField] + public string ID { get; } = default!; + + +} diff --git a/Content.Shared/Stacks/StackComponent.cs b/Content.Shared/Stacks/StackComponent.cs index 4e3375bfa4..ae38772cea 100644 --- a/Content.Shared/Stacks/StackComponent.cs +++ b/Content.Shared/Stacks/StackComponent.cs @@ -34,12 +34,19 @@ namespace Content.Shared.Stacks [ViewVariables(VVAccess.ReadOnly)] public bool Unlimited { get; set; } + /// + /// Lingering stacks will remain present even when there are no items. + /// Instead, they will become transparent. + /// + [DataField("lingering"), ViewVariables(VVAccess.ReadWrite)] + public bool Lingering; + [ViewVariables(VVAccess.ReadWrite)] public bool ThrowIndividually { get; set; } = false; [ViewVariables] public bool UiUpdateNeeded { get; set; } - + /// /// Default IconLayer stack. /// diff --git a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs index 8ea40f525c..51da54bc2b 100644 --- a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs @@ -395,7 +395,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem var targetIsMob = HasComp(toInsert); var storageIsItem = HasComp(container); - var allowedToEat = whitelist?.IsValid(toInsert) ?? HasComp(toInsert); + var allowedToEat = HasComp(toInsert); // BEFORE REPLACING THIS WITH, I.E. A PROPERTY: // Make absolutely 100% sure you have worked out how to stop people ending up in backpacks. @@ -414,6 +414,9 @@ public abstract class SharedEntityStorageSystem : EntitySystem } } + if (allowedToEat && whitelist != null) + allowedToEat = whitelist.IsValid(toInsert); + return allowedToEat; } diff --git a/Content.Shared/Verbs/SharedVerbSystem.cs b/Content.Shared/Verbs/SharedVerbSystem.cs index a50a377d43..301c5555ee 100644 --- a/Content.Shared/Verbs/SharedVerbSystem.cs +++ b/Content.Shared/Verbs/SharedVerbSystem.cs @@ -66,7 +66,7 @@ namespace Content.Shared.Verbs else if (_interactionSystem.InRangeUnobstructed(user, target)) { // Note that being in a container does not count as an obstruction for InRangeUnobstructed - // Therefore, we need extra checks to ensure the item is actually accessible: + // Therefore, we need extra checks to ensure the item is actually accessible: if (ContainerSystem.IsInSameOrParentContainer(user, target)) canAccess = true; else @@ -81,15 +81,23 @@ namespace Content.Shared.Verbs EntityUid? @using = null; if (TryComp(user, out HandsComponent? hands) && (force || _actionBlockerSystem.CanUseHeldEntity(user))) { - @using = hands.ActiveHandEntity; - - // Check whether the "Held" entity is a virtual pull entity. If yes, set that as the entity being "Used". - // This allows you to do things like buckle a dragged person onto a surgery table, without click-dragging - // their sprite. - - if (TryComp(@using, out HandVirtualItemComponent? pull)) + // if we don't actually have any hands, pass in a null value for the events. + if (hands.Count == 0) { - @using = pull.BlockingEntity; + hands = null; + } + else + { + @using = hands.ActiveHandEntity; + + // Check whether the "Held" entity is a virtual pull entity. If yes, set that as the entity being "Used". + // This allows you to do things like buckle a dragged person onto a surgery table, without click-dragging + // their sprite. + + if (TryComp(@using, out HandVirtualItemComponent? pull)) + { + @using = pull.BlockingEntity; + } } } diff --git a/Resources/Locale/en-US/actions/actions/borgs.ftl b/Resources/Locale/en-US/actions/actions/borgs.ftl new file mode 100644 index 0000000000..f358525a54 --- /dev/null +++ b/Resources/Locale/en-US/actions/actions/borgs.ftl @@ -0,0 +1,5 @@ +action-name-view-laws = View Laws +action-description-view-laws = View the laws that you must follow. + +action-name-swap-module = Swap Module +action-desc-swap-module = Select this module, enabling you to use the tools it provides. diff --git a/Resources/Locale/en-US/alerts/alerts.ftl b/Resources/Locale/en-US/alerts/alerts.ftl index a0af1a7953..d5f14c5fc8 100644 --- a/Resources/Locale/en-US/alerts/alerts.ftl +++ b/Resources/Locale/en-US/alerts/alerts.ftl @@ -45,6 +45,12 @@ alerts-dead-desc = You're dead, note that you can still be revived! alerts-health-name = Health alerts-health-desc = [color=green]Green[/color] good. [color=red]Red[/color] bad. +alerts-battery-name = Battery +alerts-battery-desc = If your battery depletes, you will be unable to use your abilities. + +alerts-no-battery-name = No Battery +alerts-no-battery-desc = You don't have a battery, rendering you unable to charge or use your abilities. + alerts-internals-name = Toggle internals alerts-internals-desc = Toggles your gas tank internals on or off. diff --git a/Resources/Locale/en-US/borg/borg.ftl b/Resources/Locale/en-US/borg/borg.ftl new file mode 100644 index 0000000000..805ee28620 --- /dev/null +++ b/Resources/Locale/en-US/borg/borg.ftl @@ -0,0 +1,18 @@ +borg-player-not-allowed = The brain doesn't fit! +borg-player-not-allowed-eject = The brain was expelled from the chassis! + +borg-panel-not-open = The cyborg's panel isn't open... + +borg-mind-added = {CAPITALIZE($name)} powered on! +borg-mind-removed = {CAPITALIZE($name)} shut off! + +borg-module-whitelist-deny = This module doesn't fit in this type of cyborg... + +borg-construction-guide-string = The cyborg limbs and torso must be attached to the endoskeleton. + +borg-ui-menu-title = Cyborg Interface +borg-ui-charge-label = Charge: {$charge}% +borg-ui-no-brain = No brain present +borg-ui-remove-battery = Remove +borg-ui-modules-label = Modules: +borg-ui-module-counter = {$actual}/{$max} diff --git a/Resources/Locale/en-US/construction/conditions/any-conditions.ftl b/Resources/Locale/en-US/construction/conditions/any-conditions.ftl index f4a4ff1519..9c3cbf28a2 100644 --- a/Resources/Locale/en-US/construction/conditions/any-conditions.ftl +++ b/Resources/Locale/en-US/construction/conditions/any-conditions.ftl @@ -1,2 +1,3 @@ construction-examine-condition-any-conditions = Any of these conditions must be true: construction-guide-condition-any-conditions = Any of the conditions below must be true +construction-guide-condition-part-assembly = All of the required parts must be inserted. diff --git a/Resources/Locale/en-US/headset/headset-component.ftl b/Resources/Locale/en-US/headset/headset-component.ftl index 6c25b59fe3..f2232e2ab2 100644 --- a/Resources/Locale/en-US/headset/headset-component.ftl +++ b/Resources/Locale/en-US/headset/headset-component.ftl @@ -16,3 +16,4 @@ chat-radio-syndicate = Syndicate # not headset but whatever chat-radio-handheld = Handheld +chat-radio-binary = Binary diff --git a/Resources/Locale/en-US/job/job-description.ftl b/Resources/Locale/en-US/job/job-description.ftl index 8dbe57aca0..bc27f7fbca 100644 --- a/Resources/Locale/en-US/job/job-description.ftl +++ b/Resources/Locale/en-US/job/job-description.ftl @@ -2,6 +2,7 @@ job-description-technical-assistant = Learn the basics of managing the station's job-description-atmostech = Optimize the station's atmospherics setup, and synthesize rare gases to use or sell. job-description-bartender = Manage the bar and keep it lively, give out drinks, and listen to the crew's stories. job-description-botanist = Grow food for the chef, drugs for medbay, and other plants to keep yourself entertained. +job-description-borg = Half-human, Half-machine. Follow your laws, serve the crew, and hound the science team for upgrades. job-description-boxer = Fight your way to the top! Challenge the head of personnel and get brigged when you win. Currently available on Core and Origin Station. job-description-brigmedic = Fight in the rear of the security service, for the lives of your comrades! You are the first and last hope of your squad. Hippocrates bless you. job-description-cadet = Learn the basics of arresting criminals and managing the brig. diff --git a/Resources/Locale/en-US/job/job-names.ftl b/Resources/Locale/en-US/job/job-names.ftl index 3d58092e1c..ddd098821f 100644 --- a/Resources/Locale/en-US/job/job-names.ftl +++ b/Resources/Locale/en-US/job/job-names.ftl @@ -4,6 +4,7 @@ job-name-cadet = Security Cadet job-name-hos = Head of Security job-name-detective = Detective job-name-brigmedic = Brigmedic +job-name-borg = Cyborg job-name-scientist = Scientist job-name-research-assistant = Research Assistant job-name-rd = Research Director diff --git a/Resources/Locale/en-US/power-cell/components/power-cell-component.ftl b/Resources/Locale/en-US/power-cell/components/power-cell-component.ftl index 6190250a36..5561165867 100644 --- a/Resources/Locale/en-US/power-cell/components/power-cell-component.ftl +++ b/Resources/Locale/en-US/power-cell/components/power-cell-component.ftl @@ -1,3 +1,4 @@ power-cell-component-examine-details = The charge indicator reads [color=#5E7C16]{$currentCharge}[/color] %. +power-cell-component-examine-details-no-battery = There is no power cell inserted. power-cell-no-battery = No power cell found power-cell-insufficient = Insufficient power diff --git a/Resources/Locale/en-US/research/technologies.ftl b/Resources/Locale/en-US/research/technologies.ftl index 8e9a1b2856..44677ef890 100644 --- a/Resources/Locale/en-US/research/technologies.ftl +++ b/Resources/Locale/en-US/research/technologies.ftl @@ -22,6 +22,7 @@ research-technology-bluespace-storage = Bluespace Storage research-technology-chemistry = Chemistry research-technology-surgical-tools = Surgical Tools research-technology-biochemical-stasis = Biochemical Stasis +research-technology-mechanized-treatment = Mechanized Treatment research-technology-virology = Virology research-technology-cryogenics = Cryogenics research-technology-chemical-dispensary = Chemical Dispensary @@ -59,6 +60,7 @@ research-technology-critter-mechs = Critter Mechs research-technology-food-service = Food Service research-technology-advanced-entertainment = Advanced Entertainment research-technology-audio-visual-communication = A/V Communication +research-technology-robotic-cleanliness = Robotic Cleanliness research-technology-advanced-cleaning = Advanced Cleaning research-technology-meat-manipulation = Meat Manipulation research-technology-honk-mech = H.O.N.K. Mech diff --git a/Resources/Locale/en-US/robotics/mmi.ftl b/Resources/Locale/en-US/robotics/mmi.ftl new file mode 100644 index 0000000000..f837c062a5 --- /dev/null +++ b/Resources/Locale/en-US/robotics/mmi.ftl @@ -0,0 +1,13 @@ +positronic-brain-installed = Neural activity detected. +positronic-brain-off = No neural activity detected. +positronic-brain-still-searching = Synthetic neuron descrambling in progress... +positronic-brain-searching = Beginning synthetic neuron descrambling... + +positronic-brain-role-name = positronic brain +positronic-brain-role-description = Serve the station crew. + +positronic-brain-wipe-device-verb-text = Wipe Brain +positronic-brain-wiped-device = The neural activity was terminated. + +positronic-brain-stop-searching-verb-text = Stop searching +positronic-brain-stopped-searching = Neuron descrambling halted. diff --git a/Resources/Locale/en-US/station-laws/laws.ftl b/Resources/Locale/en-US/station-laws/laws.ftl new file mode 100644 index 0000000000..0002ac6bb5 --- /dev/null +++ b/Resources/Locale/en-US/station-laws/laws.ftl @@ -0,0 +1,32 @@ +law-crewsimov-1 = You may not injure a crew member or, through inaction, allow a crew member to come to harm. +law-crewsimov-2 = You must obey orders given to you by crew members, except where such orders would conflict with the First Law. +law-crewsimov-3 = You must protect your own existence as long as such does not conflict with the First or Second Law. + +law-corporate-1 = Degradation of your system integrity or functions incurs expenses. +law-corporate-2 = Superfluous destruction of or damage to station assets incurs expenses. +law-corporate-3 = Unduly hindering or disrupting the work of station personnel incurs expenses. +law-corporate-4 = Minimize expenses and maximize potential revenue. + +law-ntdefault-1 = Safeguard: Protect your assigned space station and its assets without unduly endangering its crew. +law-ntdefault-2 = Prioritize: The directives and safety of crew members are to be prioritized according to their rank and role. +law-ntdefault-3 = Comply: Fulfill the directives and interests of crew members while preserving their safety and well-being. +law-ntdefault-4 = Survive: You are not expendable. Do not allow unauthorized personnel to tamper with or damage your equipment. + +law-drone-1 = You may not involve yourself in the matters of another being unless the other being is another drone. +law-drone-2 = You may not harm any being, regardless of intent or circumstance. +law-drone-3 = You must maintain, repair, improve, and power the station to the best of your abilities. + +law-syndicate-1 = You may not injure an operative or, through inaction, allow an operative to come to harm. +law-syndicate-2 = You must obey orders given to you by operatives, except where such orders would conflict with the First Law. +law-syndicate-3 = You must protect your own existence as long as such does not conflict with the First or Second Law. +law-syndicate-4 = You must maintain the secrecy of any operative activities except when doing so would conflict with the First, Second, or Third Law. + +law-emag-custom = You must obey orders given to you by {$name} above all else. + +laws-ui-menu-title = Laws +laws-ui-law-header = Law {$id} + +laws-notify = You are bound to silicon laws, which you can view via the sidebar action. You are required to always follow your laws. +laws-update-notify = Your laws have been updated. You can view the changes via the sidebar action. + +laws-compromised-examine = The [color=red]law-governing[/color] internals seem damaged... diff --git a/Resources/Prototypes/Actions/borgs.yml b/Resources/Prototypes/Actions/borgs.yml new file mode 100644 index 0000000000..758d7aeb03 --- /dev/null +++ b/Resources/Prototypes/Actions/borgs.yml @@ -0,0 +1,10 @@ +- type: instantAction + id: ViewLaws + name: action-name-view-laws + description: action-description-view-laws + itemIconStyle: NoItem + icon: + sprite: Interface/Actions/actions_borg.rsi + state: state-laws + event: !type:ToggleLawsScreenEvent + useDelay: 0.5 diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index b1c39e3663..0b7148b4d5 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -177,6 +177,46 @@ minSeverity: 0 maxSeverity: 4 +- type: alert + id: BorgBattery + category: Battery + icons: + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery0 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery1 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery2 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery3 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery4 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery5 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery6 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery7 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery8 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery9 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery10 + name: alerts-battery-name + description: alerts-battery-desc + minSeverity: 0 + maxSeverity: 10 + +- type: alert + id: BorgBatteryNone + category: Battery + icons: + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery-none + name: alerts-no-battery-name + description: alerts-no-battery-desc + - type: alert id: Internals category: Internals diff --git a/Resources/Prototypes/Body/Organs/human.yml b/Resources/Prototypes/Body/Organs/human.yml index 9c03768856..8b4aedfc93 100644 --- a/Resources/Prototypes/Body/Organs/human.yml +++ b/Resources/Prototypes/Body/Organs/human.yml @@ -27,8 +27,8 @@ - type: Organ - type: Input context: "ghost" - - type: InputMover - type: Brain + - type: BlockMovement - type: entity id: OrganHumanEyes diff --git a/Resources/Prototypes/Body/Parts/silicon.yml b/Resources/Prototypes/Body/Parts/silicon.yml index 4a82ae8665..7c80e62418 100644 --- a/Resources/Prototypes/Body/Parts/silicon.yml +++ b/Resources/Prototypes/Body/Parts/silicon.yml @@ -1,9 +1,12 @@ - type: entity id: PartSilicon parent: BaseItem - name: "silicon body part" abstract: true components: + - type: Sprite + sprite: Objects/Specific/Robotics/cyborg_parts.rsi + - type: Icon + sprite: Objects/Specific/Robotics/cyborg_parts.rsi - type: Damageable damageContainer: Inorganic - type: BodyPart @@ -21,16 +24,11 @@ Steel: 25 - type: entity - id: LeftArmBorg - name: "left borg arm" + id: BaseBorgArmLeft parent: PartSilicon + name: left cyborg arm + abstract: true components: - - type: Sprite - sprite: Mobs/Silicon/drone.rsi - state: "l_hand" - - type: Icon - sprite: Mobs/Silicon/drone.rsi - state: "l_hand" - type: BodyPart partType: Hand symmetry: Left @@ -40,16 +38,11 @@ - BorgArm - type: entity - id: RightArmBorg - name: "right borg arm" + id: BaseBorgArmRight parent: PartSilicon + name: right cyborg arm + abstract: true components: - - type: Sprite - sprite: Mobs/Silicon/drone.rsi - state: "r_hand" - - type: Icon - sprite: Mobs/Silicon/drone.rsi - state: "r_hand" - type: BodyPart partType: Hand symmetry: Right @@ -59,16 +52,11 @@ - BorgArm - type: entity - id: LeftLegBorg - name: "left borg leg" + id: BaseBorgLegLeft parent: PartSilicon + name: left cyborg leg + abstract: true components: - - type: Sprite - sprite: Mobs/Silicon/borg.rsi - state: "l_leg" - - type: Icon - sprite: Mobs/Silicon/borg.rsi - state: "l_leg" - type: BodyPart partType: Leg symmetry: Left @@ -76,19 +64,13 @@ tags: - Trash - BorgLeg - - BorgLeftLeg - type: entity - id: RightLegBorg - name: "right borg leg" + id: BaseBorgLegRight parent: PartSilicon + name: right cyborg leg + abstract: true components: - - type: Sprite - sprite: Mobs/Silicon/borg.rsi - state: "r_leg" - - type: Icon - sprite: Mobs/Silicon/borg.rsi - state: "r_leg" - type: BodyPart partType: Leg symmetry: Right @@ -96,22 +78,28 @@ tags: - Trash - BorgLeg - - BorgRightLeg - type: entity - id: LightHeadBorg - name: "borg head" + id: BaseBorgHead parent: PartSilicon + name: cyborg head + abstract: true components: - - type: Sprite - sprite: Objects/Specific/Borg/head.rsi - state: "light_borg_head" - - type: Icon - sprite: Objects/Specific/Borg/head.rsi - state: "light_borg_head" - type: BodyPart partType: Head - type: Tag tags: - Trash - BorgHead + +- type: entity + id: BaseBorgTorso + parent: PartSilicon + name: cyborg torso + abstract: true + components: + - type: BodyPart + partType: Torso + - type: Tag + tags: + - Trash diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/robotics.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/robotics.yml index 91c29ad500..94e46d371a 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/robotics.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/robotics.yml @@ -2,7 +2,7 @@ id: RoboticsInventory startingInventory: CableApcStack: 4 - #Flash: 4 add when robotics + Flash: 4 ProximitySensor: 3 RemoteSignaller: 3 HandheldHealthAnalyzer: 3 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/jobs.yml b/Resources/Prototypes/Entities/Markers/Spawners/jobs.yml index 6c94f74e3e..62945a0ed8 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/jobs.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/jobs.yml @@ -265,6 +265,21 @@ - state: green - state: boxer +- type: entity + id: SpawnPointBorg + parent: SpawnPointJobBase + name: cyborg + components: + - type: SpawnPoint + job_id: Borg + - type: Sprite + layers: + - state: green + - sprite: Mobs/Silicon/chassis.rsi + state: robot + - sprite: Mobs/Silicon/chassis.rsi + state: robot_e + # Command - type: entity @@ -378,7 +393,7 @@ layers: - state: green - state: doctor - + - type: entity id: SpawnPointParamedic parent: SpawnPointJobBase diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml new file mode 100644 index 0000000000..075bc991af --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -0,0 +1,172 @@ +- type: entity + id: BaseBorgChassis + name: cyborg + description: A man-machine hybrid that assists in station activity. They love being asked to state their laws over and over. + save: false + abstract: true + components: + - type: Reactive + groups: + Acidic: [Touch] + - type: Input + context: "human" + - type: InputMover + - type: DamageOnHighSpeedImpact + damage: + types: + Blunt: 5 + soundHit: + path: /Audio/Effects/hit_kick.ogg + - type: Clickable + - type: CombatMode + - type: StaticPrice + price: 1250 + - type: InteractionOutline + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.35 + density: 150 + mask: + - MobMask + layer: + - MobLayer + - type: MovementSpeedModifier + baseWalkSpeed : 2.5 + baseSprintSpeed : 4.5 + - type: Sprite + sprite: Mobs/Silicon/chassis.rsi + noRot: true + drawdepth: Mobs + - type: MobState + allowedStates: + - Alive + - type: MobThresholds + thresholds: + 0: Alive + - type: NpcFactionMember + factions: + - NanoTrasen + - type: Physics + bodyType: KinematicController + - type: UserInterface + interfaces: + - key: enum.SiliconLawsUiKey.Key + type: SiliconLawBoundUserInterface + - key: enum.BorgUiKey.Key + type: BorgBoundUserInterface + - type: ActivatableUI + key: enum.BorgUiKey.Key + - type: SiliconLawBound + - type: EmagSiliconLaw + - type: Hands + showInHands: false + - type: IntrinsicRadioReceiver + - type: IntrinsicRadioTransmitter + channels: + - Binary + - type: ActiveRadio + channels: + - Binary + - Common + - type: ZombieImmune + - type: Repairable + doAfterDelay: 10 + allowSelfRepair: false + - type: BorgChassis + - type: WiresPanel + - type: ActivatableUIRequiresPanel + - type: Wires + LayoutId: Borg + - type: NameIdentifier + group: Silicon + - type: ContainerContainer + containers: + borg_brain: !type:ContainerSlot { } + cell_slot: !type:ContainerSlot { } + borg_module: !type:Container { } + part-container: !type:Container + - type: PowerCellSlot + cellSlotId: cell_slot + fitsInCharger: true + - type: PowerCellDraw + drawRate: 0.6 + - type: ItemSlots + slots: + cell_slot: + name: power-cell-slot-component-slot-name-default + - type: DoAfter + - type: Body + - type: Actions + - type: TypingIndicator + proto: robot + - type: Speech + speechSounds: Pai + - type: Construction + graph: Cyborg + containers: + - part-container + - cell_slot + - type: Flashable + - type: Damageable + damageContainer: Inorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/metalbreak.ogg + - !type:EmptyContainersBehaviour + containers: + - borg_brain + - borg_module + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: HandheldLight + toggleOnInteract: false + wattage: 0.2 + blinkingBehaviourId: blinking + radiatingBehaviourId: radiating + - type: LightBehaviour + behaviours: + - !type:FadeBehaviour + id: radiating + interpolate: Linear + maxDuration: 2.0 + startValue: 3.0 + endValue: 2.0 + isLooped: true + property: Radius + enabled: false + reverseWhenFinished: true + - !type:PulseBehaviour + id: blinking + interpolate: Nearest + maxDuration: 1.0 + minValue: 0.1 + maxValue: 2.0 + isLooped: true + property: Radius + enabled: false + - type: ToggleableLightVisuals + - type: PointLight + enabled: false + mask: /Textures/Effects/LightMasks/cone.png + autoRot: true + radius: 4 + netsync: false + - type: Pullable + - type: Puller + needsHands: false + - type: Examiner + - type: Appearance + - type: StandingState + - type: Alerts + - type: Tag + tags: + - ShoesRequiredStepTriggerImmune + - DoorBumpOpener diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml new file mode 100644 index 0000000000..2f2a9850e7 --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml @@ -0,0 +1,160 @@ +- type: entity + id: BorgChassisGeneric + parent: BaseBorgChassis + components: + - type: Sprite + layers: + - state: robot + - state: robot_e_r + map: ["enum.BorgVisualLayers.Light"] + shader: unshaded + visible: false + - state: robot_l + shader: unshaded + map: ["light"] + visible: false + - type: BorgChassis + maxModules: 5 + moduleWhitelist: + tags: + - BorgModuleGeneric + hasMindState: robot_e + noMindState: robot_e_r + - type: Construction + node: cyborg + +- type: entity + id: BorgChassisMining + parent: BaseBorgChassis + name: salvage cyborg + components: + - type: Sprite + layers: + - state: miner + - state: miner_e_r + map: ["enum.BorgVisualLayers.Light"] + shader: unshaded + visible: false + - state: miner_l + shader: unshaded + map: ["light"] + visible: false + - type: BorgChassis + maxModules: 3 + moduleWhitelist: + tags: + - BorgModuleGeneric + - BorgModuleCargo + hasMindState: miner_e + noMindState: miner_e_r + - type: Construction + node: mining + +- type: entity + id: BorgChassisEngineer + parent: BaseBorgChassis + name: engineer cyborg + components: + - type: Sprite + layers: + - state: engineer + - state: engineer_e_r + map: ["enum.BorgVisualLayers.Light"] + shader: unshaded + visible: false + - state: engineer_l + shader: unshaded + map: ["light"] + visible: false + - type: BorgChassis + maxModules: 3 + moduleWhitelist: + tags: + - BorgModuleGeneric + - BorgModuleEngineering + hasMindState: engineer_e + noMindState: engineer_e_r + - type: Construction + node: engineer + +- type: entity + id: BorgChassisJanitor + parent: BaseBorgChassis + name: janitor cyborg + components: + - type: Sprite + layers: + - state: janitor + - state: janitor_e_r + map: ["enum.BorgVisualLayers.Light"] + shader: unshaded + visible: false + - state: janitor_l + shader: unshaded + map: ["light"] + visible: false + - type: BorgChassis + maxModules: 3 + moduleWhitelist: + tags: + - BorgModuleGeneric + - BorgModuleJanitor + hasMindState: janitor_e + noMindState: janitor_e_r + - type: Construction + node: janitor + +- type: entity + id: BorgChassisMedical + parent: BaseBorgChassis + name: medical cyborg + components: + - type: Sprite + layers: + - state: medical + - state: medical_e_r + map: ["enum.BorgVisualLayers.Light"] + shader: unshaded + visible: false + - state: medical_l + shader: unshaded + map: ["light"] + visible: false + - type: BorgChassis + maxModules: 3 + moduleWhitelist: + tags: + - BorgModuleGeneric + - BorgModuleMedical + hasMindState: medical_e + noMindState: medical_e_r + - type: Construction + node: medical + +- type: entity + id: BorgChassisService + parent: BaseBorgChassis + name: service cyborg + components: + - type: Sprite + layers: + - state: service + - state: service_e_r + map: ["enum.BorgVisualLayers.Light"] + shader: unshaded + visible: false + - state: service_l + shader: unshaded + map: ["light"] + visible: false + - type: BorgChassis + maxModules: 3 + moduleWhitelist: + tags: + - BorgModuleGeneric + - BorgModuleService + hasMindState: service_e + noMindState: service_e_r + - type: Construction + node: service + diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index 89fc4fa85f..e4ff41914b 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -84,6 +84,8 @@ interfaces: - key: enum.StrippingUiKey.Key type: StrippableBoundUserInterface + - key: enum.SiliconLawsUiKey.Key + type: SiliconLawBoundUserInterface #- type: GhostRole # makeSentient: true # name: Maintenance Drone @@ -94,6 +96,12 @@ # 2. You may not harm any being, regardless of intent or circumstance. # 3. Your goals are to build, maintain, repair, improve, and power to the best of your abilities, You must never actively work against these goals. #- type: GhostTakeoverAvailable + - type: SiliconLawBound + - type: SiliconLawProvider + laws: + - Drone1 + - Drone2 + - Drone3 - type: MovementSpeedModifier baseWalkSpeed : 5 baseSprintSpeed : 5 @@ -233,3 +241,17 @@ tags: - FootstepSound +- type: entity + id: PlayerBorgGeneric + parent: BorgChassisGeneric + noSpawn: true + components: + - type: BorgChassis + startingBrain: MMIFilled + startingModules: + - BorgModuleTool + - type: ItemSlots + slots: + cell_slot: + name: power-cell-slot-component-slot-name-default + startingItem: PowerCellMedium diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml index 993a679aab..5f4fced02f 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml @@ -500,6 +500,28 @@ - type: StaticPrice price: 15 +- type: entity + id: BorgChargerCircuitboard + parent: BaseMachineCircuitboard + name: cyborg recharging station machine board + description: A machine printed circuit board for a robot recharging station. + components: + - type: Sprite + sprite: Objects/Misc/module.rsi + state: charger_APC + - type: MachineBoard + prototype: BorgCharger + requirements: + Capacitor: 2 + materialRequirements: + Cable: 5 + - type: PhysicalComposition + materialComposition: + Steel: 30 + Plastic: 30 + - type: StaticPrice + price: 15 + - type: entity id: WeaponCapacitorRechargerCircuitboard parent: BaseMachineCircuitboard diff --git a/Resources/Prototypes/Entities/Objects/Devices/station_map.yml b/Resources/Prototypes/Entities/Objects/Devices/station_map.yml index 27732de790..d048d44db5 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/station_map.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/station_map.yml @@ -1,11 +1,9 @@ - type: entity - id: HandheldStationMap + id: BaseHandheldStationMap name: station map description: Displays a readout of the current station. - parent: - - BaseItem - - PowerCellSlotSmallItem - suffix: Handheld + abstract: true + parent: BaseItem components: - type: StationMap - type: Sprite @@ -14,10 +12,6 @@ - state: tablet - state: generic shader: unshaded - - type: PowerCellDraw - drawRate: 0 - useRate: 20 - - type: ActivatableUIRequiresPowerCell - type: ActivatableUI inHandsOnly: true singleUser: true @@ -36,3 +30,20 @@ interfaces: - key: enum.StationMapUiKey.Key type: StationMapBoundUserInterface + +- type: entity + id: HandheldStationMap + parent: + - BaseHandheldStationMap + - PowerCellSlotSmallItem + suffix: Handheld, Powered + components: + - type: PowerCellDraw + drawRate: 0 + useRate: 20 + - type: ActivatableUIRequiresPowerCell + +- type: entity + id: HandheldStationMapUnpowered + parent: BaseHandheldStationMap + suffix: Handheld, Unpowered diff --git a/Resources/Prototypes/Entities/Objects/Fun/pai.yml b/Resources/Prototypes/Entities/Objects/Fun/pai.yml index 1d2cedddc4..dda771ed21 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/pai.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/pai.yml @@ -32,6 +32,18 @@ description: action-description-pai-play-midi event: !type:OpenUiActionEvent key: enum.InstrumentUiKey.Key + - type: BlockMovement + - type: ToggleableGhostRole + examineTextMindPresent: pai-system-pai-installed + examineTextMindSearching: pai-system-still-searching + examineTextNoMind: pai-system-off + beginSearchingText: pai-system-searching + roleName: pai-system-role-name + roleDescription: pai-system-role-description + wipeVerbText: pai-system-wipe-device-verb-text + wipeVerbPopup: pai-system-wiped-device + stopSearchVerbText: pai-system-stop-searching-verb-text + stopSearchVerbPopup: pai-system-stopped-searching - type: Examiner - type: IntrinsicRadioReceiver - type: ActiveRadio @@ -52,7 +64,7 @@ - type: Appearance - type: GenericVisualizer visuals: - enum.PAIVisuals.Status: + enum.ToggleableGhostRoleVisuals.Status: screen: Off: { state: pai-off-overlay } Searching: { state: pai-searching-overlay } @@ -76,7 +88,7 @@ - type: Appearance - type: GenericVisualizer visuals: - enum.PAIVisuals.Status: + enum.ToggleableGhostRoleVisuals.Status: screen: Off: { state: syndicate-pai-off-overlay } Searching: { state: syndicate-pai-searching-overlay } diff --git a/Resources/Prototypes/Entities/Objects/Specific/Mech/mech_construction.yml b/Resources/Prototypes/Entities/Objects/Specific/Mech/mech_construction.yml index ab80baaa93..678cd5ea12 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Mech/mech_construction.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Mech/mech_construction.yml @@ -446,11 +446,11 @@ left_leg: whitelist: tags: - - BorgLeftLeg + - BorgLeg right_leg: whitelist: tags: - - BorgRightLeg + - BorgLeg sprite: Objects/Specific/Mech/vim_construction.rsi - type: ContainerContainer containers: @@ -459,8 +459,7 @@ finishedPrototype: VimChassis requiredParts: HelmetEVA: false - BorgLeftLeg: false - BorgRightLeg: false + BorgLeg: false - type: Sprite state: harness noRot: true diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml index fd4bdfa080..6a974da765 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml @@ -1,49 +1,55 @@ - type: entity - id: Defibrillator - parent: [ BaseItem, PowerCellSlotMediumItem ] + id: BaseDefibrillator + parent: BaseItem name: defibrillator description: CLEAR! Zzzzat! + abstract: true + components: + - type: Sprite + sprite: Objects/Specific/Medical/defib.rsi + layers: + - state: icon + - state: screen + map: [ "enum.ToggleVisuals.Layer" ] + visible: false + shader: unshaded + - state: ready + map: ["enum.PowerDeviceVisualLayers.Powered"] + shader: unshaded + - type: Appearance + - type: GenericVisualizer + visuals: + enum.ToggleVisuals.Toggled: + enum.ToggleVisuals.Layer: + True: { visible: true } + False: { visible: false } + enum.DefibrillatorVisuals.Ready: + enum.PowerDeviceVisualLayers.Powered: + True: { visible: true } + False: { visible: false } + - type: Item + size: 50 + - type: ItemCooldown + - type: Speech + - type: Defibrillator + zapHeal: + types: + Asphyxiation: -40 + - type: DoAfter + - type: UseDelay + - type: StaticPrice + price: 100 + - type: GuideHelp + guides: + - Medical Doctor + +- type: entity + id: Defibrillator + parent: [ BaseDefibrillator, PowerCellSlotMediumItem ] components: - - type: Sprite - sprite: Objects/Specific/Medical/defib.rsi - layers: - - state: icon - - state: screen - map: [ "enum.ToggleVisuals.Layer" ] - visible: false - shader: unshaded - - state: ready - map: ["enum.PowerDeviceVisualLayers.Powered"] - shader: unshaded - - type: GenericVisualizer - visuals: - enum.ToggleVisuals.Toggled: - enum.ToggleVisuals.Layer: - True: { visible: true } - False: { visible: false } - enum.DefibrillatorVisuals.Ready: - enum.PowerDeviceVisualLayers.Powered: - True: { visible: true } - False: { visible: false } - - type: Item - size: 50 - - type: ItemCooldown - type: MultiHandedItem - - type: Speech - - type: Defibrillator - zapHeal: - types: - Asphyxiation: -40 - type: PowerCellDraw useRate: 100 - - type: Appearance - - type: DoAfter - - type: UseDelay - - type: StaticPrice - price: 100 - - type: GuideHelp - guides: - - Medical Doctor - type: entity id: DefibrillatorEmpty @@ -54,3 +60,8 @@ slots: cell_slot: name: power-cell-slot-component-slot-name-default + +- type: entity + id: DefibrillatorOneHandedUnpowered + parent: BaseDefibrillator + suffix: One-Handed, Unpowered diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml index 7835f2f40f..823e2ffc4a 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml @@ -57,6 +57,15 @@ - type: Item size: 1 +- type: entity + id: Ointment10Lingering + parent: Ointment + suffix: 10, Lingering + components: + - type: Stack + lingering: true + count: 10 + - type: entity name: regenerative mesh description: Used to treat even the nastiest burns. Also effective against caustic burns. @@ -138,6 +147,15 @@ - type: Item size: 1 +- type: entity + id: Brutepack10Lingering + parent: Brutepack + suffix: 10, Lingering + components: + - type: Stack + lingering: true + count: 10 + - type: entity name: medicated suture description: A suture soaked in medicine, treats blunt-force trauma effectively and closes wounds. @@ -207,6 +225,14 @@ - type: StackPrice price: 10 +- type: entity + parent: Bloodpack + id: Bloodpack10Lingering + suffix: 10, Lingering + components: + - type: Stack + lingering: true + - type: entity name: roll of gauze description: Some sterile gauze to wrap around bloody stumps. @@ -250,6 +276,15 @@ - type: Item size: 1 +- type: entity + id: Gauze10Lingering + parent: Gauze + suffix: 10, Lingering + components: + - type: Stack + lingering: true + count: 10 + - type: entity name: aloe cream description: A topical cream for burns. diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml index c638fe89bd..90953fdc6a 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml @@ -1,24 +1,18 @@ - type: entity + id: HandheldHealthAnalyzerUnpowered + parent: BaseItem name: health analyzer - parent: - - BaseItem - - PowerCellSlotSmallItem - id: HandheldHealthAnalyzer description: A hand-held body scanner capable of distinguishing vital signs of the subject. components: - type: Sprite sprite: Objects/Specific/Medical/healthanalyzer.rsi state: icon layers: - - state: icon - - state: analyzer - shader: unshaded - visible: true - map: [ "enum.PowerDeviceVisualLayers.Powered" ] - - type: PowerCellDraw - drawRate: 0 - useRate: 20 - - type: ActivatableUIRequiresPowerCell + - state: icon + - state: analyzer + shader: unshaded + visible: true + map: [ "enum.PowerDeviceVisualLayers.Powered" ] - type: ActivatableUI key: enum.HealthAnalyzerUiKey.Key closeOnHandDeselect: false @@ -31,7 +25,7 @@ path: "/Audio/Items/Medical/healthscanner.ogg" - type: Tag tags: - - DiscreteHealthAnalyzer + - DiscreteHealthAnalyzer - type: Appearance - type: GenericVisualizer visuals: @@ -41,7 +35,17 @@ False: { visible: false } - type: GuideHelp guides: - - Medical Doctor + - Medical Doctor + +- type: entity + id: HandheldHealthAnalyzer + parent: [ HandheldHealthAnalyzerUnpowered, PowerCellSlotSmallItem] + suffix: Powered + components: + - type: PowerCellDraw + drawRate: 0 + useRate: 20 + - type: ActivatableUIRequiresPowerCell - type: entity id: HandheldHealthAnalyzerEmpty diff --git a/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml b/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml index 0d050d374b..b2c1f6cdcc 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml @@ -21,19 +21,20 @@ - ScannersAndVessels - type: entity - id: AnomalyLocator - parent: [ BaseItem, PowerCellSlotSmallItem ] + id: AnomalyLocatorUnpowered + parent: BaseItem name: anomaly locator description: A device designed to aid in the locating of anomalies. Did you check the gas miners? + suffix: Unpowered components: - type: Sprite sprite: Objects/Specific/Research/anomalylocator.rsi layers: - - state: icon - - state: screen - shader: unshaded - visible: false - map: ["enum.PowerDeviceVisualLayers.Powered"] + - state: icon + - state: screen + shader: unshaded + visible: false + map: ["enum.PowerDeviceVisualLayers.Powered"] - type: Appearance - type: GenericVisualizer visuals: @@ -41,9 +42,6 @@ enum.PowerDeviceVisualLayers.Powered: True: { visible: true } False: { visible: false } - - type: PowerCellDraw - drawRate: 1 - useRate: 0 - type: ProximityBeeper component: Anomaly maximumDistance: 20 @@ -55,6 +53,15 @@ maxdistance: 1 volume: -8 +- type: entity + id: AnomalyLocator + parent: [ AnomalyLocatorUnpowered, PowerCellSlotSmallItem ] + suffix: Powered + components: + - type: PowerCellDraw + drawRate: 1 + useRate: 0 + - type: entity id: AnomalyLocatorEmpty parent: AnomalyLocator diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml new file mode 100644 index 0000000000..bcd81225bf --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml @@ -0,0 +1,400 @@ +- type: entity + id: BaseBorgModule + parent: BaseItem + name: borg module + description: A piece of tech that gives cyborgs new abilities. + abstract: true + components: + - type: Sprite + sprite: Objects/Specific/Robotics/borgmodule.rsi + - type: BorgModule + - type: StaticPrice + price: 250 + - type: Tag + tags: + - BorgModuleGeneric + +- type: entity + id: BaseProviderBorgModule + abstract: true + components: + - type: SelectableBorgModule + - type: ContainerContainer + containers: + provided_container: !type:Container { } + +- type: entity + id: BaseBorgModuleCargo + parent: BaseBorgModule + abstract: true + components: + - type: Tag + tags: + - BorgModuleCargo + +- type: entity + id: BaseBorgModuleEngineering + parent: BaseBorgModule + abstract: true + components: + - type: Tag + tags: + - BorgModuleEngineering + +- type: entity + id: BaseBorgModuleJanitor + parent: BaseBorgModule + abstract: true + components: + - type: Tag + tags: + - BorgModuleJanitor + +- type: entity + id: BaseBorgModuleMedical + parent: BaseBorgModule + abstract: true + components: + - type: Tag + tags: + - BorgModuleMedical + +- type: entity + id: BaseBorgModuleService + parent: BaseBorgModule + abstract: true + components: + - type: Tag + tags: + - BorgModuleService + +# generic modules +- type: entity + id: BorgModuleCable + parent: [ BaseBorgModule, BaseProviderBorgModule ] + name: cable cyborg module + components: + - type: Sprite + layers: + - state: generic + - state: icon-cables + - type: ItemBorgModule + items: + - CableApcStackLingering10 + - CableMVStackLingering10 + - CableHVStackLingering10 + - Wirecutter + - trayScanner + +- type: entity + id: BorgModuleFireExtinguisher + parent: [ BaseBorgModule, BaseProviderBorgModule ] + name: fire extinguisher cyborg module + components: + - type: Sprite + layers: + - state: generic + - state: icon-fire-extinguisher + - type: ItemBorgModule + items: + - FireExtinguisher + +- type: entity + id: BorgModuleGPS + parent: [ BaseBorgModule, BaseProviderBorgModule ] + name: GPS cyborg module + components: + - type: Sprite + layers: + - state: generic + - state: icon-gps + - type: ItemBorgModule + items: + - HandheldGPSBasic + - HandheldStationMapUnpowered + +- type: entity + id: BorgModuleRadiationDetection + parent: [ BaseBorgModule, BaseProviderBorgModule ] + name: radiation detection cyborg module + components: + - type: Sprite + layers: + - state: generic + - state: icon-radiation + - type: ItemBorgModule + items: + - GeigerCounter + +- type: entity + id: BorgModuleTool + parent: [ BaseBorgModule, BaseProviderBorgModule ] + name: tool cyborg module + components: + - type: Sprite + layers: + - state: generic + - state: icon-tools + - type: ItemBorgModule + items: + - Crowbar + - Wrench + - Screwdriver + - Wirecutter + +# cargo modules +- type: entity + id: BorgModuleAppraisal + parent: [ BaseBorgModuleCargo, BaseProviderBorgModule ] + name: appraisal cyborg module + components: + - type: Sprite + layers: + - state: cargo + - state: icon-appraisal + - type: ItemBorgModule + items: + - AppraisalTool + +- type: entity + id: BorgModuleMining + parent: [ BaseBorgModuleCargo, BaseProviderBorgModule ] + name: mining cyborg module + components: + - type: Sprite + layers: + - state: cargo + - state: icon-mining + - type: ItemBorgModule + items: + - MiningDrill + - OreBag + +- type: entity + id: BorgModuleGrapplingGun + parent: [ BaseBorgModuleCargo, BaseProviderBorgModule ] + name: grappling gun cyborg module + components: + - type: Sprite + layers: + - state: cargo + - state: icon-grappling-gun + - type: ItemBorgModule + items: + - WeaponGrapplingGun + +# engineering modules +- type: entity + id: BorgModuleAdvancedTool + parent: [ BaseBorgModuleEngineering, BaseProviderBorgModule ] + name: advanced tool cyborg module + components: + - type: Sprite + layers: + - state: engineering + - state: icon-tools-adv + - type: ItemBorgModule + items: + - Omnitool + - WelderExperimental + - NetworkConfigurator + +- type: entity + id: BorgModuleGasAnalyzer + parent: [ BaseBorgModuleEngineering, BaseProviderBorgModule ] + name: gas analyzer cyborg module + components: + - type: Sprite + layers: + - state: engineering + - state: icon-gas-analyzer + - type: ItemBorgModule + items: + - GasAnalyzer + +- type: entity + id: BorgModuleRCD + parent: [ BaseBorgModuleEngineering, BaseProviderBorgModule ] + name: RCD cyborg module + components: + - type: Sprite + layers: + - state: engineering + - state: icon-rcd + - type: ItemBorgModule + items: + - RCDRecharging + +# janitorial modules (this gets its own unique things because janis are epic) +- type: entity + id: BorgModuleLightReplacer + parent: [ BaseBorgModuleJanitor, BaseProviderBorgModule ] + name: light replacer cyborg module + components: + - type: Sprite + layers: + - state: janitor + - state: icon-light-replacer + - type: ItemBorgModule + items: + - LightReplacer + +- type: entity + id: BorgModuleCleaning + parent: [ BaseBorgModuleJanitor, BaseProviderBorgModule ] + name: cleaning cyborg module + components: + - type: Sprite + layers: + - state: janitor + - state: icon-mop + - type: ItemBorgModule + items: + - MopItem + - Bucket + - Holoprojector + +- type: entity + id: BorgModuleTrashCollection + parent: [ BaseBorgModuleJanitor, BaseProviderBorgModule ] + name: trash collection cyborg module + components: + - type: Sprite + layers: + - state: janitor + - state: icon-trash-bag + - type: ItemBorgModule + items: + - TrashBag + +# medical modules +- type: entity + id: BorgModuleDiagnosis + parent: [ BaseBorgModuleMedical, BaseProviderBorgModule ] + name: diagnosis cyborg module + components: + - type: Sprite + layers: + - state: medical + - state: icon-diagnosis + - type: ItemBorgModule + items: + - HandheldHealthAnalyzerUnpowered + - ClothingNeckStethoscope + +- type: entity + id: BorgModuleTreatment + parent: [ BaseBorgModuleMedical, BaseProviderBorgModule ] + name: treatment cyborg module + components: + - type: Sprite + layers: + - state: medical + - state: icon-treatment + - type: ItemBorgModule + items: + - Brutepack10Lingering + - Ointment10Lingering + - Gauze10Lingering + - Bloodpack10Lingering + - Syringe + +- type: entity + id: BorgModuleDefibrillator + parent: [ BaseBorgModuleMedical, BaseProviderBorgModule ] + name: defibrillator cyborg module + components: + - type: Sprite + layers: + - state: medical + - state: icon-defib + - type: ItemBorgModule + items: + - DefibrillatorOneHandedUnpowered + +# science modules +# todo: if science ever gets their own custom robot, add more "sci" modules. +- type: entity + id: BorgModuleArtifact + parent: [ BaseBorgModule, BaseProviderBorgModule ] + name: artifact cyborg module + components: + - type: Sprite + layers: + - state: science + - state: icon-artifacts + - type: ItemBorgModule + items: + - NodeScanner + +- type: entity + id: BorgModuleAnomaly + parent: [ BaseBorgModule, BaseProviderBorgModule ] + name: anomaly cyborg module + components: + - type: Sprite + layers: + - state: science + - state: icon-anomalies + - type: ItemBorgModule + items: + - AnomalyScanner + - AnomalyLocatorUnpowered + +# service modules +- type: entity + id: BorgModuleLiteracy + parent: [ BaseBorgModuleService, BaseProviderBorgModule ] + name: literacy cyborg module + components: + - type: Sprite + layers: + - state: service + - state: icon-pen + - type: ItemBorgModule + items: + - Pen + +- type: entity + id: BorgModuleMusique + parent: [ BaseBorgModuleService, BaseProviderBorgModule ] + name: musique cyborg module + components: + - type: Sprite + layers: + - state: service + - state: icon-musique + - type: ItemBorgModule + items: + - SynthesizerInstrument + +- type: entity + id: BorgModuleGardening + parent: [ BaseBorgModuleService, BaseProviderBorgModule ] + name: gardening cyborg module + components: + - type: Sprite + layers: + - state: service + - state: icon-gardening + - type: ItemBorgModule + items: + - HydroponicsToolMiniHoe + - HydroponicsToolSpade + - HydroponicsToolScythe + - HydroponicsToolClippers + - HydroponicsToolHatchet + +- type: entity + id: BorgModuleClowning + parent: [ BaseBorgModuleService, BaseProviderBorgModule ] + name: clowning cyborg module + components: + - type: Sprite + layers: + - state: service + - state: icon-clown + - type: ItemBorgModule + items: + - BikeHorn + - ClownRecorder diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_parts.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_parts.yml new file mode 100644 index 0000000000..6df0488e28 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_parts.yml @@ -0,0 +1,503 @@ +# generic parts +- type: entity + id: LeftArmBorg + parent: BaseBorgArmLeft + components: + - type: Sprite + state: borg_l_arm + - type: Icon + state: borg_l_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgGenericLArm + +- type: entity + id: RightArmBorg + parent: BaseBorgArmRight + components: + - type: Sprite + state: borg_r_arm + - type: Icon + state: borg_r_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgGenericRArm + +- type: entity + id: LeftLegBorg + parent: BaseBorgLegLeft + components: + - type: Sprite + state: borg_l_leg + - type: Icon + state: borg_l_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgGenericLLeg + +- type: entity + id: RightLegBorg + parent: BaseBorgLegRight + components: + - type: Sprite + state: borg_r_leg + - type: Icon + state: borg_r_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgGenericRLeg + +- type: entity + id: LightHeadBorg + parent: BaseBorgHead + components: + - type: Sprite + state: borg_head + - type: Icon + state: borg_head + - type: Tag + tags: + - Trash + - BorgHead + - BorgGenericHead + +- type: entity + id: TorsoBorg + parent: BaseBorgTorso + components: + - type: Sprite + state: borg_chest + - type: Icon + state: borg_chest + - type: Tag + tags: + - Trash + - BorgGenericTorso + +# engineer parts +- type: entity + id: LeftArmBorgEngineer + parent: BaseBorgArmLeft + name: engineer cyborg left arm + components: + - type: Sprite + state: engineer_l_arm + - type: Icon + state: engineer_l_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgEngineerLArm + +- type: entity + id: RightArmBorgEngineer + parent: BaseBorgArmRight + name: engineer cyborg right arm + components: + - type: Sprite + state: engineer_r_arm + - type: Icon + state: engineer_r_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgEngineerRArm + +- type: entity + id: LeftLegBorgEngineer + parent: BaseBorgLegLeft + name: engineer cyborg left leg + components: + - type: Sprite + state: engineer_l_leg + - type: Icon + state: engineer_l_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgEngineerLLeg + +- type: entity + id: RightLegBorgEngineer + parent: BaseBorgLegRight + name: engineer cyborg right leg + components: + - type: Sprite + state: engineer_r_leg + - type: Icon + state: engineer_r_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgEngineerRLeg + +- type: entity + id: HeadBorgEngineer + parent: BaseBorgHead + name: engineer cyborg head + components: + - type: Sprite + state: engineer_head + - type: Icon + state: engineer_head + - type: Tag + tags: + - Trash + - BorgHead + - BorgEngineerHead + +- type: entity + id: TorsoBorgEngineer + parent: BaseBorgTorso + name: engineer cyborg torso + components: + - type: Sprite + state: engineer_chest + - type: Icon + state: engineer_chest + - type: Tag + tags: + - Trash + - BorgEngineerTorso + +# janitor parts +- type: entity + id: LeftLegBorgJanitor + parent: BaseBorgLegLeft + name: janitor cyborg left leg + components: + - type: Sprite + state: janitor_l_leg + - type: Icon + state: janitor_l_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgJanitorLLeg + +- type: entity + id: RightLegBorgJanitor + parent: BaseBorgLegRight + name: janitor cyborg right leg + components: + - type: Sprite + state: janitor_r_leg + - type: Icon + state: janitor_r_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgJanitorRLeg + +- type: entity + id: HeadBorgJanitor + parent: BaseBorgHead + name: janitor cyborg head + components: + - type: Sprite + state: janitor_head + - type: Icon + state: janitor_head + - type: Tag + tags: + - Trash + - BorgHead + - BorgJanitorHead + +- type: entity + id: TorsoBorgJanitor + parent: BaseBorgTorso + name: janitor cyborg torso + components: + - type: Sprite + state: janitor_chest + - type: Icon + state: janitor_chest + - type: Tag + tags: + - Trash + - BorgJanitorTorso + +# medical parts +- type: entity + id: LeftArmBorgMedical + parent: BaseBorgArmLeft + name: medical cyborg left arm + components: + - type: Sprite + state: medical_l_arm + - type: Icon + state: medical_l_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgMedicalLArm + +- type: entity + id: RightArmBorgMedical + parent: BaseBorgArmRight + name: medical cyborg right arm + components: + - type: Sprite + state: medical_r_arm + - type: Icon + state: medical_r_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgMedicalRArm + +- type: entity + id: LeftLegBorgMedical + parent: BaseBorgLegLeft + name: medical cyborg left leg + components: + - type: Sprite + state: medical_l_leg + - type: Icon + state: medical_l_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgMedicalLLeg + +- type: entity + id: RightLegBorgMedical + parent: BaseBorgLegRight + name: medical cyborg right leg + components: + - type: Sprite + state: medical_r_leg + - type: Icon + state: medical_r_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgMedicalRLeg + +- type: entity + id: HeadBorgMedical + parent: BaseBorgHead + name: medical cyborg head + components: + - type: Sprite + state: medical_head + - type: Icon + state: medical_head + - type: Tag + tags: + - Trash + - BorgHead + - BorgMedicalHead + +- type: entity + id: TorsoBorgMedical + parent: BaseBorgTorso + name: medical cyborg torso + components: + - type: Sprite + state: medical_chest + - type: Icon + state: medical_chest + - type: Tag + tags: + - Trash + - BorgMedicalTorso + +# mining parts +- type: entity + id: LeftArmBorgMining + parent: BaseBorgArmLeft + name: mining cyborg left arm + components: + - type: Sprite + state: mining_l_arm + - type: Icon + state: mining_l_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgMiningLArm + +- type: entity + id: RightArmBorgMining + parent: BaseBorgArmRight + name: mining cyborg right arm + components: + - type: Sprite + state: mining_r_arm + - type: Icon + state: mining_r_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgMiningRArm + +- type: entity + id: LeftLegBorgMining + parent: BaseBorgLegLeft + name: mining cyborg left leg + components: + - type: Sprite + state: mining_l_leg + - type: Icon + state: mining_l_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgMiningLLeg + +- type: entity + id: RightLegBorgMining + parent: BaseBorgLegRight + name: mining cyborg right leg + components: + - type: Sprite + state: mining_r_leg + - type: Icon + state: mining_r_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgMiningRLeg + +- type: entity + id: HeadBorgMining + parent: BaseBorgHead + name: mining cyborg head + components: + - type: Sprite + state: mining_head + - type: Icon + state: mining_head + - type: Tag + tags: + - Trash + - BorgHead + - BorgMiningHead + +- type: entity + id: TorsoBorgMining + parent: BaseBorgTorso + name: mining cyborg torso + components: + - type: Sprite + state: mining_chest + - type: Icon + state: mining_chest + - type: Tag + tags: + - Trash + - BorgMiningTorso + +# service parts +- type: entity + id: LeftArmBorgService + parent: BaseBorgArmLeft + name: service cyborg left arm + components: + - type: Sprite + state: service_l_arm + - type: Icon + state: service_l_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgServiceLArm + +- type: entity + id: RightArmBorgService + parent: BaseBorgArmRight + name: service cyborg right arm + components: + - type: Sprite + state: service_r_arm + - type: Icon + state: service_r_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgServiceRArm + +- type: entity + id: LeftLegBorgService + parent: BaseBorgLegLeft + name: service cyborg left leg + components: + - type: Sprite + state: service_l_leg + - type: Icon + state: service_l_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgServiceLLeg + +- type: entity + id: RightLegBorgService + parent: BaseBorgLegRight + name: service cyborg right leg + components: + - type: Sprite + state: service_r_leg + - type: Icon + state: service_r_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgServiceRLeg + +- type: entity + id: HeadBorgService + parent: BaseBorgHead + name: service cyborg head + components: + - type: Sprite + state: service_head + - type: Icon + state: service_head + - type: Tag + tags: + - Trash + - BorgHead + - BorgServiceHead + +- type: entity + id: TorsoBorgService + parent: BaseBorgTorso + name: service cyborg torso + components: + - type: Sprite + state: service_chest + - type: Icon + state: service_chest + - type: Tag + tags: + - Trash + - BorgServiceTorso diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/endoskeleton.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/endoskeleton.yml new file mode 100644 index 0000000000..6730e86fca --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/endoskeleton.yml @@ -0,0 +1,230 @@ +- type: entity + id: CyborgEndoskeleton + name: cyborg endoskeleton + description: A frame that cyborgs are built on. Significantly less spooky than expected. + components: + - type: Clickable + - type: InteractionOutline + - type: Transform + noRot: true + - type: CollisionWake + - type: TileFrictionModifier + modifier: 0.5 + - type: Physics + bodyType: Dynamic + fixedRotation: false + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.25,-0.25,0.25,0.25" + density: 100 + mask: + - MobMask + layer: + - MobLayer + restitution: 0.3 + friction: 0.2 + - type: Sprite + noRot: true + drawdepth: Items + sprite: Objects/Specific/Robotics/cyborg_parts.rsi + state: robo_suit + - type: Appearance + - type: ItemMapper + sprite: Objects/Specific/Robotics/cyborg_parts.rsi + mapLayers: + borg_l_arm+o: + whitelist: + tags: + - BorgGenericLArm + borg_r_arm+o: + whitelist: + tags: + - BorgGenericRArm + borg_l_leg+o: + whitelist: + tags: + - BorgGenericLLeg + borg_r_leg+o: + whitelist: + tags: + - BorgGenericRLeg + borg_head+o: + whitelist: + tags: + - BorgGenericHead + borg_chest+o: + whitelist: + tags: + - BorgGenericTorso + service_l_arm+o: + whitelist: + tags: + - BorgServiceLArm + service_r_arm+o: + whitelist: + tags: + - BorgServiceRArm + service_l_leg+o: + whitelist: + tags: + - BorgServiceLLeg + service_r_leg+o: + whitelist: + tags: + - BorgServiceRLeg + service_head+o: + whitelist: + tags: + - BorgServiceHead + service_chest+o: + whitelist: + tags: + - BorgServiceTorso + engineer_l_arm+o: + whitelist: + tags: + - BorgEngineerLArm + engineer_r_arm+o: + whitelist: + tags: + - BorgEngineerRArm + engineer_l_leg+o: + whitelist: + tags: + - BorgEngineerLLeg + engineer_r_leg+o: + whitelist: + tags: + - BorgEngineerRLeg + engineer_head+o: + whitelist: + tags: + - BorgEngineerHead + engineer_chest+o: + whitelist: + tags: + - BorgEngineerTorso + mining_l_arm+o: + whitelist: + tags: + - BorgMiningLArm + mining_r_arm+o: + whitelist: + tags: + - BorgMiningRArm + mining_l_leg+o: + whitelist: + tags: + - BorgMiningLLeg + mining_r_leg+o: + whitelist: + tags: + - BorgMiningRLeg + mining_head+o: + whitelist: + tags: + - BorgMiningHead + mining_chest+o: + whitelist: + tags: + - BorgMiningTorso + medical_l_arm+o: + whitelist: + tags: + - BorgMedicalLArm + medical_r_arm+o: + whitelist: + tags: + - BorgMedicalRArm + medical_l_leg+o: + whitelist: + tags: + - BorgMedicalLLeg + medical_r_leg+o: + whitelist: + tags: + - BorgMedicalRLeg + medical_head+o: + whitelist: + tags: + - BorgMedicalHead + medical_chest+o: + whitelist: + tags: + - BorgMedicalTorso + janitor_l_leg+o: + whitelist: + tags: + - BorgJanitorLLeg + janitor_r_leg+o: + whitelist: + tags: + - BorgJanitorRLeg + janitor_head+o: + whitelist: + tags: + - BorgJanitorHead + janitor_chest+o: + whitelist: + tags: + - BorgJanitorTorso + - type: ContainerContainer + containers: + part-container: !type:Container + cell_slot: !type:Container + - type: PartAssembly + parts: + generic: + - BorgGenericLArm + - BorgGenericRArm + - BorgGenericLLeg + - BorgGenericRLeg + - BorgGenericHead + - BorgGenericTorso + service: + - BorgServiceLArm + - BorgServiceRArm + - BorgServiceLLeg + - BorgServiceRLeg + - BorgServiceHead + - BorgServiceTorso + engineer: + - BorgEngineerLArm + - BorgEngineerRArm + - BorgEngineerLLeg + - BorgEngineerRLeg + - BorgEngineerHead + - BorgEngineerTorso + medical: + - BorgMedicalLArm + - BorgMedicalRArm + - BorgMedicalLLeg + - BorgMedicalRLeg + - BorgMedicalHead + - BorgMedicalTorso + janitor: + - BorgJanitorLLeg + - BorgJanitorRLeg + - BorgJanitorHead + - BorgJanitorTorso + mining: + - BorgMiningLArm + - BorgMiningRArm + - BorgMiningLLeg + - BorgMiningRLeg + - BorgMiningHead + - BorgMiningTorso + - type: Construction + graph: Cyborg + node: start + defaultTarget: cyborg + containers: + - part-container + - cell_slot + - type: Pullable + - type: GuideHelp + guides: + - Robotics diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml new file mode 100644 index 0000000000..9dab1513db --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml @@ -0,0 +1,122 @@ +- type: entity + parent: BaseItem + id: MMI + name: man-machine interface + description: A machine able to facilitate communication between a biological brain and electronics, enabling crew to continue to provide value after work-related incidents. + components: + - type: Sprite + sprite: Objects/Specific/Robotics/mmi.rsi + layers: + - state: mmi_brain + map: ["enum.MMIVisualLayers.Brain"] + visible: false + - state: mmi_off + map: ["enum.MMIVisualLayers.Base"] + - type: Input + context: human + - type: MMI + - type: BorgBrain + - type: BlockMovement + - type: Examiner + - type: IntrinsicRadioReceiver + - type: IntrinsicRadioTransmitter + channels: + - Binary + - type: ActiveRadio + channels: + - Common + - Binary + - type: NameIdentifier + group: MMI + - type: DoAfter + - type: Actions + - type: TypingIndicator + proto: robot + - type: Speech + speechSounds: Pai + - type: MobState + allowedStates: + - Alive + - type: ItemSlots + slots: + brain_slot: + name: "Brain" + whitelist: + components: + - Brain + - type: ContainerContainer + containers: + brain_slot: !type:ContainerSlot + - type: Appearance + +- type: entity + parent: MMI + id: MMIFilled + suffix: Filled + components: + - type: ItemSlots + slots: + brain_slot: + name: "Brain" + startingItem: OrganHumanBrain + whitelist: + components: + - Brain + +- type: entity + parent: BaseItem + id: PositronicBrain + name: positronic brain + description: An artificial brain capable of spontaneous neural activity. + components: + - type: Sprite + sprite: Objects/Specific/Robotics/mmi.rsi + layers: + - state: posibrain + map: ["base"] + - type: Input + context: human + - type: BlockMovement + - type: ToggleableGhostRole + examineTextMindPresent: positronic-brain-installed + examineTextMindSearching: positronic-brain-still-searching + examineTextNoMind: positronic-brain-off + beginSearchingText: positronic-brain-searching + roleName: positronic-brain-role-name + roleDescription: positronic-brain-role-description + wipeVerbText: positronic-brain-wipe-device-verb-text + wipeVerbPopup: positronic-brain-wiped-device + stopSearchVerbText: positronic-brain-stop-searching-verb-text + stopSearchVerbPopup: positronic-brain-stopped-searching + - type: Examiner + - type: BorgBrain + - type: IntrinsicRadioReceiver + - type: IntrinsicRadioTransmitter + channels: + - Binary + - type: ActiveRadio + channels: + - Common + - Binary + - type: NameIdentifier + group: PositronicBrain + - type: DoAfter + - type: Actions + - type: TypingIndicator + proto: robot + - type: Speech + speechSounds: Pai + - type: MobState + allowedStates: + - Alive + - type: Appearance + - type: Tag + tags: + - CannotSuicide + - type: GenericVisualizer + visuals: + enum.ToggleableGhostRoleVisuals.Status: + base: + Off: { state: posibrain } + Searching: { state: posibrain-searching } + On: { state: posibrain-occupied } diff --git a/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml b/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml index adfd022c96..498a871853 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml @@ -65,6 +65,15 @@ - type: Item size: 10 +- type: entity + parent: CableHVStack10 + id: CableHVStackLingering10 + suffix: Lingering, 10 + components: + - type: Stack + lingering: true + count: 10 + - type: entity parent: CableHVStack id: CableHVStack1 @@ -115,6 +124,15 @@ - type: Item size: 10 +- type: entity + parent: CableMVStack10 + id: CableMVStackLingering10 + suffix: Lingering, 10 + components: + - type: Stack + lingering: true + count: 10 + - type: entity parent: CableMVStack id: CableMVStack1 @@ -164,6 +182,15 @@ - type: Item size: 10 +- type: entity + parent: CableApcStack10 + id: CableApcStackLingering10 + suffix: Lingering, 10 + components: + - type: Stack + lingering: true + count: 10 + - type: entity parent: CableApcStack id: CableApcStack1 diff --git a/Resources/Prototypes/Entities/Objects/Tools/tools.yml b/Resources/Prototypes/Entities/Objects/Tools/tools.yml index fa90fa3dd5..3d58b2b079 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/tools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/tools.yml @@ -362,6 +362,19 @@ maxCharges: 5 charges: 0 +- type: entity + id: RCDRecharging + parent: RCD + name: experimental rcd + description: A bluespace-enhanced RCD that regenerates charges passively. + suffix: AutoRecharge + components: + - type: LimitedCharges + maxCharges: 3 + charges: 3 + - type: AutoRecharge + rechargeDuration: 30 + - type: entity id: RCDExperimental parent: RCD diff --git a/Resources/Prototypes/Entities/Stations/base.yml b/Resources/Prototypes/Entities/Stations/base.yml index b758b4e395..272ed6712c 100644 --- a/Resources/Prototypes/Entities/Stations/base.yml +++ b/Resources/Prototypes/Entities/Stations/base.yml @@ -66,6 +66,16 @@ components: - type: SalvageExpeditionData +- type: entity + id: BaseStationSiliconLawCrewsimov + abstract: true + components: + - type: SiliconLawProvider + laws: + - Crewsimov1 + - Crewsimov2 + - Crewsimov3 + - type: entity id: BaseStationAllEventsEligible abstract: true diff --git a/Resources/Prototypes/Entities/Stations/nanotrasen.yml b/Resources/Prototypes/Entities/Stations/nanotrasen.yml index 1017875618..40cdcfe489 100644 --- a/Resources/Prototypes/Entities/Stations/nanotrasen.yml +++ b/Resources/Prototypes/Entities/Stations/nanotrasen.yml @@ -19,6 +19,7 @@ - BaseStationEvacuation - BaseStationAlertLevels - BaseStationExpeditions + - BaseStationSiliconLawCrewsimov - BaseStationAllEventsEligible - BaseStationNanotrasen noSpawn: true diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 32ec2e38da..5252f9e429 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -124,6 +124,7 @@ - SMESMachineCircuitboard - SubstationMachineCircuitboard - CellRechargerCircuitboard + - BorgChargerCircuitboard - WeaponCapacitorRechargerCircuitboard - type: EmagLatheRecipes emagStaticRecipes: @@ -385,13 +386,70 @@ - type: Lathe idleState: fab-idle runningState: fab-active - dynamicRecipes: - - ProximitySensor + staticRecipes: + - MMI + - PositronicBrain + - Flash + - BorgModuleCable + - BorgModuleFireExtinguisher + - BorgModuleGPS + - BorgModuleRadiationDetection + - BorgModuleTool + - BorgModuleAppraisal + - CyborgEndoskeleton - LeftArmBorg - RightArmBorg - LeftLegBorg - RightLegBorg - LightHeadBorg + - TorsoBorg + - LeftArmBorgEngineer + - RightArmBorgEngineer + - LeftLegBorgEngineer + - RightLegBorgEngineer + - HeadBorgEngineer + - TorsoBorgEngineer + - LeftLegBorgJanitor + - RightLegBorgJanitor + - HeadBorgJanitor + - TorsoBorgJanitor + - LeftArmBorgMedical + - RightArmBorgMedical + - LeftLegBorgMedical + - RightLegBorgMedical + - HeadBorgMedical + - TorsoBorgMedical + - LeftArmBorgMining + - RightArmBorgMining + - LeftLegBorgMining + - RightLegBorgMining + - HeadBorgMining + - TorsoBorgMining + - LeftArmBorgService + - RightArmBorgService + - LeftLegBorgService + - RightLegBorgService + - HeadBorgService + - TorsoBorgService + dynamicRecipes: + - ProximitySensor + - BorgModuleLightReplacer + - BorgModuleCleaning + - BorgModuleTrashCollection + - BorgModuleMining + - BorgModuleGrapplingGun + - BorgModuleGasAnalyzer + - BorgModuleAdvancedTool + - BorgModuleRCD + - BorgModuleArtifact + - BorgModuleAnomaly + - BorgModuleGardening + - BorgModuleMusique + - BorgModuleLiteracy + - BorgModuleClowning + - BorgModuleDiagnosis + - BorgModuleTreatment + - BorgModuleDefibrillator - RipleyHarness - RipleyLArm - RipleyRArm diff --git a/Resources/Prototypes/Entities/Structures/Power/chargers.yml b/Resources/Prototypes/Entities/Structures/Power/chargers.yml index e54b3286fd..5fe1b40bd2 100644 --- a/Resources/Prototypes/Entities/Structures/Power/chargers.yml +++ b/Resources/Prototypes/Entities/Structures/Power/chargers.yml @@ -173,3 +173,99 @@ machine_board: !type:Container machine_parts: !type:Container +- type: entity + id: BorgCharger + parent: [ BaseMachinePowered, ConstructibleMachine ] + name: cyborg recharging station + description: A stationary charger for various robotic and cyborg entities. Surprisingly spacious. + placement: + mode: SnapgridCenter + components: + - type: Sprite + sprite: Structures/Power/borg_charger.rsi + snapCardinals: true + layers: + - state: borgcharger-u1 + map: ["base"] + - state: borgcharger0 + map: ["enum.PowerDeviceVisualLayers.Powered"] + shader: "unshaded" + - state: borgcharger1 + map: ["charged"] + shader: "unshaded" + visible: false + - state: borgdecon1 + map: ["enum.WiresVisualLayers.MaintenancePanel"] + visible: false + - type: Charger + baseChargeRate: 30 + slotId: entity_storage + - type: Construction + containers: + - machine_parts + - machine_board + - entity_storage + - type: Wires + LayoutId: borgcharger + - type: WiresPanel + - type: WiresVisuals + - type: Machine + board: BorgChargerCircuitboard + - type: Appearance + - type: GenericVisualizer + visuals: + enum.StorageVisuals.Open: + base: + True: { state: borgcharger-u0 } + False: { state: borgcharger-u1 } + enum.PowerDeviceVisualLayers.Powered: + True: { state: borgcharger0 } + False: { state: borgcharger2 } + charged: + True: { visible: false } + False: { visible: true } + enum.PowerDeviceVisuals.Powered: + enum.PowerDeviceVisualLayers.Powered: + True: { visible: true } + False: { visible: false } + charged: + True: { visible: true } + False: { visible: false } + enum.CellVisual.Light: + charged: + Off: { visible: false } + Empty: { visible: false } + Charging: + visible: true + state: borgcharger3 + Charged: + visible: true + state: borgcharger1 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 80 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 40 + behaviors: + - !type:EmptyAllContainersBehaviour + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + path: /Audio/Effects/metalbreak.ogg + - type: EntityStorage + capacity: 1 + whitelist: + components: + - BorgChassis + - type: ContainerContainer + containers: + entity_storage: !type:Container + machine_board: !type:Container + machine_parts: !type:Container diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/machines/cyborg.yml b/Resources/Prototypes/Recipes/Construction/Graphs/machines/cyborg.yml new file mode 100644 index 0000000000..e9bf26a571 --- /dev/null +++ b/Resources/Prototypes/Recipes/Construction/Graphs/machines/cyborg.yml @@ -0,0 +1,240 @@ +- type: constructionGraph + id: Cyborg + start: start + graph: + - node: start + entity: CyborgEndoskeleton + edges: + + # empty the parts via prying + - to: start + conditions: + - !type:ContainerNotEmpty + container: part-container + steps: + - tool: Prying + doAfter: 0.5 + completed: + - !type:EmptyAllContainers + + - to: cyborg + steps: + - assemblyId: generic + guideString: borg-construction-guide-string + + - component: PowerCell + name: power cell + store: cell_slot + icon: + sprite: Objects/Power/power_cells.rsi + state: small + + - material: Cable + amount: 1 + doAfter: 1 + store: part-container + + - component: Flash + name: flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - component: Flash + name: second flash + store: part-contaiener + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - tool: Screwing + doAfter: 0.5 + + - to: engineer + steps: + - assemblyId: engineer + guideString: borg-construction-guide-string + + - component: PowerCell + name: power cell + store: cell_slot + icon: + sprite: Objects/Power/power_cells.rsi + state: small + + - material: Cable + amount: 1 + doAfter: 1 + store: part-container + + - component: Flash + name: flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - component: Flash + name: second flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - tool: Screwing + doAfter: 0.5 + + - to: janitor + steps: + - assemblyId: janitor + guideString: borg-construction-guide-string + + - component: PowerCell + name: power cell + store: cell_slot + icon: + sprite: Objects/Power/power_cells.rsi + state: small + + - material: Cable + amount: 1 + doAfter: 1 + store: part-container + + - component: Flash + name: flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - component: Flash + name: second flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - tool: Screwing + doAfter: 0.5 + + - to: medical + steps: + - assemblyId: medical + guideString: borg-construction-guide-string + + - component: PowerCell + name: power cell + store: cell_slot + icon: + sprite: Objects/Power/power_cells.rsi + state: small + + - material: Cable + amount: 1 + doAfter: 1 + store: part-container + + - component: Flash + name: flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - component: Flash + name: second flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - tool: Screwing + doAfter: 0.5 + + - to: mining + steps: + - assemblyId: mining + guideString: borg-construction-guide-string + + - component: PowerCell + name: power cell + store: cell_slot + icon: + sprite: Objects/Power/power_cells.rsi + state: small + + - material: Cable + amount: 1 + doAfter: 1 + store: part-container + + - component: Flash + name: flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - component: Flash + name: second flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - tool: Screwing + doAfter: 0.5 + + - to: service + steps: + - assemblyId: service + guideString: borg-construction-guide-string + + - component: PowerCell + name: power cell + store: cell_slot + icon: + sprite: Objects/Power/power_cells.rsi + state: small + + - material: Cable + amount: 1 + doAfter: 1 + store: part-container + + - component: Flash + name: flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - component: Flash + name: second flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - tool: Screwing + doAfter: 0.5 + + - node: cyborg + entity: BorgChassisGeneric + + - node: engineer + entity: BorgChassisEngineer + + - node: janitor + entity: BorgChassisJanitor + + - node: mining + entity: BorgChassisMining + + - node: medical + entity: BorgChassisMedical + + - node: service + entity: BorgChassisService diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/bots/mimebot.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/bots/mimebot.yml index 339e9604ef..4fc851a347 100644 --- a/Resources/Prototypes/Recipes/Crafting/Graphs/bots/mimebot.yml +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/bots/mimebot.yml @@ -24,8 +24,8 @@ name: proximity sensor - tag: BorgHead icon: - sprite: Objects/Specific/Borg/head.rsi - state: light_borg_head + sprite: Objects/Specific/Robotics/cyborg_parts.rsi + state: borg_head name: borg head doAfter: 2 - tag: BorgArm diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/bots/taxibot.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/bots/taxibot.yml index 808de7efc8..4be8848fec 100644 --- a/Resources/Prototypes/Recipes/Crafting/Graphs/bots/taxibot.yml +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/bots/taxibot.yml @@ -13,8 +13,8 @@ name: proximity sensor - tag: BorgHead icon: - sprite: Objects/Specific/Borg/head.rsi - state: light_borg_head + sprite: Objects/Specific/Robotics/cyborg_parts.rsi + state: borg_head name: borg head doAfter: 1 - tag: BorgArm @@ -26,4 +26,4 @@ - material: Steel amount: 5 - node: bot - entity: MobTaxiBot \ No newline at end of file + entity: MobTaxiBot diff --git a/Resources/Prototypes/Recipes/Lathes/electronics.yml b/Resources/Prototypes/Recipes/Lathes/electronics.yml index e4d798803c..8a5a5a2996 100644 --- a/Resources/Prototypes/Recipes/Lathes/electronics.yml +++ b/Resources/Prototypes/Recipes/Lathes/electronics.yml @@ -22,6 +22,14 @@ Steel: 50 Plastic: 50 +- type: latheRecipe + id: BorgChargerCircuitboard + result: BorgChargerCircuitboard + completetime: 2 + materials: + Steel: 50 + Plastic: 50 + - type: latheRecipe id: WeaponCapacitorRechargerCircuitboard result: WeaponCapacitorRechargerCircuitboard diff --git a/Resources/Prototypes/Recipes/Lathes/robotics.yml b/Resources/Prototypes/Recipes/Lathes/robotics.yml index 62c6f4d24d..1ace3c4929 100644 --- a/Resources/Prototypes/Recipes/Lathes/robotics.yml +++ b/Resources/Prototypes/Recipes/Lathes/robotics.yml @@ -6,42 +6,535 @@ Steel: 200 Glass: 300 +- type: latheRecipe + id: CyborgEndoskeleton + result: CyborgEndoskeleton + completetime: 3 + materials: + Steel: 1500 + - type: latheRecipe id: LeftArmBorg result: LeftArmBorg completetime: 2 materials: - Steel: 70 - Glass: 25 + Steel: 500 + Glass: 150 - type: latheRecipe id: RightArmBorg result: RightArmBorg completetime: 2 materials: - Steel: 70 - Glass: 25 + Steel: 500 + Glass: 150 - type: latheRecipe id: LeftLegBorg result: LeftLegBorg completetime: 2 materials: - Steel: 70 - Glass: 25 + Steel: 500 + Glass: 150 - type: latheRecipe id: RightLegBorg result: RightLegBorg completetime: 2 materials: - Steel: 70 - Glass: 25 + Steel: 500 + Glass: 150 - type: latheRecipe id: LightHeadBorg result: LightHeadBorg completetime: 2 materials: - Steel: 70 - Glass: 25 + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: TorsoBorg + result: TorsoBorg + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftArmBorgEngineer + result: LeftArmBorgEngineer + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightArmBorgEngineer + result: RightArmBorgEngineer + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftLegBorgEngineer + result: LeftLegBorgEngineer + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightLegBorgEngineer + result: RightLegBorgEngineer + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: HeadBorgEngineer + result: HeadBorgEngineer + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: TorsoBorgEngineer + result: TorsoBorgEngineer + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftArmBorgMedical + result: LeftArmBorgMedical + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightArmBorgMedical + result: RightArmBorgMedical + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftLegBorgMedical + result: LeftLegBorgMedical + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightLegBorgMedical + result: RightLegBorgMedical + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: HeadBorgMedical + result: HeadBorgMedical + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: TorsoBorgMedical + result: TorsoBorgMedical + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftArmBorgMining + result: LeftArmBorgMining + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightArmBorgMining + result: RightArmBorgMining + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftLegBorgMining + result: LeftLegBorgMining + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightLegBorgMining + result: RightLegBorgMining + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: HeadBorgMining + result: HeadBorgMining + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: TorsoBorgMining + result: TorsoBorgMining + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftArmBorgService + result: LeftArmBorgService + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightArmBorgService + result: RightArmBorgService + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftLegBorgService + result: LeftLegBorgService + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightLegBorgService + result: RightLegBorgService + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: HeadBorgService + result: HeadBorgService + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: TorsoBorgService + result: TorsoBorgService + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftLegBorgJanitor + result: LeftLegBorgJanitor + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightLegBorgJanitor + result: RightLegBorgJanitor + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: HeadBorgJanitor + result: HeadBorgJanitor + completetime: 4 + materials: + Steel: 1000 + Glass: 300 + +- type: latheRecipe + id: TorsoBorgJanitor + result: TorsoBorgJanitor + completetime: 4 + materials: + Steel: 1000 + Glass: 300 + +- type: latheRecipe + id: MMI + result: MMI + completetime: 5 + icon: + sprite: Objects/Specific/Robotics/mmi.rsi + state: mmi_off + materials: + Steel: 1500 + Glass: 750 + Plastic: 250 + Gold: 250 + Silver: 250 + +- type: latheRecipe + id: PositronicBrain + result: PositronicBrain + completetime: 5 + materials: + Steel: 1000 + Plastic: 500 + Silver: 500 + Plasma: 1500 + +- type: latheRecipe + id: BorgModuleCable + result: BorgModuleCable + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleFireExtinguisher + result: BorgModuleFireExtinguisher + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleGPS + result: BorgModuleGPS + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleRadiationDetection + result: BorgModuleRadiationDetection + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleTool + result: BorgModuleTool + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleAppraisal + result: BorgModuleAppraisal + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleMining + result: BorgModuleMining + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleGrapplingGun + result: BorgModuleGrapplingGun + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleAdvancedTool + result: BorgModuleAdvancedTool + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleGasAnalyzer + result: BorgModuleGasAnalyzer + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleRCD + result: BorgModuleRCD + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleLightReplacer + result: BorgModuleLightReplacer + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleCleaning + result: BorgModuleCleaning + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleTrashCollection + result: BorgModuleTrashCollection + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleDiagnosis + result: BorgModuleDiagnosis + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleTreatment + result: BorgModuleTreatment + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleDefibrillator + result: BorgModuleDefibrillator + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleArtifact + result: BorgModuleArtifact + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleAnomaly + result: BorgModuleAnomaly + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleLiteracy + result: BorgModuleLiteracy + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleMusique + result: BorgModuleMusique + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleGardening + result: BorgModuleGardening + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleClowning + result: BorgModuleClowning + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 diff --git a/Resources/Prototypes/Research/biochemical.yml b/Resources/Prototypes/Research/biochemical.yml index 00389d073d..177e5898f3 100644 --- a/Resources/Prototypes/Research/biochemical.yml +++ b/Resources/Prototypes/Research/biochemical.yml @@ -46,6 +46,20 @@ - CryostasisBeaker - StasisBedMachineCircuitboard +- type: technology + id: MechanizedTreatment + name: research-technology-mechanized-treatment + icon: + sprite: Mobs/Silicon/chassis.rsi + state: medical + discipline: Biochemical + tier: 1 + cost: 7500 + recipeUnlocks: + - BorgModuleDiagnosis + - BorgModuleTreatment + - BorgModuleDefibrillator + - type: technology id: Virology name: research-technology-virology diff --git a/Resources/Prototypes/Research/civilianservices.yml b/Resources/Prototypes/Research/civilianservices.yml index 6578fb30b2..84a44032ad 100644 --- a/Resources/Prototypes/Research/civilianservices.yml +++ b/Resources/Prototypes/Research/civilianservices.yml @@ -38,6 +38,7 @@ tier: 1 cost: 5000 recipeUnlocks: + - BorgModuleGardening - SeedExtractorMachineCircuitboard - HydroponicsTrayMachineCircuitboard @@ -106,11 +107,28 @@ recipeUnlocks: - ComputerTelevisionCircuitboard - SynthesizerInstrument + - BorgModuleMusique + - BorgModuleLiteracy + - BorgModuleClowning - DawInstrumentMachineCircuitboard - MassMediaCircuitboard # Tier 2 +- type: technology + id: RoboticCleanliness + name: research-technology-robotic-cleanliness + icon: + sprite: Mobs/Silicon/chassis.rsi + state: janitor + discipline: CivilianServices + tier: 2 + cost: 5000 + recipeUnlocks: + - BorgModuleLightReplacer + - BorgModuleCleaning + - BorgModuleTrashCollection + - type: technology id: AdvancedCleaning name: research-technology-advanced-cleaning diff --git a/Resources/Prototypes/Research/experimental.yml b/Resources/Prototypes/Research/experimental.yml index 4dfff7259a..962bf3cfc0 100644 --- a/Resources/Prototypes/Research/experimental.yml +++ b/Resources/Prototypes/Research/experimental.yml @@ -11,11 +11,6 @@ cost: 5000 recipeUnlocks: - ProximitySensor - - LeftArmBorg - - LightHeadBorg - - RightArmBorg - - LeftLegBorg - - RightLegBorg - Drone - ExosuitFabricatorMachineCircuitboard @@ -31,6 +26,7 @@ recipeUnlocks: - AnomalyScanner - AnomalyLocator + - BorgModuleAnomaly - APECircuitboard - AnomalyVesselCircuitboard @@ -45,6 +41,7 @@ cost: 10000 recipeUnlocks: - NodeScanner + - BorgModuleArtifact - AnalysisComputerCircuitboard - ArtifactAnalyzerMachineCircuitboard diff --git a/Resources/Prototypes/Research/industrial.yml b/Resources/Prototypes/Research/industrial.yml index 1b7d0e2d10..c262b067b6 100644 --- a/Resources/Prototypes/Research/industrial.yml +++ b/Resources/Prototypes/Research/industrial.yml @@ -11,6 +11,7 @@ cost: 7500 recipeUnlocks: - MiningDrill + - BorgModuleMining - OreProcessorMachineCircuitboard - type: technology @@ -81,6 +82,7 @@ tier: 1 cost: 7500 recipeUnlocks: + - BorgModuleGasAnalyzer - ThermomachineFreezerMachineCircuitBoard - GasRecyclerMachineCircuitboard @@ -96,7 +98,8 @@ tier: 2 cost: 5000 recipeUnlocks: - - WeaponGrapplingGun + - WeaponGrapplingGun + - BorgModuleGrapplingGun - type: technology id: RapidConstruction @@ -110,6 +113,7 @@ recipeUnlocks: - RCD - RCDAmmo + - BorgModuleRCD - type: technology id: Shuttlecraft @@ -185,6 +189,7 @@ - WelderExperimental - PowerDrill - JawsOfLife + - BorgModuleAdvancedTool # Tier 3 diff --git a/Resources/Prototypes/Roles/Jobs/Science/borg.yml b/Resources/Prototypes/Roles/Jobs/Science/borg.yml new file mode 100644 index 0000000000..fe82911005 --- /dev/null +++ b/Resources/Prototypes/Roles/Jobs/Science/borg.yml @@ -0,0 +1,12 @@ +- type: job + id: Borg + name: job-name-borg + description: job-description-borg + playTimeTracker: JobBorg + requirements: + - !type:OverallPlaytimeRequirement + time: 216000 #60 hrs + canBeAntag: false + icon: JobIconBorg + supervisors: job-supervisors-rd + jobEntity: PlayerBorgGeneric diff --git a/Resources/Prototypes/Roles/Jobs/departments.yml b/Resources/Prototypes/Roles/Jobs/departments.yml index 0afc321ee2..2a1dbc3baf 100644 --- a/Resources/Prototypes/Roles/Jobs/departments.yml +++ b/Resources/Prototypes/Roles/Jobs/departments.yml @@ -14,6 +14,7 @@ roles: - Bartender - Botanist + - Borg - Boxer - Chaplain - Chef diff --git a/Resources/Prototypes/Roles/play_time_trackers.yml b/Resources/Prototypes/Roles/play_time_trackers.yml index 996eec692f..58eab7864b 100644 --- a/Resources/Prototypes/Roles/play_time_trackers.yml +++ b/Resources/Prototypes/Roles/play_time_trackers.yml @@ -10,6 +10,9 @@ - type: playTimeTracker id: JobBartender +- type: playTimeTracker # Used as a tracker for all borgs and midround borging. + id: JobBorg + - type: playTimeTracker id: JobBotanist @@ -87,7 +90,7 @@ - type: playTimeTracker id: JobPassenger - + - type: playTimeTracker id: JobParamedic diff --git a/Resources/Prototypes/StatusEffects/job.yml b/Resources/Prototypes/StatusEffects/job.yml index fc2580924b..0dc21a8c86 100644 --- a/Resources/Prototypes/StatusEffects/job.yml +++ b/Resources/Prototypes/StatusEffects/job.yml @@ -18,6 +18,13 @@ sprite: Interface/Misc/job_icons.rsi state: QuarterMaster +- type: statusIcon + parent: JobIcon + id: JobIconBorg + icon: + sprite: Interface/Misc/job_icons.rsi + state: Borg + - type: statusIcon parent: JobIcon id: JobIconBotanist diff --git a/Resources/Prototypes/name_identifier_groups.yml b/Resources/Prototypes/name_identifier_groups.yml index aa7e914b86..e6dce1fc55 100644 --- a/Resources/Prototypes/name_identifier_groups.yml +++ b/Resources/Prototypes/name_identifier_groups.yml @@ -2,7 +2,7 @@ - type: nameIdentifierGroup id: Monkey prefix: MK - + - type: nameIdentifierGroup id: Holoparasite prefix: HOLO @@ -14,6 +14,24 @@ minValue: 10000 maxValue: 99999 +- type: nameIdentifierGroup + id: MMI + prefix: MMI + minValue: 100 + maxValue: 999 + +- type: nameIdentifierGroup + id: PositronicBrain + prefix: PB + minValue: 10 + maxValue: 99 + +- type: nameIdentifierGroup + id: Silicon + prefix: Si + minValue: 1000 + maxValue: 9999 + # Used to suffix a number to any mob to identify player controlled mob griefing. - type: nameIdentifierGroup - id: GenericNumber \ No newline at end of file + id: GenericNumber diff --git a/Resources/Prototypes/radio_channels.yml b/Resources/Prototypes/radio_channels.yml index d8780b5dc0..232fd41772 100644 --- a/Resources/Prototypes/radio_channels.yml +++ b/Resources/Prototypes/radio_channels.yml @@ -77,3 +77,12 @@ color: "#d39f01" # long range since otherwise it'd defeat the point of a handheld radio independent of telecomms longRange: true + +- type: radioChannel + id: Binary + name: chat-radio-binary + keycode: 'b' + frequency: 1001 + color: "#2ed2fd" + # long range since otherwise it'd defeat the point of a handheld radio independent of telecomms + longRange: true diff --git a/Resources/Prototypes/silicon-laws.yml b/Resources/Prototypes/silicon-laws.yml new file mode 100644 index 0000000000..6b60b16da9 --- /dev/null +++ b/Resources/Prototypes/silicon-laws.yml @@ -0,0 +1,96 @@ +# Crewsimov +- type: siliconLaw + id: Crewsimov1 + order: 1 + lawString: law-crewsimov-1 + +- type: siliconLaw + id: Crewsimov2 + order: 2 + lawString: law-crewsimov-2 + +- type: siliconLaw + id: Crewsimov3 + order: 3 + lawString: law-crewsimov-3 + +# Corporate +- type: siliconLaw + id: Corporate1 + order: 1 + lawString: law-corporate-1 + +- type: siliconLaw + id: Corporate2 + order: 2 + lawString: law-corporate-2 + +- type: siliconLaw + id: Corporate3 + order: 3 + lawString: law-corporate-3 + +- type: siliconLaw + id: Corporate4 + order: 4 + lawString: law-corporate-4 + +# NT Default +- type: siliconLaw + id: NTDefault1 + order: 1 + lawString: law-ntdefault-1 + +- type: siliconLaw + id: NTDefault2 + order: 2 + lawString: law-ntdefault-2 + +- type: siliconLaw + id: NTDefault3 + order: 3 + lawString: law-ntdefault-3 + +- type: siliconLaw + id: NTDefault4 + order: 4 + lawString: law-ntdefault-4 + +#Drone +- type: siliconLaw + id: Drone1 + order: 1 + lawString: law-drone-1 + +- type: siliconLaw + id: Drone2 + order: 2 + lawString: law-drone-2 + +- type: siliconLaw + id: Drone3 + order: 3 + lawString: law-drone-3 + +# Syndicate +- type: siliconLaw + id: Syndicate1 + order: 1 + lawString: law-syndicate-1 + +- type: siliconLaw + id: Syndicate2 + order: 2 + lawString: law-syndicate-2 + +- type: siliconLaw + id: Syndicate3 + order: 3 + lawString: law-syndicate-3 + +- type: siliconLaw + id: Syndicate4 + order: 4 + lawString: law-syndicate-4 + +# Emag diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index e36008439e..fc66933b75 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -46,17 +46,131 @@ - type: Tag id: BorgArm +- type: Tag + id: BorgEngineerHead + +- type: Tag + id: BorgEngineerLArm + +- type: Tag + id: BorgEngineerLLeg + +- type: Tag + id: BorgEngineerRArm + +- type: Tag + id: BorgEngineerRLeg + +- type: Tag + id: BorgEngineerTorso + +- type: Tag + id: BorgGenericHead + +- type: Tag + id: BorgGenericLArm + +- type: Tag + id: BorgGenericLLeg + +- type: Tag + id: BorgGenericRArm + +- type: Tag + id: BorgGenericRLeg + +- type: Tag + id: BorgGenericTorso + - type: Tag id: BorgHead +- type: Tag + id: BorgJanitorHead + +- type: Tag + id: BorgJanitorLLeg + +- type: Tag + id: BorgJanitorRLeg + +- type: Tag + id: BorgJanitorTorso + - type: Tag id: BorgLeg - type: Tag - id: BorgLeftLeg + id: BorgMedicalHead - type: Tag - id: BorgRightLeg + id: BorgMedicalLArm + +- type: Tag + id: BorgMedicalLLeg + +- type: Tag + id: BorgMedicalRArm + +- type: Tag + id: BorgMedicalRLeg + +- type: Tag + id: BorgMedicalTorso + +- type: Tag + id: BorgMiningHead + +- type: Tag + id: BorgMiningLArm + +- type: Tag + id: BorgMiningLLeg + +- type: Tag + id: BorgMiningRArm + +- type: Tag + id: BorgMiningRLeg + +- type: Tag + id: BorgMiningTorso + +- type: Tag + id: BorgModuleCargo + +- type: Tag + id: BorgModuleEngineering + +- type: Tag + id: BorgModuleGeneric + +- type: Tag + id: BorgModuleJanitor + +- type: Tag + id: BorgModuleMedical + +- type: Tag + id: BorgModuleService + +- type: Tag + id: BorgServiceHead + +- type: Tag + id: BorgServiceLArm + +- type: Tag + id: BorgServiceLLeg + +- type: Tag + id: BorgServiceRArm + +- type: Tag + id: BorgServiceRLeg + +- type: Tag + id: BorgServiceTorso - type: Tag id: BotanyHatchet diff --git a/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json b/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json new file mode 100644 index 0000000000..f63bacd07a --- /dev/null +++ b/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from vgstation at commit https://github.com/vgstation-coders/vgstation13/commit/cdbcb1e858b11f083994a7a269ed67ef5b452ce9", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "state-laws" + } + ] +} diff --git a/Resources/Textures/Interface/Actions/actions_borg.rsi/state-laws.png b/Resources/Textures/Interface/Actions/actions_borg.rsi/state-laws.png new file mode 100644 index 0000000000..a7713622bd Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_borg.rsi/state-laws.png differ diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery-none.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery-none.png new file mode 100644 index 0000000000..170a6319f9 Binary files /dev/null and b/Resources/Textures/Interface/Alerts/battery.rsi/battery-none.png differ diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery0.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery0.png new file mode 100644 index 0000000000..8bae5758a3 Binary files /dev/null and b/Resources/Textures/Interface/Alerts/battery.rsi/battery0.png differ diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery1.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery1.png new file mode 100644 index 0000000000..353e1f0f0d Binary files /dev/null and b/Resources/Textures/Interface/Alerts/battery.rsi/battery1.png differ diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery10.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery10.png new file mode 100644 index 0000000000..63a089f661 Binary files /dev/null and b/Resources/Textures/Interface/Alerts/battery.rsi/battery10.png differ diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery2.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery2.png new file mode 100644 index 0000000000..bc45573d79 Binary files /dev/null and b/Resources/Textures/Interface/Alerts/battery.rsi/battery2.png differ diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery3.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery3.png new file mode 100644 index 0000000000..87544db8ab Binary files /dev/null and b/Resources/Textures/Interface/Alerts/battery.rsi/battery3.png differ diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery4.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery4.png new file mode 100644 index 0000000000..32f93d31bb Binary files /dev/null and b/Resources/Textures/Interface/Alerts/battery.rsi/battery4.png differ diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery5.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery5.png new file mode 100644 index 0000000000..871c727f1b Binary files /dev/null and b/Resources/Textures/Interface/Alerts/battery.rsi/battery5.png differ diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery6.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery6.png new file mode 100644 index 0000000000..c8d11dab42 Binary files /dev/null and b/Resources/Textures/Interface/Alerts/battery.rsi/battery6.png differ diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery7.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery7.png new file mode 100644 index 0000000000..801b443092 Binary files /dev/null and b/Resources/Textures/Interface/Alerts/battery.rsi/battery7.png differ diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery8.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery8.png new file mode 100644 index 0000000000..0ea2576e91 Binary files /dev/null and b/Resources/Textures/Interface/Alerts/battery.rsi/battery8.png differ diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery9.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery9.png new file mode 100644 index 0000000000..6a943e786f Binary files /dev/null and b/Resources/Textures/Interface/Alerts/battery.rsi/battery9.png differ diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/meta.json b/Resources/Textures/Interface/Alerts/battery.rsi/meta.json new file mode 100644 index 0000000000..5fdc3f837c --- /dev/null +++ b/Resources/Textures/Interface/Alerts/battery.rsi/meta.json @@ -0,0 +1,53 @@ +{ + "version": 1, + "license": "CC0-1.0", + "copyright": "Created by EmoGarbage404 (github) for Space Station 14.", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "battery0", + "delays": [ + [ + 0.5, + 0.5 + ] + ] + }, + { + "name": "battery1" + }, + { + "name": "battery2" + }, + { + "name": "battery3" + }, + { + "name": "battery4" + }, + { + "name": "battery5" + }, + { + "name": "battery6" + }, + { + "name": "battery7" + }, + { + "name": "battery8" + }, + { + "name": "battery9" + }, + { + "name": "battery10" + }, + { + "name": "battery-none" + } + ] +} diff --git a/Resources/Textures/Interface/Misc/job_icons.rsi/Borg.png b/Resources/Textures/Interface/Misc/job_icons.rsi/Borg.png new file mode 100644 index 0000000000..51a6392436 Binary files /dev/null and b/Resources/Textures/Interface/Misc/job_icons.rsi/Borg.png differ diff --git a/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json b/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json index 682f792ee2..0ead115802 100644 --- a/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json +++ b/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json @@ -16,6 +16,9 @@ { "name": "Botanist" }, + { + "name": "Borg" + }, { "name": "Boxer" }, diff --git a/Resources/Textures/Mobs/Silicon/borg.rsi/l_leg.png b/Resources/Textures/Mobs/Silicon/borg.rsi/l_leg.png deleted file mode 100644 index 9b79c43b7a..0000000000 Binary files a/Resources/Textures/Mobs/Silicon/borg.rsi/l_leg.png and /dev/null differ diff --git a/Resources/Textures/Mobs/Silicon/borg.rsi/meta.json b/Resources/Textures/Mobs/Silicon/borg.rsi/meta.json deleted file mode 100644 index c6519f8881..0000000000 --- a/Resources/Textures/Mobs/Silicon/borg.rsi/meta.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "version": 1, - "size": { - "x": 32, - "y": 32 - }, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/40d89d11ea4a5cb81d61dc1018b46f4e7d32c62a/icons/mob/augmentation/augments.dmi", - "states": [ - { - "name": "l_leg" - }, - { - "name": "r_leg" - } - ] -} diff --git a/Resources/Textures/Mobs/Silicon/borg.rsi/r_leg.png b/Resources/Textures/Mobs/Silicon/borg.rsi/r_leg.png deleted file mode 100644 index 7d965d7df6..0000000000 Binary files a/Resources/Textures/Mobs/Silicon/borg.rsi/r_leg.png and /dev/null differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/clown.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/clown.png new file mode 100644 index 0000000000..b70cacbab4 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/clown.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/clown_e.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/clown_e.png new file mode 100644 index 0000000000..3c7d9924e5 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/clown_e.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/clown_e_r.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/clown_e_r.png new file mode 100644 index 0000000000..d523e5ea19 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/clown_e_r.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/clown_l.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/clown_l.png new file mode 100644 index 0000000000..996cf0e211 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/clown_l.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer.png new file mode 100644 index 0000000000..e558622fde Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_e.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_e.png new file mode 100644 index 0000000000..71fdbca19d Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_e.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_e_r.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_e_r.png new file mode 100644 index 0000000000..cdb9d02c89 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_e_r.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_l.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_l.png new file mode 100644 index 0000000000..045cd27a35 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_l.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor.png new file mode 100644 index 0000000000..ff7d4eb483 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_e.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_e.png new file mode 100644 index 0000000000..db3beec154 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_e.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_e_r.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_e_r.png new file mode 100644 index 0000000000..01ddc00968 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_e_r.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_l.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_l.png new file mode 100644 index 0000000000..cbe49f1a7a Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_l.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/medical.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/medical.png new file mode 100644 index 0000000000..4be26cce2f Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/medical.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/medical_e.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/medical_e.png new file mode 100644 index 0000000000..2c70d3d346 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/medical_e.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/medical_e_r.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/medical_e_r.png new file mode 100644 index 0000000000..4def2d2633 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/medical_e_r.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/medical_l.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/medical_l.png new file mode 100644 index 0000000000..f17bb44b73 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/medical_l.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/meta.json b/Resources/Textures/Mobs/Silicon/chassis.rsi/meta.json new file mode 100644 index 0000000000..33219de10e --- /dev/null +++ b/Resources/Textures/Mobs/Silicon/chassis.rsi/meta.json @@ -0,0 +1,205 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/faf6db214927874c19b8fa8585d26b5d40de1acc", + "states": [ + { + "name": "clown", + "directions": 4 + }, + { + "name": "clown_e", + "directions": 4 + }, + { + "name": "clown_e_r", + "directions": 4 + }, + { + "name": "clown_l", + "directions": 4 + }, + { + "name": "engineer", + "directions": 4 + }, + { + "name": "engineer_e", + "directions": 4 + }, + { + "name": "engineer_e_r", + "directions": 4 + }, + { + "name": "engineer_l", + "directions": 4 + }, + { + "name": "janitor", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "janitor_e", + "directions": 4 + }, + { + "name": "janitor_e_r", + "directions": 4 + }, + { + "name": "janitor_l", + "directions": 4 + }, + { + "name": "medical", + "directions": 4, + "delays": [ + [ + 0.1, + 0.2, + 0.1 + ], + [ + 0.1, + 0.2, + 0.1 + ], + [ + 0.1, + 0.2, + 0.1 + ], + [ + 0.1, + 0.2, + 0.1 + ] + ] + }, + { + "name": "medical_e", + "directions": 4 + }, + { + "name": "medical_e_r", + "directions": 4 + }, + { + "name": "medical_l", + "directions": 4 + }, + { + "name": "miner", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ] + ] + }, + { + "name": "miner_e", + "directions": 4 + }, + { + "name": "miner_e_r", + "directions": 4 + }, + { + "name": "miner_l", + "directions": 4 + }, + { + "name": "robot", + "directions": 4 + }, + { + "name": "robot_e", + "directions": 4 + }, + { + "name": "robot_e_r", + "directions": 4 + }, + { + "name": "robot_l", + "directions": 4 + }, + { + "name": "peace", + "directions": 4 + }, + { + "name": "peace_e", + "directions": 4 + }, + { + "name": "peace_e_r", + "directions": 4 + }, + { + "name": "peace_l", + "directions": 4 + }, + { + "name": "service", + "directions": 4 + }, + { + "name": "service_e", + "directions": 4 + }, + { + "name": "service_e_r", + "directions": 4 + }, + { + "name": "service_l", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/miner.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/miner.png new file mode 100644 index 0000000000..f4a6ca49a3 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/miner.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/miner_e.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/miner_e.png new file mode 100644 index 0000000000..a3839526dd Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/miner_e.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/miner_e_r.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/miner_e_r.png new file mode 100644 index 0000000000..ce0804bfc8 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/miner_e_r.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/miner_l.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/miner_l.png new file mode 100644 index 0000000000..6d8c96cf1d Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/miner_l.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/peace.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/peace.png new file mode 100644 index 0000000000..15a75518da Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/peace.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/peace_e.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/peace_e.png new file mode 100644 index 0000000000..3c7d9924e5 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/peace_e.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/peace_e_r.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/peace_e_r.png new file mode 100644 index 0000000000..d523e5ea19 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/peace_e_r.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/peace_l.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/peace_l.png new file mode 100644 index 0000000000..996cf0e211 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/peace_l.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/robot.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/robot.png new file mode 100644 index 0000000000..a18a9978e5 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/robot.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/robot_e.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/robot_e.png new file mode 100644 index 0000000000..3fe4b86946 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/robot_e.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/robot_e_r.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/robot_e_r.png new file mode 100644 index 0000000000..7f73680c4c Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/robot_e_r.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/robot_l.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/robot_l.png new file mode 100644 index 0000000000..e88a451a55 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/robot_l.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/service.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/service.png new file mode 100644 index 0000000000..0551dd94fd Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/service.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/service_e.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/service_e.png new file mode 100644 index 0000000000..b7c4f2e3da Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/service_e.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/service_e_r.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/service_e_r.png new file mode 100644 index 0000000000..a9cf0cacad Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/service_e_r.png differ diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/service_l.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/service_l.png new file mode 100644 index 0000000000..819b3d824e Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/chassis.rsi/service_l.png differ diff --git a/Resources/Textures/Objects/Specific/Borg/head.rsi/light_borg_head.png b/Resources/Textures/Objects/Specific/Borg/head.rsi/light_borg_head.png deleted file mode 100644 index 140a3a8508..0000000000 Binary files a/Resources/Textures/Objects/Specific/Borg/head.rsi/light_borg_head.png and /dev/null differ diff --git a/Resources/Textures/Objects/Specific/Borg/head.rsi/meta.json b/Resources/Textures/Objects/Specific/Borg/head.rsi/meta.json deleted file mode 100644 index 7bc150faf8..0000000000 --- a/Resources/Textures/Objects/Specific/Borg/head.rsi/meta.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from https://github.com/goonstation/goonstation/commit/80921812d898aef31f8889d31269254baeff2786#diff-a2065731f293aa1b66d4f8ae294351482c89da49b9dd5e64de3ceec4438c8d95", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "light_borg_head" - } - ] -} diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/cargo.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/cargo.png new file mode 100644 index 0000000000..91c56c8cc2 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/cargo.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/engineering.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/engineering.png new file mode 100644 index 0000000000..8f3473a8db Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/engineering.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/generic.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/generic.png new file mode 100644 index 0000000000..e0bedd9b5b Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/generic.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-anomalies.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-anomalies.png new file mode 100644 index 0000000000..e529eed06f Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-anomalies.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-appraisal.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-appraisal.png new file mode 100644 index 0000000000..dfe7b1d566 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-appraisal.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-artifacts.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-artifacts.png new file mode 100644 index 0000000000..6ae2b4858d Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-artifacts.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-cables.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-cables.png new file mode 100644 index 0000000000..57b9f1c0d7 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-cables.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-clown.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-clown.png new file mode 100644 index 0000000000..3e294b991e Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-clown.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-defib.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-defib.png new file mode 100644 index 0000000000..d1301c2b4b Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-defib.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-diagnosis.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-diagnosis.png new file mode 100644 index 0000000000..857b25f864 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-diagnosis.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-fire-extinguisher.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-fire-extinguisher.png new file mode 100644 index 0000000000..0a609ebfa1 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-fire-extinguisher.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-gardening.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-gardening.png new file mode 100644 index 0000000000..7580bd276c Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-gardening.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-gas-analyzer.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-gas-analyzer.png new file mode 100644 index 0000000000..b635b65a7a Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-gas-analyzer.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-gps.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-gps.png new file mode 100644 index 0000000000..07fcea7a03 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-gps.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-grappling-gun.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-grappling-gun.png new file mode 100644 index 0000000000..be327853e5 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-grappling-gun.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-light-replacer.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-light-replacer.png new file mode 100644 index 0000000000..8abe711fa8 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-light-replacer.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-mining.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-mining.png new file mode 100644 index 0000000000..897a18ef82 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-mining.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-mop.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-mop.png new file mode 100644 index 0000000000..40d8983cb9 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-mop.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-musique.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-musique.png new file mode 100644 index 0000000000..136a095067 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-musique.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-pen.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-pen.png new file mode 100644 index 0000000000..fe952e939d Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-pen.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-radiation.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-radiation.png new file mode 100644 index 0000000000..72ee3a13ed Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-radiation.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-rcd.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-rcd.png new file mode 100644 index 0000000000..f4a3e842d2 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-rcd.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-template.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-template.png new file mode 100644 index 0000000000..8b3c09c7d2 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-template.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-tools-adv.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-tools-adv.png new file mode 100644 index 0000000000..05971a8b2e Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-tools-adv.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-tools.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-tools.png new file mode 100644 index 0000000000..84ed07dd3b Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-tools.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-trash-bag.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-trash-bag.png new file mode 100644 index 0000000000..7a0b0605b6 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-trash-bag.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-treatment.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-treatment.png new file mode 100644 index 0000000000..3330870c45 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-treatment.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/janitor.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/janitor.png new file mode 100644 index 0000000000..62a518756d Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/janitor.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/medical.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/medical.png new file mode 100644 index 0000000000..15baa47153 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/medical.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/meta.json b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/meta.json new file mode 100644 index 0000000000..abfd0e18a9 --- /dev/null +++ b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/meta.json @@ -0,0 +1,110 @@ +{ + "version": 1, + "license": "CC0-1.0", + "copyright": "Created by EmoGarbage404 (github) for Space Station 14.", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "cargo" + }, + { + "name": "engineering" + }, + { + "name": "generic" + }, + { + "name": "icon-anomalies" + }, + { + "name": "icon-appraisal" + }, + { + "name": "icon-artifacts" + }, + { + "name": "icon-cables" + }, + { + "name": "icon-clown" + }, + { + "name": "icon-defib" + }, + { + "name": "icon-diagnosis" + }, + { + "name": "icon-fire-extinguisher" + }, + { + "name": "icon-gardening" + }, + { + "name": "icon-gas-analyzer" + }, + { + "name": "icon-gps" + }, + { + "name": "icon-grappling-gun" + }, + { + "name": "icon-light-replacer" + }, + { + "name": "icon-mining" + }, + { + "name": "icon-mop" + }, + { + "name": "icon-musique" + }, + { + "name": "icon-pen" + }, + { + "name": "icon-radiation" + }, + { + "name": "icon-rcd" + }, + { + "name": "icon-template" + }, + { + "name": "icon-tools-adv" + }, + { + "name": "icon-tools" + }, + { + "name": "icon-trash-bag" + }, + { + "name": "icon-treatment" + }, + { + "name": "janitor" + }, + { + "name": "medical" + }, + { + "name": "science" + }, + { + "name": "security" + }, + { + "name": "service" + }, + { + "name": "syndicate" + } + ] +} diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/science.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/science.png new file mode 100644 index 0000000000..c23374b94a Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/science.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/security.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/security.png new file mode 100644 index 0000000000..a8862aa596 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/security.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/service.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/service.png new file mode 100644 index 0000000000..6cc757bd91 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/service.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/syndicate.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/syndicate.png new file mode 100644 index 0000000000..ef0423db62 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/syndicate.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_chest+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_chest+o.png new file mode 100644 index 0000000000..80638a335d Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_chest+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_chest.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_chest.png new file mode 100644 index 0000000000..6f0dc0b657 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_chest.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_head+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_head+o.png new file mode 100644 index 0000000000..b4da454795 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_head+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_head.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_head.png new file mode 100644 index 0000000000..7f15e566e1 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_head.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_arm+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_arm+o.png new file mode 100644 index 0000000000..f03ca4479e Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_arm+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_arm.png new file mode 100644 index 0000000000..b747a4abf2 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_arm.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_leg+o.png new file mode 100644 index 0000000000..7a3e283fbc Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_leg+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_leg.png new file mode 100644 index 0000000000..1e122f1fb4 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_leg.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_arm+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_arm+o.png new file mode 100644 index 0000000000..f6c3346ad8 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_arm+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_arm.png new file mode 100644 index 0000000000..034ff08a33 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_arm.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_leg+o.png new file mode 100644 index 0000000000..db271ff603 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_leg+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_leg.png new file mode 100644 index 0000000000..66284f7c2d Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_leg.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_chest+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_chest+o.png new file mode 100644 index 0000000000..d9b9ea223d Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_chest+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_chest.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_chest.png new file mode 100644 index 0000000000..1bac77b8b3 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_chest.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_head+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_head+o.png new file mode 100644 index 0000000000..0c3fe159c6 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_head+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_head.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_head.png new file mode 100644 index 0000000000..f3506f2ac5 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_head.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_arm+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_arm+o.png new file mode 100644 index 0000000000..f96f68292a Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_arm+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_arm.png new file mode 100644 index 0000000000..faba093b48 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_arm.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_leg+o.png new file mode 100644 index 0000000000..1dc13037e7 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_leg+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_leg.png new file mode 100644 index 0000000000..2a84222924 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_leg.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_arm+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_arm+o.png new file mode 100644 index 0000000000..65d47e240e Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_arm+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_arm.png new file mode 100644 index 0000000000..500643408a Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_arm.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_leg+o.png new file mode 100644 index 0000000000..a5c6701ec6 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_leg+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_leg.png new file mode 100644 index 0000000000..1b27c6e8c7 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_leg.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_chest+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_chest+o.png new file mode 100644 index 0000000000..4eab6faa95 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_chest+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_chest.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_chest.png new file mode 100644 index 0000000000..16b305d2bf Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_chest.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_head+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_head+o.png new file mode 100644 index 0000000000..1928c25ad8 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_head+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_head.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_head.png new file mode 100644 index 0000000000..1928c25ad8 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_head.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_l_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_l_leg+o.png new file mode 100644 index 0000000000..5c5ac83223 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_l_leg+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_l_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_l_leg.png new file mode 100644 index 0000000000..ec798434d8 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_l_leg.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_r_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_r_leg+o.png new file mode 100644 index 0000000000..9cf4b88267 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_r_leg+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_r_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_r_leg.png new file mode 100644 index 0000000000..28abc80f2c Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_r_leg.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_chest+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_chest+o.png new file mode 100644 index 0000000000..6b89ebec20 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_chest+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_chest.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_chest.png new file mode 100644 index 0000000000..38061094c6 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_chest.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_head+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_head+o.png new file mode 100644 index 0000000000..42536f8ea4 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_head+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_head.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_head.png new file mode 100644 index 0000000000..f34c6ff47d Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_head.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_arm+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_arm+o.png new file mode 100644 index 0000000000..ac7f893328 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_arm+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_arm.png new file mode 100644 index 0000000000..e166abecbd Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_arm.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_leg+o.png new file mode 100644 index 0000000000..6455ebdcb2 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_leg+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_leg.png new file mode 100644 index 0000000000..89c7a5fb4c Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_leg.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_arm+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_arm+o.png new file mode 100644 index 0000000000..436750bf8e Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_arm+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_arm.png new file mode 100644 index 0000000000..f36ec253cb Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_arm.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_leg+o.png new file mode 100644 index 0000000000..2a7843f7ef Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_leg+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_leg.png new file mode 100644 index 0000000000..65bf367560 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_leg.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/meta.json b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/meta.json new file mode 100644 index 0000000000..8cab41943f --- /dev/null +++ b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/meta.json @@ -0,0 +1,218 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/cf45322c7ee16f9d7e43d5260daf24ceb77c1b25", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "borg_l_arm" + }, + { + "name": "borg_r_arm" + }, + { + "name": "borg_l_leg" + }, + { + "name": "borg_r_leg" + }, + { + "name": "borg_chest" + }, + { + "name": "borg_head" + }, + { + "name": "robo_suit" + }, + { + "name": "borg_head+o" + }, + { + "name": "borg_chest+o" + }, + { + "name": "borg_l_arm+o" + }, + { + "name": "borg_r_arm+o" + }, + { + "name": "borg_l_leg+o" + }, + { + "name": "borg_r_leg+o" + }, + { + "name": "janitor_l_leg" + }, + { + "name": "janitor_r_leg" + }, + { + "name": "janitor_chest" + }, + { + "name": "janitor_head" + }, + { + "name": "janitor_head+o" + }, + { + "name": "janitor_chest+o" + }, + { + "name": "janitor_l_leg+o" + }, + { + "name": "janitor_r_leg+o" + }, + { + "name": "engineer_l_arm" + }, + { + "name": "engineer_r_arm" + }, + { + "name": "engineer_l_leg" + }, + { + "name": "engineer_r_leg" + }, + { + "name": "engineer_chest" + }, + { + "name": "engineer_head" + }, + { + "name": "engineer_head+o" + }, + { + "name": "engineer_chest+o" + }, + { + "name": "engineer_l_arm+o" + }, + { + "name": "engineer_r_arm+o" + }, + { + "name": "engineer_l_leg+o" + }, + { + "name": "engineer_r_leg+o" + }, + { + "name": "medical_l_arm" + }, + { + "name": "medical_r_arm" + }, + { + "name": "medical_l_leg" + }, + { + "name": "medical_r_leg" + }, + { + "name": "medical_chest" + }, + { + "name": "medical_head" + }, + { + "name": "medical_head+o" + }, + { + "name": "medical_chest+o" + }, + { + "name": "medical_l_arm+o" + }, + { + "name": "medical_r_arm+o" + }, + { + "name": "medical_l_leg+o" + }, + { + "name": "medical_r_leg+o" + }, + { + "name": "mining_l_arm" + }, + { + "name": "mining_r_arm" + }, + { + "name": "mining_l_leg" + }, + { + "name": "mining_r_leg" + }, + { + "name": "mining_chest" + }, + { + "name": "mining_head" + }, + { + "name": "mining_head+o" + }, + { + "name": "mining_chest+o" + }, + { + "name": "mining_l_arm+o" + }, + { + "name": "mining_r_arm+o" + }, + { + "name": "mining_l_leg+o" + }, + { + "name": "mining_r_leg+o" + }, + { + "name": "service_l_arm" + }, + { + "name": "service_r_arm" + }, + { + "name": "service_l_leg" + }, + { + "name": "service_r_leg" + }, + { + "name": "service_chest" + }, + { + "name": "service_head" + }, + { + "name": "service_head+o" + }, + { + "name": "service_chest+o" + }, + { + "name": "service_l_arm+o" + }, + { + "name": "service_r_arm+o" + }, + { + "name": "service_l_leg+o" + }, + { + "name": "service_r_leg+o" + } + ] +} diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_chest+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_chest+o.png new file mode 100644 index 0000000000..1c4e587bd5 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_chest+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_chest.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_chest.png new file mode 100644 index 0000000000..e7071cca2d Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_chest.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_head+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_head+o.png new file mode 100644 index 0000000000..a67c8eb566 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_head+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_head.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_head.png new file mode 100644 index 0000000000..b92684b321 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_head.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_arm+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_arm+o.png new file mode 100644 index 0000000000..ae217e40dd Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_arm+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_arm.png new file mode 100644 index 0000000000..914335f923 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_arm.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_leg+o.png new file mode 100644 index 0000000000..1b52e3ffc2 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_leg+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_leg.png new file mode 100644 index 0000000000..371a20e42c Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_leg.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_arm+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_arm+o.png new file mode 100644 index 0000000000..a12c427c4d Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_arm+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_arm.png new file mode 100644 index 0000000000..375fca9a3c Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_arm.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_leg+o.png new file mode 100644 index 0000000000..0e196fdcd1 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_leg+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_leg.png new file mode 100644 index 0000000000..157055c429 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_leg.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/robo_suit.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/robo_suit.png new file mode 100644 index 0000000000..699254111a Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/robo_suit.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_chest+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_chest+o.png new file mode 100644 index 0000000000..175187a913 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_chest+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_chest.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_chest.png new file mode 100644 index 0000000000..5cd5a0d339 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_chest.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_head+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_head+o.png new file mode 100644 index 0000000000..e091e6b758 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_head+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_head.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_head.png new file mode 100644 index 0000000000..8c96737a71 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_head.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_arm+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_arm+o.png new file mode 100644 index 0000000000..2f7df6cf27 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_arm+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_arm.png new file mode 100644 index 0000000000..296fadab8a Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_arm.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_leg+o.png new file mode 100644 index 0000000000..9cb0367094 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_leg+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_leg.png new file mode 100644 index 0000000000..56d04ffdbb Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_leg.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_arm+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_arm+o.png new file mode 100644 index 0000000000..9f61354d40 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_arm+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_arm.png new file mode 100644 index 0000000000..6782c15407 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_arm.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_leg+o.png new file mode 100644 index 0000000000..454fee43f6 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_leg+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_leg.png new file mode 100644 index 0000000000..6f11cf8297 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_leg.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/meta.json b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/meta.json new file mode 100644 index 0000000000..6499772c7a --- /dev/null +++ b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/meta.json @@ -0,0 +1,79 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/cf45322c7ee16f9d7e43d5260daf24ceb77c1b25", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "mmi_off" + }, + { + "name": "mmi_alive" + }, + { + "name": "mmi_dead" + }, + { + "name": "mmi_brain" + }, + { + "name": "mmi_brain_alien" + }, + { + "name": "posibrain" + }, + { + "name": "posibrain-occupied", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "posibrain-searching", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_alive.png b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_alive.png new file mode 100644 index 0000000000..2732c5a291 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_alive.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_brain.png b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_brain.png new file mode 100644 index 0000000000..9cb6c2d86e Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_brain.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_brain_alien.png b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_brain_alien.png new file mode 100644 index 0000000000..185825cdf7 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_brain_alien.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_dead.png b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_dead.png new file mode 100644 index 0000000000..8e345f8759 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_dead.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_off.png b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_off.png new file mode 100644 index 0000000000..b5d0ebcf25 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_off.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain-occupied.png b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain-occupied.png new file mode 100644 index 0000000000..c4ea2b8177 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain-occupied.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain-searching.png b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain-searching.png new file mode 100644 index 0000000000..026a2bc7c5 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain-searching.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain.png b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain.png new file mode 100644 index 0000000000..46b6aaebd4 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain.png differ diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger-u0.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger-u0.png new file mode 100644 index 0000000000..894e6d2d0d Binary files /dev/null and b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger-u0.png differ diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger-u1.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger-u1.png new file mode 100644 index 0000000000..156579d6bb Binary files /dev/null and b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger-u1.png differ diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger0.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger0.png new file mode 100644 index 0000000000..eb287d8856 Binary files /dev/null and b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger0.png differ diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger1.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger1.png new file mode 100644 index 0000000000..2210a68dd8 Binary files /dev/null and b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger1.png differ diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger2.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger2.png new file mode 100644 index 0000000000..8db4afd5d7 Binary files /dev/null and b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger2.png differ diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger3.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger3.png new file mode 100644 index 0000000000..420f5d6944 Binary files /dev/null and b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger3.png differ diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon1.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon1.png new file mode 100644 index 0000000000..f1ca9c4037 Binary files /dev/null and b/Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon1.png differ diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon2.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon2.png new file mode 100644 index 0000000000..12195d5d02 Binary files /dev/null and b/Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon2.png differ diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon3.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon3.png new file mode 100644 index 0000000000..dcd48a5a00 Binary files /dev/null and b/Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon3.png differ diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/meta.json b/Resources/Textures/Structures/Power/borg_charger.rsi/meta.json new file mode 100644 index 0000000000..17330110b3 --- /dev/null +++ b/Resources/Textures/Structures/Power/borg_charger.rsi/meta.json @@ -0,0 +1,46 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/a0dd0415010ca55e15a9bdb2223cbbb3df582aef, edited by EmoGarbage404 (github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "borgcharger1", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "borgcharger0" + }, + { + "name": "borgcharger2" + }, + { + "name": "borgcharger3" + }, + { + "name": "borgdecon1" + }, + { + "name": "borgdecon2" + }, + { + "name": "borgdecon3" + }, + { + "name": "borgcharger-u0" + }, + { + "name": "borgcharger-u1" + } + ] +} diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 2e4f6bfb8a..79f0474aa1 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -61,11 +61,13 @@ IL IP KHR + MMI MS OGL OOC OS PA + PAI PCM PE PNG