* 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
This commit is contained in:
DamianX
2019-09-01 22:15:34 +02:00
committed by Pieter-Jan Briers
parent 70e3cffa90
commit 264a63b7f6
11 changed files with 524 additions and 4 deletions

View File

@@ -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<SolutionComponent>();
factory.Register<SharedVendingMachineComponent>();
factory.Register<SharedWiresComponent>();
prototypes.RegisterIgnore("material");

View File

@@ -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));
}
}
}

View File

@@ -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<ClientWire> 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);
}
}
}
}

View File

@@ -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<ISpriteComponent>();
if (component.TryGetData<bool>(WiresVisuals.MaintenancePanelState, out var state))
{
sprite.LayerSetVisible(WiresVisualLayers.MaintenancePanel, state);
}
}
public enum WiresVisualLayers
{
MaintenancePanel,
}
}
}

View File

@@ -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<WiresComponent>();
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
{
/// <summary>
/// Shoots a random item when pulsed.
/// </summary>
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();
}
}
/// <summary>
/// Ejects a random item if present.
/// </summary>
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);
}
}

View File

@@ -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);
}
}
/// <summary>
/// Contains all registered wires.
/// </summary>
public readonly List<Wire> WiresList = new List<Wire>();
/// <summary>
/// As seen on /vg/station.
/// <see cref="AssignColor"/> and <see cref="WiresBuilder.CreateWire"/>.
/// </summary>
private readonly List<Color> _availableColors = new List<Color>()
{
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<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
_appearance = Owner.GetComponent<AppearanceComponent>();
_appearance.SetData(WiresVisuals.MaintenancePanelState, IsOpen);
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
.GetBoundUserInterface(WiresUiKey.Key);
_userInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
foreach (var wiresProvider in Owner.GetAllComponents<IWires>())
{
var builder = new WiresBuilder(this, wiresProvider);
wiresProvider.RegisterWires(builder);
}
UpdateUserInterface();
}
public class Wire
{
/// <summary>
/// Used in client-server communication to identify a wire without telling the client what the wire does.
/// </summary>
public readonly Guid Guid;
/// <summary>
/// Registered by components implementing IWires, used to identify which wire the client interacted with.
/// </summary>
public readonly object Identifier;
/// <summary>
/// The color of the wire. It needs to have a corresponding entry in <see cref="Robust.Shared.Maths.Color.DefaultColors"/>.
/// </summary>
public readonly Color Color;
/// <summary>
/// The component that registered the wire.
/// </summary>
public readonly IWires Owner;
/// <summary>
/// Whether the wire is cut.
/// </summary>
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;
}
}
/// <summary>
/// Used by <see cref="IWires.RegisterWires"/>.
/// </summary>
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));
}
}
/// <summary>
/// Picks a color from <see cref="_availableColors"/> and removes it from the list.
/// </summary>
/// <returns>The picked color.</returns>
private Color AssignColor()
{
if(_availableColors.Count == 0)
{
return Color.Black;
}
return _random.PickAndTake(_availableColors);
}
/// <summary>
/// Call this from other components to open the wires UI.
/// </summary>
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<WirecutterComponent>() != 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<WirecutterComponent>() != 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<MultitoolComponent>() != 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<ClientWire>();
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<ScrewdriverComponent>()) return false;
IsOpen = !IsOpen;
return true;
}
void IExamine.Examine(FormattedMessage message)
{
message.AddText($"The maintenance panel is {(IsOpen ? "open" : "closed")}.");
}
}
}

View File

@@ -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<ClientWire> WiresList;
public WiresBoundUserInterfaceState(List<ClientWire> 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;
}
}
}
}

View File

@@ -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;
}
}

Binary file not shown.

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B