Skip to content

Commit afd1568

Browse files
authored
Include screen objects in the right-click menu (#2457)
1 parent 1dc4a7f commit afd1568

File tree

2 files changed

+64
-65
lines changed

2 files changed

+64
-65
lines changed

OpenDreamClient/Input/ContextMenu/ContextMenuPopup.xaml.cs

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1+
using OpenDreamClient.Interface.Controls.UI;
12
using OpenDreamClient.Rendering;
23
using OpenDreamShared.Dream;
34
using OpenDreamShared.Rendering;
45
using Robust.Client.AutoGenerated;
5-
using Robust.Client.GameObjects;
66
using Robust.Client.Player;
77
using Robust.Client.UserInterface;
88
using Robust.Client.UserInterface.Controls;
99
using Robust.Client.UserInterface.XAML;
1010
using Robust.Shared.Map;
11+
using Robust.Shared.Map.Components;
1112

1213
namespace OpenDreamClient.Input.ContextMenu;
1314

@@ -16,12 +17,12 @@ internal sealed partial class ContextMenuPopup : Popup {
1617
[Dependency] private readonly IPlayerManager _playerManager = default!;
1718
[Dependency] private readonly IEntityManager _entityManager = default!;
1819
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
19-
[Dependency] private readonly IMapManager _mapManager = default!;
2020
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
2121
private readonly ClientAppearanceSystem _appearanceSystem;
22-
private readonly TransformSystem _transformSystem;
2322
private readonly ClientVerbSystem _verbSystem;
2423
private readonly DMISpriteSystem _spriteSystem;
24+
private readonly EntityLookupSystem _lookupSystem;
25+
private readonly MouseInputSystem _mouseInputSystem;
2526
private readonly EntityQuery<DMISpriteComponent> _spriteQuery;
2627
private readonly EntityQuery<TransformComponent> _xformQuery;
2728
private readonly EntityQuery<DreamMobSightComponent> _mobSightQuery;
@@ -34,47 +35,64 @@ public ContextMenuPopup() {
3435
IoCManager.InjectDependencies(this);
3536
RobustXamlLoader.Load(this);
3637

37-
_transformSystem = _entitySystemManager.GetEntitySystem<TransformSystem>();
3838
_verbSystem = _entitySystemManager.GetEntitySystem<ClientVerbSystem>();
3939
_appearanceSystem = _entitySystemManager.GetEntitySystem<ClientAppearanceSystem>();
4040
_spriteSystem = _entitySystemManager.GetEntitySystem<DMISpriteSystem>();
41+
_lookupSystem = _entitySystemManager.GetEntitySystem<EntityLookupSystem>();
42+
_mouseInputSystem = _entitySystemManager.GetEntitySystem<MouseInputSystem>();
4143
_spriteQuery = _entityManager.GetEntityQuery<DMISpriteComponent>();
4244
_xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
4345
_mobSightQuery = _entityManager.GetEntityQuery<DreamMobSightComponent>();
4446
}
4547

46-
public void RepopulateEntities(ClientObjectReference[] entities, uint? turfId) {
48+
public void RepopulateEntities(ScalingViewport viewport, Vector2 relativePos, ScreenCoordinates pointerLocation) {
4749
ContextMenu.RemoveAllChildren();
4850

49-
foreach (var objectReference in entities) {
50-
var name = _appearanceSystem.GetName(objectReference);
51-
DreamIcon? icon = null;
52-
53-
switch (objectReference.Type) {
54-
case ClientObjectReference.RefType.Entity: {
55-
var entity = _entityManager.GetEntity(objectReference.Entity);
56-
if (_xformQuery.TryGetComponent(entity, out TransformComponent? transform) && !_mapManager.IsGrid(_transformSystem.GetParentUid(entity))) // Not a child of another entity
57-
continue;
58-
if (!_spriteQuery.TryGetComponent(entity, out DMISpriteComponent? sprite)) // Has a sprite
59-
continue;
60-
if (sprite.Icon.Appearance?.MouseOpacity == MouseOpacity.Transparent) // Not transparent to mouse clicks
61-
continue;
62-
if (!_spriteSystem.IsVisible(sprite, transform, GetSeeInvisible(), null)) // Not invisible
63-
continue;
64-
65-
icon = sprite.Icon;
66-
break;
67-
}
68-
case ClientObjectReference.RefType.Turf when turfId is not null:
69-
icon = _appearanceSystem.GetTurfIcon(turfId.Value);
70-
break;
71-
}
51+
var mapCoords = viewport.ScreenToMap(pointerLocation.Position);
52+
var entities = _lookupSystem.GetEntitiesInRange(mapCoords, 0.01f, LookupFlags.Uncontained | LookupFlags.Approximate);
7253

73-
if (icon is null)
54+
foreach (var uid in entities) {
55+
if (_xformQuery.TryGetComponent(uid, out var transform) && !_entityManager.HasComponent<MapGridComponent>(transform.ParentUid)) // Not a child of another entity
56+
continue;
57+
if (!_spriteQuery.TryGetComponent(uid, out var sprite)) // Has a sprite
58+
continue;
59+
if (sprite.Icon.Appearance?.MouseOpacity == MouseOpacity.Transparent) // Not transparent to mouse clicks
7460
continue;
61+
if (!_spriteSystem.IsVisible(sprite, transform, GetSeeInvisible(), null)) // Not invisible
62+
continue;
63+
64+
var reference = new ClientObjectReference(_entityManager.GetNetEntity(uid));
65+
var name = _appearanceSystem.GetName(reference);
66+
ContextMenu.AddChild(new ContextMenuItem(this, reference, name, sprite.Icon));
67+
}
7568

76-
ContextMenu.AddChild(new ContextMenuItem(this, objectReference, name, icon));
69+
// Add the screen object directly under the mouse, if any
70+
var atomUnderMouse = _mouseInputSystem.GetAtomUnderMouse(viewport, relativePos, pointerLocation);
71+
if (atomUnderMouse is { IsScreen: true }) {
72+
var uid = _entityManager.GetEntity(atomUnderMouse.Value.Atom.Entity);
73+
74+
if (_spriteQuery.TryGetComponent(uid, out var sprite) &&
75+
sprite.Icon.Appearance?.MouseOpacity != MouseOpacity.Transparent) {
76+
var reference = new ClientObjectReference(_entityManager.GetNetEntity(uid));
77+
var name = _appearanceSystem.GetName(reference);
78+
ContextMenu.AddChild(new ContextMenuItem(this, reference, name, sprite.Icon));
79+
}
80+
}
81+
82+
// Append the turf to the end of the context menu
83+
var turfUnderMouse = _mouseInputSystem.GetTurfUnderMouse(mapCoords, out var turfId)?.Atom;
84+
if (turfUnderMouse is not null && turfId is not null) {
85+
var name = _appearanceSystem.GetName(turfUnderMouse.Value);
86+
var icon = _appearanceSystem.GetTurfIcon(turfId.Value);
87+
88+
ContextMenu.AddChild(new ContextMenuItem(this, turfUnderMouse.Value, name, icon));
7789
}
90+
91+
//TODO filter entities by the valid verbs that exist on them
92+
//they should only show up if there is a verb attached to usr which matches the filter in world syntax
93+
//ie, obj|turf in world
94+
//note that popup_menu = 0 overrides this behaviour, as does verb invisibility (urgh), and also hidden
95+
//because BYOND sure loves redundancy
7896
}
7997

8098
public void SetActiveItem(ContextMenuItem item) {

OpenDreamClient/Input/MouseInputSystem.cs

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ internal sealed class MouseInputSystem : SharedMouseInputSystem {
2222
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
2323
[Dependency] private readonly IMapManager _mapManager = default!;
2424
[Dependency] private readonly IOverlayManager _overlayManager = default!;
25-
[Dependency] private readonly EntityLookupSystem _lookupSystem = default!;
2625
[Dependency] private readonly IEntityManager _entityManager = default!;
2726
[Dependency] private readonly MapSystem _mapSystem = default!;
2827
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
@@ -100,12 +99,12 @@ public void HandleAtomMouseMove(ScalingViewport viewport, Vector2 relativePos, C
10099
RaiseNetworkEvent(new MouseMoveEvent(atomRef, CreateClickParams(viewport, relativePos, iconPos)));
101100
}
102101

103-
public (ClientObjectReference Atom, Vector2i IconPosition)? GetAtomUnderMouse(ScalingViewport viewport, Vector2 relativePos, ScreenCoordinates globalPos) {
102+
public (ClientObjectReference Atom, Vector2i IconPosition, bool IsScreen)? GetAtomUnderMouse(ScalingViewport viewport, Vector2 relativePos, ScreenCoordinates globalPos) {
104103
_dreamViewOverlay ??= _overlayManager.GetOverlay<DreamViewOverlay>();
105104
if(_dreamViewOverlay.MouseMap == null)
106105
return null;
107106

108-
UIBox2i viewportBox = viewport.GetDrawBox();
107+
var viewportBox = viewport.GetDrawBox();
109108
if (!viewportBox.Contains((int)relativePos.X, (int)relativePos.Y))
110109
return null; // Was outside of the viewport
111110

@@ -122,15 +121,20 @@ public void HandleAtomMouseMove(ScalingViewport viewport, Vector2 relativePos, C
122121
return null;
123122

124123
if (underMouse.ClickUid == EntityUid.Invalid) { // A turf
125-
return GetTurfUnderMouse(mapCoords, out _);
124+
var turf = GetTurfUnderMouse(mapCoords, out _);
125+
if (turf == null)
126+
return null;
127+
128+
return (turf.Value.Atom, turf.Value.IconPosition, false);
126129
} else {
127-
Vector2i iconPosition = (Vector2i) ((mapCoords.Position - underMouse.Position) * _dreamInterfaceManager.IconSize);
130+
var iconPosition = (Vector2i) ((mapCoords.Position - underMouse.Position) * _dreamInterfaceManager.IconSize);
131+
var reference = new ClientObjectReference(_entityManager.GetNetEntity(underMouse.ClickUid));
128132

129-
return (new(_entityManager.GetNetEntity(underMouse.ClickUid)), iconPosition);
133+
return (reference, iconPosition, underMouse.IsScreen);
130134
}
131135
}
132136

133-
private (ClientObjectReference Atom, Vector2i IconPosition)? GetTurfUnderMouse(MapCoordinates mapCoords, out uint? turfId) {
137+
public (ClientObjectReference Atom, Vector2i IconPosition)? GetTurfUnderMouse(MapCoordinates mapCoords, out uint? turfId) {
134138
// Grid coordinates are half a meter off from entity coordinates
135139
mapCoords = new MapCoordinates(mapCoords.Position + new Vector2(0.5f), mapCoords.MapId);
136140

@@ -151,37 +155,14 @@ public void HandleAtomMouseMove(ScalingViewport viewport, Vector2 relativePos, C
151155
private bool OnPress(ScalingViewport viewport, GUIBoundKeyEventArgs args, ControlDescriptor descriptor) {
152156
//either turf or atom was clicked, and it was a right-click, and the popup menu is enabled, and the right-click parameter is disabled
153157
if (args.Function == EngineKeyFunctions.UIRightClick && _dreamInterfaceManager.ShowPopupMenus && !descriptor.RightClick.Value) {
154-
var mapCoords = viewport.ScreenToMap(args.PointerLocation.Position);
155-
var entities = _lookupSystem.GetEntitiesInRange(mapCoords, 0.01f, LookupFlags.Uncontained | LookupFlags.Approximate);
158+
_contextMenu.RepopulateEntities(viewport, args.RelativePosition, args.PointerLocation);
159+
if (_contextMenu.EntityCount != 0) { //don't open a 1x1 empty context menu
160+
var contextMenuLocation = args.PointerLocation.Position / _userInterfaceManager.ModalRoot.UIScale; // Take scaling into account
156161

157-
ClientObjectReference[] objects = new ClientObjectReference[entities.Count + 1];
158-
159-
// We can't index a HashSet so we have to use a foreach loop
160-
int index = 0;
161-
foreach (var uid in entities) {
162-
objects[index] = new ClientObjectReference(_entityManager.GetNetEntity(uid));
163-
index += 1;
162+
_contextMenu.Measure(_userInterfaceManager.ModalRoot.Size);
163+
_contextMenu.Open(UIBox2.FromDimensions(contextMenuLocation, _contextMenu.DesiredSize));
164164
}
165165

166-
// Append the turf to the end of the context menu
167-
var turfUnderMouse = GetTurfUnderMouse(mapCoords, out var turfId)?.Atom;
168-
if (turfUnderMouse is not null)
169-
objects[index] = turfUnderMouse.Value;
170-
171-
//TODO filter entities by the valid verbs that exist on them
172-
//they should only show up if there is a verb attached to usr which matches the filter in world syntax
173-
//ie, obj|turf in world
174-
//note that popup_menu = 0 overrides this behaviour, as does verb invisibility (urgh), and also hidden
175-
//because BYOND sure loves redundancy
176-
177-
_contextMenu.RepopulateEntities(objects, turfId);
178-
if(_contextMenu.EntityCount == 0)
179-
return true; //don't open a 1x1 empty context menu
180-
181-
_contextMenu.Measure(_userInterfaceManager.ModalRoot.Size);
182-
Vector2 contextMenuLocation = args.PointerLocation.Position / _userInterfaceManager.ModalRoot.UIScale; // Take scaling into account
183-
_contextMenu.Open(UIBox2.FromDimensions(contextMenuLocation, _contextMenu.DesiredSize));
184-
185166
return true;
186167
}
187168

0 commit comments

Comments
 (0)