From 264a63b7f6bd3c07c41b34ea7a5ee856c4f9f583 Mon Sep 17 00:00:00 2001 From: DamianX Date: Sun, 1 Sep 2019 22:15:34 +0200 Subject: [PATCH] Wires! (#315) * Wires! * Use state instead of messages * cleanup * Update submodule * actually fix conflict * Maybe fix conflicts? * Localized strings, removed hardcoded sprite path * cleanup * More localization and sounds --- Content.Client/EntryPoint.cs | 2 + .../Wires/WiresBoundUserInterface.cs | 37 +++ .../GameObjects/Components/Wires/WiresMenu.cs | 60 +++++ .../Components/Wires/WiresVisualizer2D.cs | 25 ++ .../VendingMachineComponent.cs | 74 +++++- .../GameObjects/Components/WiresComponent.cs | 251 ++++++++++++++++++ .../Components/SharedWiresComponent.cs | 72 +++++ Content.Shared/GameObjects/ContentNetIDs.cs | 1 + Resources/Audio/effects/multitool_pulse.ogg | Bin 0 -> 7891 bytes .../Entities/buildings/vending_machines.yml | 6 + .../Textures/Buildings/maintenance_panel.png | Bin 0 -> 147 bytes 11 files changed, 524 insertions(+), 4 deletions(-) create mode 100644 Content.Client/GameObjects/Components/Wires/WiresBoundUserInterface.cs create mode 100644 Content.Client/GameObjects/Components/Wires/WiresMenu.cs create mode 100644 Content.Client/GameObjects/Components/Wires/WiresVisualizer2D.cs create mode 100644 Content.Server/GameObjects/Components/WiresComponent.cs create mode 100644 Content.Shared/GameObjects/Components/SharedWiresComponent.cs create mode 100644 Resources/Audio/effects/multitool_pulse.ogg create mode 100644 Resources/Textures/Buildings/maintenance_panel.png diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index ee2ccbca0a..d3032c8720 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -8,6 +8,7 @@ using Content.Client.Interfaces.Chat; using Content.Client.Interfaces.Parallax; using Content.Client.Parallax; using Content.Client.UserInterface; +using Content.Shared.GameObjects.Components; using Content.Shared.GameObjects.Components.Chemistry; using Content.Shared.GameObjects.Components.Markers; using Content.Shared.GameObjects.Components.Research; @@ -110,6 +111,7 @@ namespace Content.Client factory.Register(); factory.Register(); + factory.Register(); prototypes.RegisterIgnore("material"); diff --git a/Content.Client/GameObjects/Components/Wires/WiresBoundUserInterface.cs b/Content.Client/GameObjects/Components/Wires/WiresBoundUserInterface.cs new file mode 100644 index 0000000000..f6f9db605a --- /dev/null +++ b/Content.Client/GameObjects/Components/Wires/WiresBoundUserInterface.cs @@ -0,0 +1,37 @@ +using System; +using Robust.Client.GameObjects.Components.UserInterface; +using Robust.Shared.GameObjects.Components.UserInterface; +using static Content.Shared.GameObjects.Components.SharedWiresComponent; + +namespace Content.Client.GameObjects.Components.Wires +{ + public class WiresBoundUserInterface : BoundUserInterface + { + public WiresBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) + { + } + + private WiresMenu _menu; + + protected override void Open() + { + base.Open(); + _menu = new WiresMenu() {Owner = this}; + + _menu.OnClose += Close; + _menu.OpenCentered(); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + var castState = (WiresBoundUserInterfaceState) state; + _menu.Populate(castState.WiresList); + } + + public void PerformAction(Guid guid, WiresAction action) + { + SendMessage(new WiresActionMessage(guid, action)); + } + } +} diff --git a/Content.Client/GameObjects/Components/Wires/WiresMenu.cs b/Content.Client/GameObjects/Components/Wires/WiresMenu.cs new file mode 100644 index 0000000000..b05bfa95b8 --- /dev/null +++ b/Content.Client/GameObjects/Components/Wires/WiresMenu.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using static Content.Shared.GameObjects.Components.SharedWiresComponent; + +namespace Content.Client.GameObjects.Components.Wires +{ + public class WiresMenu : SS14Window + { +#pragma warning disable 649 + [Dependency] private readonly ILocalizationManager _localizationManager; +#pragma warning restore 649 + protected override Vector2? CustomSize => (300, 450); + public WiresBoundUserInterface Owner { get; set; } + + private readonly VBoxContainer _rows; + + public WiresMenu() + { + IoCManager.InjectDependencies(this); // TODO: Remove this and use DynamicTypeFactory? + Title = _localizationManager.GetString("Wires"); + _rows = new VBoxContainer(); + Contents.AddChild(_rows); + } + + public void Populate(List wiresList) + { + _rows.RemoveAllChildren(); + foreach (var entry in wiresList) + { + var container = new HBoxContainer(); + var newLabel = new Label() + { + Text = $"{_localizationManager.GetString(entry.Color.Name())}: ", + FontColorOverride = entry.Color, + }; + container.AddChild(newLabel); + + var newButton = new Button() + { + Text = _localizationManager.GetString("Pulse"), + }; + newButton.OnPressed += _ => Owner.PerformAction(entry.Guid, WiresAction.Pulse); + container.AddChild(newButton); + + newButton = new Button() + { + Text = entry.IsCut ? _localizationManager.GetString("Mend") : _localizationManager.GetString("Cut"), + }; + newButton.OnPressed += _ => Owner.PerformAction(entry.Guid, entry.IsCut ? WiresAction.Mend : WiresAction.Cut); + container.AddChild(newButton); + _rows.AddChild(container); + } + } + + } +} diff --git a/Content.Client/GameObjects/Components/Wires/WiresVisualizer2D.cs b/Content.Client/GameObjects/Components/Wires/WiresVisualizer2D.cs new file mode 100644 index 0000000000..5038f7fdf6 --- /dev/null +++ b/Content.Client/GameObjects/Components/Wires/WiresVisualizer2D.cs @@ -0,0 +1,25 @@ +using Robust.Client.GameObjects; +using Robust.Client.Interfaces.GameObjects.Components; +using static Content.Shared.GameObjects.Components.SharedWiresComponent; + +namespace Content.Client.GameObjects.Components.Wires +{ + public class WiresVisualizer2D : AppearanceVisualizer + { + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + + var sprite = component.Owner.GetComponent(); + if (component.TryGetData(WiresVisuals.MaintenancePanelState, out var state)) + { + sprite.LayerSetVisible(WiresVisualLayers.MaintenancePanel, state); + } + } + + public enum WiresVisualLayers + { + MaintenancePanel, + } + } +} diff --git a/Content.Server/GameObjects/Components/VendingMachines/VendingMachineComponent.cs b/Content.Server/GameObjects/Components/VendingMachines/VendingMachineComponent.cs index 8ca5248d84..288e218281 100644 --- a/Content.Server/GameObjects/Components/VendingMachines/VendingMachineComponent.cs +++ b/Content.Server/GameObjects/Components/VendingMachines/VendingMachineComponent.cs @@ -5,23 +5,28 @@ using Content.Shared.VendingMachines; using Robust.Server.GameObjects.Components.UserInterface; using Robust.Server.Interfaces.GameObjects; using Robust.Shared.GameObjects; -using Robust.Shared.GameObjects.Components.UserInterface; using Robust.Shared.IoC; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Timers; using Robust.Shared.Utility; using System.Collections.Generic; +using System.Linq; using Content.Server.GameObjects.Components.Power; using Robust.Server.GameObjects; -using Robust.Shared.Log; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.Random; +using static Content.Shared.GameObjects.Components.SharedWiresComponent; namespace Content.Server.GameObjects.Components.VendingMachines { [RegisterComponent] [ComponentReference(typeof(IActivate))] - public class VendingMachineComponent : SharedVendingMachineComponent, IActivate, IExamine, IBreakAct + public class VendingMachineComponent : SharedVendingMachineComponent, IActivate, IExamine, IBreakAct, IWires { +#pragma warning disable 649 + [Dependency] private readonly IRobustRandom _random; +#pragma warning restore 649 private AppearanceComponent _appearance; private BoundUserInterface _userInterface; private PowerDeviceComponent _powerDevice; @@ -42,7 +47,14 @@ namespace Content.Server.GameObjects.Components.VendingMachines return; } - _userInterface.Open(actor.playerSession); + var wires = Owner.GetComponent(); + if (wires.IsOpen) + { + wires.OpenInterface(actor.playerSession); + } else + { + _userInterface.Open(actor.playerSession); + } } public override void ExposeData(ObjectSerializer serializer) @@ -190,6 +202,60 @@ namespace Content.Server.GameObjects.Components.VendingMachines _broken = true; TrySetVisualState(VendingMachineVisualState.Broken); } + + public enum Wires + { + /// + /// Shoots a random item when pulsed. + /// + Shoot + } + + void IWires.RegisterWires(WiresComponent.WiresBuilder builder) + { + builder.CreateWire(Wires.Shoot); + } + + void IWires.WiresUpdate(WiresUpdateEventArgs args) + { + var identifier = (Wires) args.Identifier; + if (identifier == Wires.Shoot && args.Action == WiresAction.Pulse) + { + EjectRandom(); + } + } + + /// + /// Ejects a random item if present. + /// + private void EjectRandom() + { + var availableItems = Inventory.Where(x => x.Amount > 0).ToList(); + if (availableItems.Count <= 0) + { + return; + } + TryEject(_random.Pick(availableItems).ID); + } + } + + public class WiresUpdateEventArgs : EventArgs + { + public readonly object Identifier; + public readonly WiresAction Action; + + public WiresUpdateEventArgs(object identifier, WiresAction action) + { + Identifier = identifier; + Action = action; + } + } + + public interface IWires + { + void RegisterWires(WiresComponent.WiresBuilder builder); + void WiresUpdate(WiresUpdateEventArgs args); + } } diff --git a/Content.Server/GameObjects/Components/WiresComponent.cs b/Content.Server/GameObjects/Components/WiresComponent.cs new file mode 100644 index 0000000000..03cf1cc41d --- /dev/null +++ b/Content.Server/GameObjects/Components/WiresComponent.cs @@ -0,0 +1,251 @@ +using System; +using System.Collections.Generic; +using Content.Server.GameObjects.Components.Interactable.Tools; +using Content.Server.GameObjects.Components.VendingMachines; +using Content.Server.GameObjects.EntitySystems; +using Content.Server.Interfaces; +using Content.Server.Interfaces.GameObjects; +using Content.Shared.GameObjects.Components; +using JetBrains.Annotations; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.Components.UserInterface; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Server.Interfaces.Player; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using Robust.Shared.Random; +using Robust.Shared.Utility; + +namespace Content.Server.GameObjects.Components +{ + [RegisterComponent] + public class WiresComponent : SharedWiresComponent, IAttackBy, IExamine + { +#pragma warning disable 649 + [Dependency] private readonly IRobustRandom _random; + [Dependency] private readonly IServerNotifyManager _notifyManager; + [Dependency] private readonly ILocalizationManager _localizationManager; +#pragma warning restore 649 + private AudioSystem _audioSystem; + private AppearanceComponent _appearance; + private BoundUserInterface _userInterface; + private bool _isOpen; + + public bool IsOpen + { + get => _isOpen; + private set + { + _isOpen = value; + _appearance.SetData(WiresVisuals.MaintenancePanelState, value); + } + } + + /// + /// Contains all registered wires. + /// + public readonly List WiresList = new List(); + + /// + /// As seen on /vg/station. + /// and . + /// + private readonly List _availableColors = new List() + { + Color.Red, + Color.Blue, + Color.Green, + Color.Orange, + Color.Brown, + Color.Gold, + Color.Gray, + Color.Cyan, + Color.Navy, + Color.Purple, + Color.Pink, + Color.Fuchsia, + Color.Aqua, + }; + + public override void Initialize() + { + base.Initialize(); + _audioSystem = IoCManager.Resolve().GetEntitySystem(); + _appearance = Owner.GetComponent(); + _appearance.SetData(WiresVisuals.MaintenancePanelState, IsOpen); + _userInterface = Owner.GetComponent() + .GetBoundUserInterface(WiresUiKey.Key); + _userInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage; + + foreach (var wiresProvider in Owner.GetAllComponents()) + { + var builder = new WiresBuilder(this, wiresProvider); + wiresProvider.RegisterWires(builder); + } + + UpdateUserInterface(); + } + + public class Wire + { + /// + /// Used in client-server communication to identify a wire without telling the client what the wire does. + /// + public readonly Guid Guid; + /// + /// Registered by components implementing IWires, used to identify which wire the client interacted with. + /// + public readonly object Identifier; + /// + /// The color of the wire. It needs to have a corresponding entry in . + /// + public readonly Color Color; + /// + /// The component that registered the wire. + /// + public readonly IWires Owner; + /// + /// Whether the wire is cut. + /// + public bool IsCut; + public Wire(Guid guid, object identifier, Color color, IWires owner, bool isCut) + { + Guid = guid; + Identifier = identifier; + Color = color; + Owner = owner; + IsCut = isCut; + } + } + + /// + /// Used by . + /// + public class WiresBuilder + { + [NotNull] private readonly WiresComponent _wires; + [NotNull] private readonly IWires _owner; + + public WiresBuilder(WiresComponent wires, IWires owner) + { + _wires = wires; + _owner = owner; + } + + public void CreateWire(object identifier, Color? color = null, bool isCut = false) + { + if (!color.HasValue) + { + color = _wires.AssignColor(); + } + else + { + _wires._availableColors.Remove(color.Value); + } + _wires.WiresList.Add(new Wire(Guid.NewGuid(), identifier, color.Value, _owner, isCut)); + } + } + + /// + /// Picks a color from and removes it from the list. + /// + /// The picked color. + private Color AssignColor() + { + if(_availableColors.Count == 0) + { + return Color.Black; + } + return _random.PickAndTake(_availableColors); + } + + /// + /// Call this from other components to open the wires UI. + /// + public void OpenInterface(IPlayerSession session) + { + _userInterface.Open(session); + } + + private void UserInterfaceOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg) + { + var message = serverMsg.Message; + switch (message) + { + case WiresActionMessage msg: + var wire = WiresList.Find(x => x.Guid == msg.Guid); + var player = serverMsg.Session.AttachedEntity; + if (!player.TryGetComponent(out IHandsComponent handsComponent)) + { + _notifyManager.PopupMessage(Owner.Transform.GridPosition, player, _localizationManager.GetString("You have no hands.")); + return; + } + var activeHandEntity = handsComponent.GetActiveHand?.Owner; + switch (msg.Action) + { + case WiresAction.Cut: + if (activeHandEntity?.HasComponent() != true) + { + _notifyManager.PopupMessage(Owner.Transform.GridPosition, player, _localizationManager.GetString("You need to hold a wirecutter in your hand!")); + return; + } + _audioSystem.Play("/Audio/items/wirecutter.ogg", Owner); + wire.IsCut = true; + UpdateUserInterface(); + break; + case WiresAction.Mend: + if (activeHandEntity?.HasComponent() != true) + { + _notifyManager.PopupMessage(Owner.Transform.GridPosition, player, _localizationManager.GetString("You need to hold a wirecutter in your hand!")); + return; + } + _audioSystem.Play("/Audio/items/wirecutter.ogg", Owner); + wire.IsCut = false; + UpdateUserInterface(); + break; + case WiresAction.Pulse: + if (activeHandEntity?.HasComponent() != true) + { + _notifyManager.PopupMessage(Owner.Transform.GridPosition, player, _localizationManager.GetString("You need to hold a multitool in your hand!")); + return; + } + if (wire.IsCut) + { + _notifyManager.PopupMessage(Owner.Transform.GridPosition, player, _localizationManager.GetString("You can't pulse a wire that's been cut!")); + return; + } + _audioSystem.Play("/Audio/effects/multitool_pulse.ogg", Owner); + break; + } + wire.Owner.WiresUpdate(new WiresUpdateEventArgs(wire.Identifier, msg.Action)); + break; + } + } + + private void UpdateUserInterface() + { + var clientList = new List(); + foreach (var entry in WiresList) + { + clientList.Add(new ClientWire(entry.Guid, entry.Color, entry.IsCut)); + } + _userInterface.SetState(new WiresBoundUserInterfaceState(clientList)); + } + + bool IAttackBy.AttackBy(AttackByEventArgs eventArgs) + { + if (!eventArgs.AttackWith.HasComponent()) return false; + IsOpen = !IsOpen; + return true; + } + + void IExamine.Examine(FormattedMessage message) + { + message.AddText($"The maintenance panel is {(IsOpen ? "open" : "closed")}."); + } + } +} diff --git a/Content.Shared/GameObjects/Components/SharedWiresComponent.cs b/Content.Shared/GameObjects/Components/SharedWiresComponent.cs new file mode 100644 index 0000000000..6011e73b20 --- /dev/null +++ b/Content.Shared/GameObjects/Components/SharedWiresComponent.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components.UserInterface; +using Robust.Shared.Maths; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components +{ + public class SharedWiresComponent : Component + { + public override string Name => "Wires"; + + [Serializable, NetSerializable] + public enum WiresVisuals + { + MaintenancePanelState + } + + [Serializable, NetSerializable] + public enum WiresUiKey + { + Key, + } + + [Serializable, NetSerializable] + public enum WiresAction + { + Mend, + Cut, + Pulse, + } + + [Serializable, NetSerializable] + public class WiresBoundUserInterfaceState : BoundUserInterfaceState + { + public readonly List WiresList; + + public WiresBoundUserInterfaceState(List wiresList) + { + WiresList = wiresList; + } + } + + [Serializable, NetSerializable] + public class ClientWire + { + public Guid Guid; + public Color Color; + public bool IsCut; + + public ClientWire(Guid guid, Color color, bool isCut) + { + Guid = guid; + Color = color; + IsCut = isCut; + } + } + + [Serializable, NetSerializable] + public class WiresActionMessage : BoundUserInterfaceMessage + { + public readonly Guid Guid; + public readonly WiresAction Action; + public WiresActionMessage(Guid guid, WiresAction action) + { + Guid = guid; + Action = action; + } + } + } +} diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index ae8692f522..dcb8609547 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -24,5 +24,6 @@ public const uint MATERIAL_STORAGE = 1018; public const uint HAND_TELEPORTER = 1019; public const uint VENDING_MACHINE = 1020; + public const uint WIRES = 1021; } } diff --git a/Resources/Audio/effects/multitool_pulse.ogg b/Resources/Audio/effects/multitool_pulse.ogg new file mode 100644 index 0000000000000000000000000000000000000000..629f858ee99bfce44ed4ae36f7eb5f6f184daa5c GIT binary patch literal 7891 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∾ zk@aN{lGu%TcWDPLPMOV0a~F@z&yhn>jWk5Ld5_U7eGzSa*WQp2A)C7|-Jd*2b2<5i zHs-rWb#(Loro?9>1)0{-Q!OViVstc84br_f3Lfp*4=)W* zPVcs1tli{%rB@5M>otr$J;uJ-msh8noEGpr`m9m3^5*t(yP@`RR)@P=f7PEi3p{4~ yGCPv;1t)pm(9d9ZnMgQAyj((}54CO{u*0T~%IL7DbZqjIsw16VuDBFTLH_|Q`cSC= literal 0 HcmV?d00001 diff --git a/Resources/Prototypes/Entities/buildings/vending_machines.yml b/Resources/Prototypes/Entities/buildings/vending_machines.yml index 99942a7597..54f890e871 100644 --- a/Resources/Prototypes/Entities/buildings/vending_machines.yml +++ b/Resources/Prototypes/Entities/buildings/vending_machines.yml @@ -8,6 +8,8 @@ layers: - state: normal map: ["enum.VendingMachineVisualLayers.Base"] + - texture: Buildings/maintenance_panel.png + map: ["enum.WiresVisualLayers.MaintenancePanel"] - type: Icon sprite: Buildings/VendingMachines/empty.rsi state: normal @@ -20,12 +22,16 @@ - type: Appearance visuals: - type: VendingMachineVisualizer2D + - type: WiresVisualizer2D - type: UserInterface interfaces: - key: enum.VendingMachineUiKey.Key type: VendingMachineBoundUserInterface + - key: enum.WiresUiKey.Key + type: WiresBoundUserInterface - type: PowerDevice priority: Low + - type: Wires - type: entity parent: VendingMachine diff --git a/Resources/Textures/Buildings/maintenance_panel.png b/Resources/Textures/Buildings/maintenance_panel.png new file mode 100644 index 0000000000000000000000000000000000000000..27dad1140443b5a1f6732b6ad1bed6b286fbbe7f GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvi2$Dv*8>L*OrAVBEiJ9TzrP?` zL`6k~!Fc9OW8+&3TGW7Q7)yfuf*Bm1-ADs+G(BA$Lo_BPCrGe5^B8DpF6J`WX|<5E rMQ!2g0%zmJ9=xFqvWI6e8E`V}w`W$nIp^FKpe6=SS3j3^P6