diff --git a/Content.Client/Content.Client.csproj b/Content.Client/Content.Client.csproj
index 16c1964505..b21bff8ec5 100644
--- a/Content.Client/Content.Client.csproj
+++ b/Content.Client/Content.Client.csproj
@@ -92,6 +92,7 @@
+
diff --git a/Content.Client/GameObjects/EntitySystems/ExamineSystem.cs b/Content.Client/GameObjects/EntitySystems/ExamineSystem.cs
new file mode 100644
index 0000000000..aad58b3e4a
--- /dev/null
+++ b/Content.Client/GameObjects/EntitySystems/ExamineSystem.cs
@@ -0,0 +1,139 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Content.Shared.GameObjects.EntitySystemMessages;
+using Content.Shared.Input;
+using JetBrains.Annotations;
+using SS14.Client.GameObjects.EntitySystems;
+using SS14.Client.Interfaces.GameObjects.Components;
+using SS14.Client.Interfaces.Input;
+using SS14.Client.Interfaces.UserInterface;
+using SS14.Client.UserInterface;
+using SS14.Client.UserInterface.Controls;
+using SS14.Shared.GameObjects;
+using SS14.Shared.GameObjects.Systems;
+using SS14.Shared.Input;
+using SS14.Shared.Interfaces.GameObjects;
+using SS14.Shared.IoC;
+using SS14.Shared.Map;
+using SS14.Shared.Maths;
+using SS14.Shared.Players;
+
+namespace Content.Client.GameObjects.EntitySystems
+{
+ [UsedImplicitly]
+ internal sealed class ExamineSystem : EntitySystem
+ {
+ public const string StyleClassEntityTooltip = "entity-tooltip";
+
+#pragma warning disable 649
+ [Dependency] private IInputManager _inputManager;
+ [Dependency] private IUserInterfaceManager _userInterfaceManager;
+ [Dependency] private IEntityManager _entityManager;
+#pragma warning restore 649
+
+ private Popup _examineTooltipOpen;
+ private CancellationTokenSource _requestCancelTokenSource;
+
+ public override void Initialize()
+ {
+ IoCManager.InjectDependencies(this);
+
+ var inputSys = EntitySystemManager.GetEntitySystem();
+ inputSys.BindMap.BindFunction(ContentKeyFunctions.ExamineEntity, new PointerInputCmdHandler(HandleExamine));
+ }
+
+ public override void RegisterMessageTypes()
+ {
+ base.RegisterMessageTypes();
+
+ RegisterMessageType();
+ }
+
+ private void HandleExamine(ICommonSession session, GridCoordinates coords, EntityUid uid)
+ {
+ if (!uid.IsValid() || !_entityManager.TryGetEntity(uid, out var entity))
+ {
+ return;
+ }
+
+ DoExamine(entity);
+ }
+
+ public async void DoExamine(IEntity entity)
+ {
+ CloseTooltip();
+
+ var mousePos = _inputManager.MouseScreenPosition;
+
+ // Actually open the tooltip.
+ _examineTooltipOpen = new Popup();
+ _userInterfaceManager.StateRoot.AddChild(_examineTooltipOpen);
+ var panel = new PanelContainer();
+ panel.AddStyleClass(StyleClassEntityTooltip);
+ _examineTooltipOpen.AddChild(panel);
+ panel.SetAnchorAndMarginPreset(Control.LayoutPreset.Wide);
+ var vBox = new VBoxContainer();
+ panel.AddChild(vBox);
+ var hBox = new HBoxContainer { SeparationOverride = 5};
+ vBox.AddChild(hBox);
+ if (entity.TryGetComponent(out ISpriteComponent sprite))
+ {
+ hBox.AddChild(new SpriteView {Sprite = sprite});
+ }
+
+ hBox.AddChild(new Label
+ {
+ Text = entity.Name,
+ SizeFlagsHorizontal = Control.SizeFlags.FillExpand,
+ });
+
+ const float minWidth = 300;
+ var size = Vector2.ComponentMax((minWidth, 0), panel.CombinedMinimumSize);
+ _examineTooltipOpen.Open(UIBox2.FromDimensions(mousePos, size));
+
+ if (entity.Uid.IsClientSide())
+ {
+ return;
+ }
+
+ // Ask server for extra examine info.
+ RaiseNetworkEvent(new ExamineSystemMessages.RequestExamineInfoMessage(entity.Uid));
+
+ ExamineSystemMessages.ExamineInfoResponseMessage response;
+ try
+ {
+ _requestCancelTokenSource = new CancellationTokenSource();
+ response =
+ await AwaitNetMessage(_requestCancelTokenSource
+ .Token);
+ }
+ catch (TaskCanceledException)
+ {
+ return;
+ }
+ finally
+ {
+ _requestCancelTokenSource = null;
+ }
+
+ var richLabel = new RichTextLabel();
+ richLabel.SetMessage(response.Message);
+ vBox.AddChild(richLabel);
+ }
+
+ public void CloseTooltip()
+ {
+ if (_examineTooltipOpen != null)
+ {
+ _examineTooltipOpen.Dispose();
+ _examineTooltipOpen = null;
+ }
+
+ if (_requestCancelTokenSource != null)
+ {
+ _requestCancelTokenSource.Cancel();
+ _requestCancelTokenSource = null;
+ }
+ }
+ }
+}
diff --git a/Content.Client/UserInterface/NanoStyle.cs b/Content.Client/UserInterface/NanoStyle.cs
index 7762a1cc84..a235bc407f 100644
--- a/Content.Client/UserInterface/NanoStyle.cs
+++ b/Content.Client/UserInterface/NanoStyle.cs
@@ -1,3 +1,4 @@
+using Content.Client.GameObjects.EntitySystems;
using Content.Client.Utility;
using SS14.Client.Graphics.Drawing;
using SS14.Client.Interfaces.ResourceManagement;
@@ -131,6 +132,15 @@ namespace Content.Client.UserInterface
var checkBoxTextureChecked = resCache.GetTexture("/Nano/checkbox_checked.svg.96dpi.png");
var checkBoxTextureUnchecked = resCache.GetTexture("/Nano/checkbox_unchecked.svg.96dpi.png");
+ // Tooltip box
+ var tooltipTexture = resCache.GetTexture("/Nano/tooltip.png");
+ var tooltipBox = new StyleBoxTexture
+ {
+ Texture = tooltipTexture,
+ };
+ tooltipBox.SetPatchMargin(StyleBox.Margin.All, 2);
+ tooltipBox.SetContentMarginOverride(StyleBox.Margin.Horizontal, 5);
+
Stylesheet = new Stylesheet(new[]
{
// Default font.
@@ -337,7 +347,13 @@ namespace Content.Client.UserInterface
// Tooltip
new StyleRule(new SelectorElement(typeof(Tooltip), null, null, null), new []
{
- new StyleProperty(PanelContainer.StylePropertyPanel, new StyleBoxFlat { BackgroundColor = new Color(21, 21, 26)})
+ new StyleProperty(PanelContainer.StylePropertyPanel, tooltipBox)
+ }),
+
+ // Entity tooltip
+ new StyleRule(new SelectorElement(typeof(PanelContainer), new []{ExamineSystem.StyleClassEntityTooltip}, null, null), new []
+ {
+ new StyleProperty(PanelContainer.StylePropertyPanel, tooltipBox)
}),
// ItemList
@@ -357,6 +373,20 @@ namespace Content.Client.UserInterface
{
new StyleProperty(ItemList.StylePropertySelectedItemBackground, new StyleBoxFlat { BackgroundColor = new Color(75, 75, 86)})
}),
+
+ // Tree
+ new StyleRule(new SelectorElement(typeof(Tree), null, null, null), new []
+ {
+ new StyleProperty(Tree.StylePropertyBackground, new StyleBoxFlat { BackgroundColor = new Color(32, 32, 40)})
+ }),
+ new StyleRule(new SelectorElement(typeof(Tree), null, null, null), new []
+ {
+ new StyleProperty(Tree.StylePropertyItemBoxSelected, new StyleBoxFlat
+ {
+ BackgroundColor = new Color(55, 55, 68),
+ ContentMarginLeftOverride = 4
+ })
+ }),
});
}
}
diff --git a/Content.Server/GameObjects/Components/Interactable/HandheldLightComponent.cs b/Content.Server/GameObjects/Components/Interactable/HandheldLightComponent.cs
index 0e2a7cda69..39f47be303 100644
--- a/Content.Server/GameObjects/Components/Interactable/HandheldLightComponent.cs
+++ b/Content.Server/GameObjects/Components/Interactable/HandheldLightComponent.cs
@@ -7,6 +7,7 @@ using SS14.Server.GameObjects.Components.Container;
using SS14.Shared.Enums;
using SS14.Shared.GameObjects;
using SS14.Shared.Interfaces.GameObjects;
+using SS14.Shared.Utility;
using SS14.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Interactable
@@ -53,11 +54,12 @@ namespace Content.Server.GameObjects.Components.Interactable
return _cellContainer.Insert(eventArgs.AttackWith);
}
- string IExamine.Examine()
+ void IExamine.Examine(FormattedMessage message)
{
- if (Activated) return "The light is currently on.";
-
- return null;
+ if (Activated)
+ {
+ message.AddText("The light is currently on.");
+ }
}
bool IUse.UseEntity(UseEntityEventArgs eventArgs)
diff --git a/Content.Server/GameObjects/Components/Interactable/Tools/WelderComponent.cs b/Content.Server/GameObjects/Components/Interactable/Tools/WelderComponent.cs
index 6e44644212..45b1239d61 100644
--- a/Content.Server/GameObjects/Components/Interactable/Tools/WelderComponent.cs
+++ b/Content.Server/GameObjects/Components/Interactable/Tools/WelderComponent.cs
@@ -1,9 +1,11 @@
using System;
+using System.Text;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.Utility;
using YamlDotNet.RepresentationModel;
using SS14.Server.GameObjects;
using Content.Server.GameObjects.EntitySystems;
+using SS14.Shared.Maths;
using SS14.Shared.Serialization;
using SS14.Shared.ViewVariables;
@@ -145,13 +147,22 @@ namespace Content.Server.GameObjects.Components.Interactable.Tools
}
}
- string IExamine.Examine()
+ void IExamine.Examine(FormattedMessage message)
{
if (Activated)
{
- return "The welding tool is currently lit";
+ message.PushColor(Color.Orange);
+ message.AddText("Lit\n");
+ message.Pop();
}
- return null;
+ else
+ {
+ message.AddText("Not lit\n");
+ }
+ message.AddText("Fuel: ");
+ message.PushColor(Fuel < FuelCapacity / 4f ? Color.DarkOrange : Color.Orange);
+ message.AddText($"{Math.Round(Fuel)}/{FuelCapacity}");
+ message.Pop();
}
}
}
diff --git a/Content.Server/GameObjects/Components/Power/PowerDevice.cs b/Content.Server/GameObjects/Components/Power/PowerDevice.cs
index e2b56703e8..74c61c557a 100644
--- a/Content.Server/GameObjects/Components/Power/PowerDevice.cs
+++ b/Content.Server/GameObjects/Components/Power/PowerDevice.cs
@@ -181,13 +181,12 @@ namespace Content.Server.GameObjects.Components.Power
serializer.DataField(ref _priority, "priority", Powernet.Priority.Medium);
}
- string IExamine.Examine()
+ void IExamine.Examine(FormattedMessage message)
{
if (!Powered)
{
- return "The device is not powered";
+ message.AddText("The device is not powered");
}
- return null;
}
private void UpdateLoad(float value)
diff --git a/Content.Server/GameObjects/Components/Stack/StackComponent.cs b/Content.Server/GameObjects/Components/Stack/StackComponent.cs
index 8cb276d9f9..d0d8dae690 100644
--- a/Content.Server/GameObjects/Components/Stack/StackComponent.cs
+++ b/Content.Server/GameObjects/Components/Stack/StackComponent.cs
@@ -4,6 +4,7 @@ using SS14.Shared.GameObjects;
using SS14.Shared.Interfaces.Reflection;
using SS14.Shared.IoC;
using SS14.Shared.Serialization;
+using SS14.Shared.Utility;
using SS14.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Stack
@@ -113,9 +114,9 @@ namespace Content.Server.GameObjects.Components.Stack
return false;
}
- public string Examine()
+ void IExamine.Examine(FormattedMessage message)
{
- return $"There are {Count} things in the stack.";
+ message.AddText($"There are {Count} things in the stack.");
}
}
diff --git a/Content.Server/GameObjects/EntitySystems/Click/ExamineSystem.cs b/Content.Server/GameObjects/EntitySystems/Click/ExamineSystem.cs
index 7c24ef48d2..c932773110 100644
--- a/Content.Server/GameObjects/EntitySystems/Click/ExamineSystem.cs
+++ b/Content.Server/GameObjects/EntitySystems/Click/ExamineSystem.cs
@@ -1,5 +1,6 @@
using System;
using System.Text;
+using Content.Shared.GameObjects.EntitySystemMessages;
using Content.Shared.Input;
using SS14.Server.GameObjects.EntitySystems;
using SS14.Server.Interfaces.Chat;
@@ -7,11 +8,15 @@ using SS14.Server.Interfaces.Player;
using SS14.Shared.GameObjects;
using SS14.Shared.GameObjects.Systems;
using SS14.Shared.Input;
+using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.Interfaces.GameObjects.Components;
+using SS14.Shared.Interfaces.Network;
using SS14.Shared.IoC;
using SS14.Shared.Log;
using SS14.Shared.Map;
+using SS14.Shared.Maths;
using SS14.Shared.Players;
+using SS14.Shared.Utility;
namespace Content.Server.GameObjects.EntitySystems
{
@@ -20,63 +25,89 @@ namespace Content.Server.GameObjects.EntitySystems
///
/// Returns an status examine value for components appended to the end of the description of the entity
///
- ///
- string Examine();
+ void Examine(FormattedMessage message);
}
public class ExamineSystem : EntitySystem
{
- ///
- public override void Initialize()
+#pragma warning disable 649
+ [Dependency] private IEntityManager _entityManager;
+#pragma warning restore 649
+
+ private static readonly FormattedMessage _entityNotFoundMessage;
+
+ static ExamineSystem()
{
- var inputSys = EntitySystemManager.GetEntitySystem();
- inputSys.BindMap.BindFunction(ContentKeyFunctions.ExamineEntity, new PointerInputCmdHandler(HandleExamine));
+ _entityNotFoundMessage = new FormattedMessage();
+ _entityNotFoundMessage.AddText("That entity doesn't exist");
}
- private void HandleExamine(ICommonSession session, GridCoordinates coords, EntityUid uid)
+ public override void Initialize()
{
- if (!(session is IPlayerSession svSession))
- return;
+ base.Initialize();
- var playerEnt = svSession.AttachedEntity;
- if (!EntityManager.TryGetEntity(uid, out var examined))
- return;
+ IoCManager.InjectDependencies(this);
+ }
- //Verify player has a transform component
- if (!playerEnt.TryGetComponent(out var playerTransform))
- {
- return;
- }
+ public override void RegisterMessageTypes()
+ {
+ base.RegisterMessageTypes();
- //Verify player is on the same map as the entity he clicked on
- if (coords.MapID != playerTransform.MapID)
- {
- Logger.WarningS("sys.examine", $"Player named {session.Name} clicked on a map he isn't located on");
- return;
- }
+ RegisterMessageType();
+ }
- //Start a StringBuilder since we have no idea how many times this could be appended to
- var fullExamineText = new StringBuilder("This is " + examined.Name);
+ private FormattedMessage GetExamineText(IEntity entity)
+ {
+ var message = new FormattedMessage();
+
+ var doNewline = false;
//Add an entity description if one is declared
- if (!string.IsNullOrEmpty(examined.Description))
+ if (!string.IsNullOrEmpty(entity.Description))
{
- fullExamineText.Append(Environment.NewLine + examined.Description);
+ message.AddText(entity.Description);
+ doNewline = true;
}
+ message.PushColor(Color.DarkGray);
+
+ var subMessage = new FormattedMessage();
//Add component statuses from components that report one
- foreach (var examineComponents in examined.GetAllComponents())
+ foreach (var examineComponents in entity.GetAllComponents())
{
- var componentDescription = examineComponents.Examine();
- if (string.IsNullOrEmpty(componentDescription))
+ examineComponents.Examine(subMessage);
+ if (subMessage.Tags.Count == 0)
continue;
- fullExamineText.Append(Environment.NewLine);
- fullExamineText.Append(componentDescription);
+ if (doNewline)
+ {
+ message.AddText("\n");
+ doNewline = false;
+ }
+ message.AddMessage(subMessage);
}
- //Send to client chat channel
- IoCManager.Resolve().DispatchMessage(svSession.ConnectedClient, SS14.Shared.Console.ChatChannel.Visual, fullExamineText.ToString(), session.SessionId);
+ message.Pop();
+
+ return message;
+ }
+
+ public override void HandleNetMessage(INetChannel channel, EntitySystemMessage message)
+ {
+ base.HandleNetMessage(channel, message);
+
+ if (message is ExamineSystemMessages.RequestExamineInfoMessage request)
+ {
+ if (!_entityManager.TryGetEntity(request.EntityUid, out var entity))
+ {
+ RaiseNetworkEvent(new ExamineSystemMessages.ExamineInfoResponseMessage(
+ request.EntityUid, _entityNotFoundMessage));
+ return;
+ }
+
+ var text = GetExamineText(entity);
+ RaiseNetworkEvent(new ExamineSystemMessages.ExamineInfoResponseMessage(request.EntityUid, text));
+ }
}
}
}
diff --git a/Content.Shared/Content.Shared.csproj b/Content.Shared/Content.Shared.csproj
index a8ec723674..62021de942 100644
--- a/Content.Shared/Content.Shared.csproj
+++ b/Content.Shared/Content.Shared.csproj
@@ -81,6 +81,7 @@
+
diff --git a/Content.Shared/GameObjects/EntitySystemMessages/ExamineSystemMessages.cs b/Content.Shared/GameObjects/EntitySystemMessages/ExamineSystemMessages.cs
new file mode 100644
index 0000000000..e08061e0ed
--- /dev/null
+++ b/Content.Shared/GameObjects/EntitySystemMessages/ExamineSystemMessages.cs
@@ -0,0 +1,34 @@
+using System;
+using SS14.Shared.GameObjects;
+using SS14.Shared.Serialization;
+using SS14.Shared.Utility;
+
+namespace Content.Shared.GameObjects.EntitySystemMessages
+{
+ public static class ExamineSystemMessages
+ {
+ [Serializable, NetSerializable]
+ public class RequestExamineInfoMessage : EntitySystemMessage
+ {
+ public readonly EntityUid EntityUid;
+
+ public RequestExamineInfoMessage(EntityUid entityUid)
+ {
+ EntityUid = entityUid;
+ }
+ }
+
+ [Serializable, NetSerializable]
+ public class ExamineInfoResponseMessage : EntitySystemMessage
+ {
+ public readonly EntityUid EntityUid;
+ public readonly FormattedMessage Message;
+
+ public ExamineInfoResponseMessage(EntityUid entityUid, FormattedMessage message)
+ {
+ EntityUid = entityUid;
+ Message = message;
+ }
+ }
+ }
+}
diff --git a/Resources/Nano/tooltip.png b/Resources/Nano/tooltip.png
new file mode 100644
index 0000000000..eadfc72e37
Binary files /dev/null and b/Resources/Nano/tooltip.png differ
diff --git a/RobustToolbox b/RobustToolbox
index e40cda6c99..4b663fd508 160000
--- a/RobustToolbox
+++ b/RobustToolbox
@@ -1 +1 @@
-Subproject commit e40cda6c990eb1c42c4e5422155c45ab7dd84845
+Subproject commit 4b663fd508bed17d5af78e4274ed93740d139561