Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions DMCompiler/DMStandard/Types/Atoms/_Atom.dm
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@
var/step_x as opendream_unimplemented
var/step_y as opendream_unimplemented
var/render_source
var/tmp/mouse_drag_pointer as opendream_unimplemented
var/tmp/mouse_drop_pointer as opendream_unimplemented
var/tmp/mouse_over_pointer as opendream_unimplemented
var/tmp/mouse_drag_pointer
var/tmp/mouse_drop_pointer
var/tmp/mouse_over_pointer
var/render_target
var/vis_flags as opendream_unimplemented

Expand Down
2 changes: 1 addition & 1 deletion DMCompiler/DMStandard/Types/Client.dm
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
var/script as opendream_unimplemented
var/color = 0 as opendream_unimplemented
var/control_freak as opendream_unimplemented
var/mouse_pointer_icon as opendream_unimplemented
var/mouse_pointer_icon
var/preload_rsc = 1 as opendream_unimplemented
var/fps = 0 as opendream_unimplemented
var/dir = NORTH as opendream_unimplemented
Expand Down
8 changes: 4 additions & 4 deletions DMCompiler/DMStandard/Types/Image.dm
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
var/maptext_height = 32
var/maptext_x = 0
var/maptext_y = 0
var/mouse_over_pointer = 0 as opendream_unimplemented
var/mouse_drag_pointer = 0 as opendream_unimplemented
var/mouse_drop_pointer = 1 as opendream_unimplemented
var/mouse_drop_zone = 0 as opendream_unimplemented
var/mouse_over_pointer = 0
var/mouse_drag_pointer = 0
var/mouse_drop_pointer = 1
var/mouse_drop_zone = 0
var/mouse_opacity = 1
var/name = "image"
var/opacity = 0 as opendream_unimplemented
Expand Down
67 changes: 64 additions & 3 deletions OpenDreamClient/Input/MouseInputSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ internal sealed class MouseInputSystem : SharedMouseInputSystem {
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IDreamInterfaceManager _dreamInterfaceManager = default!;
[Dependency] private readonly ClientAppearanceSystem _appearanceSystem = default!;
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly ILogManager _logManager = default!;
private ISawmill _sawmill = default!;

public bool IsDragging { get => _selectedEntity?.IsDrag ?? false; }

Check notice

Code scanning / InspectCode

Use preferred body style: Convert into property, indexer, or event with preferred body style Note

Code body does not conform to code style settings: use expression-bodied property

private DreamViewOverlay? _dreamViewOverlay;
private ContextMenuPopup _contextMenu = default!;
Expand All @@ -42,6 +47,7 @@ private sealed class EntityClickInformation(ClientObjectReference atom, ScreenCo

public override void Initialize() {
UpdatesOutsidePrediction = true;
_sawmill = _logManager.GetSawmill("opendream.mouseinput");

_contextMenu = new ContextMenuPopup();
_userInterfaceManager.ModalRoot.AddChild(_contextMenu);
Expand Down Expand Up @@ -193,25 +199,80 @@ private bool OnPress(ScalingViewport viewport, GUIBoundKeyEventArgs args, Contro
var clickParams = CreateClickParams(viewport, args, underMouse.Value.IconPosition); // If client.show_popup_menu is disabled, this will handle sending right clicks

_selectedEntity = new(atom, args.PointerLocation, clickParams);
//cursor stuff
if (_appearanceSystem.TryGetAppearance(atom, out var atomAppearance)) {
SetCursorFromDefine(atomAppearance.MouseDragPointer, _dreamInterfaceManager.Cursors.DragCursor, viewport);
}
return true;
}

private bool OnRelease(ScalingViewport viewport, GUIBoundKeyEventArgs args) {
if (_selectedEntity == null)
if (_selectedEntity == null) {
SetCursorFromDefine(0, _dreamInterfaceManager.Cursors.BaseCursor, viewport); //default
return false;
}

var overAtom = GetAtomUnderMouse(viewport, args.RelativePixelPosition, args.PointerLocation);
if (overAtom is not null && _appearanceSystem.TryGetAppearance(overAtom.Value.Atom, out var atomAppearance)) {
SetCursorFromDefine(atomAppearance.MouseOverPointer, _dreamInterfaceManager.Cursors.OverCursor, viewport);
} else
SetCursorFromDefine(0, _dreamInterfaceManager.Cursors.BaseCursor, viewport);

if (!_selectedEntity.IsDrag) {
RaiseNetworkEvent(new AtomClickedEvent(_selectedEntity.Atom, _selectedEntity.ClickParams));
} else {
var overAtom = GetAtomUnderMouse(viewport, args.RelativePixelPosition, args.PointerLocation);

RaiseNetworkEvent(new AtomDraggedEvent(_selectedEntity.Atom, overAtom?.Atom, _selectedEntity.ClickParams));
}


_selectedEntity = null;
return true;
}

public void SetCursorFromDefine(int define, ICursor? activeCursor, ScalingViewport viewport) {
_sawmill.Debug($"SetCursor {define}");
if (_dreamInterfaceManager.Cursors.AllStateSet) {
viewport.CustomCursorShape = (_dreamInterfaceManager.Cursors.BaseCursor);
_clyde.SetCursor(viewport.CustomCursorShape);
return;
}
switch (define) {

Check warning

Code scanning / InspectCode

Incorrect blank lines: Blank lines are missing elsewhere Warning

Blank lines are missing, expected minimum 1 instead of 0
case 0: //MOUSE_INACTIVE_POINTER
viewport.CustomCursorShape = _dreamInterfaceManager.Cursors.BaseCursor;
_clyde.SetCursor(viewport.CustomCursorShape);
break;
case 1: //MOUSE_ACTIVE_POINTER
viewport.CustomCursorShape = (activeCursor);
_clyde.SetCursor(viewport.CustomCursorShape);
break;
//skipping 2 is intentional, it's what byond does
case 3: //MOUSE_DRAG_POINTER
viewport.CustomCursorShape = (_clyde.GetStandardCursor(StandardCursorShape.Move));
_clyde.SetCursor(viewport.CustomCursorShape);
break;
case 4: //MOUSE_DROP_POINTER
viewport.CustomCursorShape = (_clyde.GetStandardCursor(StandardCursorShape.NotAllowed));
_clyde.SetCursor(viewport.CustomCursorShape);
break;
case 5: //MOUSE_ARROW_POINTER
viewport.CustomCursorShape = (_clyde.GetStandardCursor(StandardCursorShape.Arrow));
_clyde.SetCursor(viewport.CustomCursorShape);
break;
case 6: //MOUSE_CROSSHAIRS_POINTER
viewport.CustomCursorShape = (_clyde.GetStandardCursor(StandardCursorShape.Crosshair));
_clyde.SetCursor(viewport.CustomCursorShape);
break;
case 7: //MOUSE_HAND_POINTER
viewport.CustomCursorShape = (_clyde.GetStandardCursor(StandardCursorShape.Hand));
_clyde.SetCursor(viewport.CustomCursorShape);
break;
default: //invalid
viewport.CustomCursorShape = (null); //default cursor
_clyde.SetCursor(viewport.CustomCursorShape);
break;
}
}
private ClickParams CreateClickParams(ScalingViewport viewport, GUIBoundKeyEventArgs args, Vector2i iconPos) {
bool right = args.Function == EngineKeyFunctions.UIRightClick;
bool middle = args.Function == OpenDreamKeyFunctions.MouseMiddle;
Expand All @@ -221,7 +282,7 @@ private ClickParams CreateClickParams(ScalingViewport viewport, GUIBoundKeyEvent
UIBox2i viewportBox = viewport.GetDrawBox();
Vector2 screenLocPos = (args.RelativePixelPosition - viewportBox.TopLeft) / viewportBox.Size * viewport.ViewportSize;
float screenLocY = viewport.ViewportSize.Y - screenLocPos.Y; // Flip the Y
ScreenLocation screenLoc = new ScreenLocation((int) screenLocPos.X, (int) screenLocY, 32); // TODO: icon_size other than 32
ScreenLocation screenLoc = new ScreenLocation((int)screenLocPos.X, (int)screenLocY, 32); // TODO: icon_size other than 32

// TODO: Take icon transformations into account for iconPos
return new(screenLoc, right, middle, shift, ctrl, alt, iconPos.X, iconPos.Y);
Expand Down
21 changes: 17 additions & 4 deletions OpenDreamClient/Interface/Controls/ControlMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public sealed class ControlMap(ControlDescriptor controlDescriptor, ControlWindo
public ScalingViewport Viewport { get; private set; }

[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
[Dependency] private readonly IDreamInterfaceManager _dreamInterfaceManager = default!;
private MouseInputSystem? _mouseInput;
private ClientAppearanceSystem? _appearanceSystem;

Expand Down Expand Up @@ -138,19 +139,31 @@ public override bool TryGetProperty(string property, [NotNullWhen(true)] out IDM
}

private void UpdateAtomUnderMouse(ClientObjectReference? atom, Vector2 relativePos, Vector2i iconPos) {
//if dragging and atom drop pointer: set drop pointer
//if atom over pointer: set over pointer
//else set pointer to default state
if (!_atomUnderMouse.Equals(atom)) {
_entitySystemManager.Resolve(ref _appearanceSystem);

var name = (atom != null) ? _appearanceSystem.GetName(atom.Value) : string.Empty;
Window?.SetStatus(name);

if (_atomUnderMouse != null)
_mouseInput?.HandleAtomMouseExited(Viewport, _atomUnderMouse.Value);
if (atom != null)
if (atom != null) {
_mouseInput?.HandleAtomMouseEntered(Viewport, relativePos, atom.Value, iconPos);
if ( _appearanceSystem.TryGetAppearance(atom.Value, out var atomAppearance)) {
if(_mouseInput?.IsDragging ?? false)
if(atomAppearance.MouseDropZone)
_mouseInput?.SetCursorFromDefine(atomAppearance.MouseDropPointer, _dreamInterfaceManager.Cursors.DropCursor, Viewport);
else
_mouseInput?.SetCursorFromDefine(atomAppearance.MouseOverPointer, _dreamInterfaceManager.Cursors.DragCursor, Viewport);
} //else
// _mouseInput?.SetCursorFromDefine(0, _dreamInterfaceManager.Cursors.BaseCursor);

}

Check warning

Code scanning / InspectCode

Incorrect blank lines: Incorrect number of blank lines near braces Warning

Incorrect number of blank lines near braces, expected maximum 0 instead of 1
} else if (atom.HasValue) {
_mouseInput?.HandleAtomMouseMove(Viewport, relativePos, atom.Value, iconPos);
}
_mouseInput?.HandleAtomMouseMove(Viewport, relativePos, atom.Value, iconPos);
}

Check notice

Code scanning / InspectCode

Incorrect indent: Around statement braces Note

Line indent is not restored to the previous level around statement braces

_atomUnderMouse = atom;
}
Expand Down
41 changes: 41 additions & 0 deletions OpenDreamClient/Interface/DreamInterfaceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
using SixLabors.ImageSharp;
using System.Linq;
using Robust.Shared.Map;
using SixLabors.ImageSharp.PixelFormats;

Check warning

Code scanning / InspectCode

Redundant using directive Warning

Using directive is not required by the code and can be safely removed
using OpenToolkit.GraphicsLibraryFramework;

Check warning

Code scanning / InspectCode

Redundant using directive Warning

Using directive is not required by the code and can be safely removed
using Robust.Client.Utility;

Check warning

Code scanning / InspectCode

Redundant using directive Warning

Using directive is not required by the code and can be safely removed

namespace OpenDreamClient.Interface;

Expand Down Expand Up @@ -58,6 +61,7 @@ internal sealed class DreamInterfaceManager : IDreamInterfaceManager {
public Dictionary<string, InterfaceMenu> Menus { get; } = new();
public Dictionary<string, InterfaceMacroSet> MacroSets { get; } = new();
private Dictionary<WindowId, ControlWindow> ClydeWindowIdToControl { get; } = new();
public CursorHolder Cursors { get; private set; } = new();

public ViewRange View {
get => _view;
Expand All @@ -75,6 +79,7 @@ private set {
public bool ShowPopupMenus { get; private set; } = true;
public int IconSize { get; private set; }


private ViewRange _view = new(5);

public void LoadInterfaceFromSource(string source) {
Expand Down Expand Up @@ -326,6 +331,31 @@ private void RxUpdateClientInfo(MsgUpdateClientInfo msg) {
IconSize = msg.IconSize;
View = msg.View;
ShowPopupMenus = msg.ShowPopupMenus;
if (msg.CursorResource != 0)
_dreamResource.LoadResourceAsync<DMIResource>(msg.CursorResource, resource => {
var allState = resource.GetStateAsImage("all", AtomDirection.South);
if (allState is not null) { //all overrides all possible states
Cursors.BaseCursor = _clyde.CreateCursor(allState!, new(32, 32));

Check warning

Code scanning / InspectCode

Redundant nullable warning suppression expression Warning

The nullable warning suppression expression is redundant
Cursors.DragCursor = Cursors.BaseCursor;
Cursors.DropCursor = Cursors.BaseCursor;
Cursors.OverCursor = Cursors.BaseCursor;
Cursors.AllStateSet = true;
} else {
var baseState = resource.GetStateAsImage("", AtomDirection.South);
var overState = resource.GetStateAsImage("over", AtomDirection.South);
var dragState = resource.GetStateAsImage("drag", AtomDirection.South);
var dropState = resource.GetStateAsImage("drop", AtomDirection.South);
Cursors.BaseCursor = baseState is null ? null : _clyde.CreateCursor(baseState, new(32, 32));
Cursors.DragCursor = overState is null ? null : _clyde.CreateCursor(overState, new(32, 32));
Cursors.DropCursor = dragState is null ? null : _clyde.CreateCursor(dragState, new(32, 32));
Cursors.OverCursor = dropState is null ? null : _clyde.CreateCursor(dropState, new(32, 32));
}
//TODO should trigger a cursor update immediately
});
else {
Cursors = new(); //reset to default
}

}

private void ShowPrompt(PromptWindow prompt) {
Expand Down Expand Up @@ -1024,6 +1054,16 @@ private void OnPromptFinished(int promptId, DreamValueType responseType, object?
}
}

public sealed class CursorHolder {
public ICursor? BaseCursor;
public ICursor? DragCursor;
public ICursor? OverCursor;
public ICursor? DropCursor;
public bool AllStateSet = false;

Check warning

Code scanning / InspectCode

Redundant member initializer Warning

Initializing field by default value is redundant

public CursorHolder() {}

Check warning

Code scanning / InspectCode

Empty constructor Warning

Empty constructor is redundant. The compiler generates the same by default.
}

public interface IDreamInterfaceManager {
Dictionary<string, ControlWindow> Windows { get; }
Dictionary<string, InterfaceMenu> Menus { get; }
Expand All @@ -1035,6 +1075,7 @@ public interface IDreamInterfaceManager {
public ViewRange View { get; }
public bool ShowPopupMenus { get; }
public int IconSize { get; }
public CursorHolder Cursors { get; }

void Initialize();
void FrameUpdate(FrameEventArgs frameEventArgs);
Expand Down
2 changes: 2 additions & 0 deletions OpenDreamClient/Interface/DummyDreamInterfaceManager.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using OpenDreamClient.Interface.Controls;
using OpenDreamShared.Dream;
using OpenDreamShared.Network.Messages;
using Robust.Client.Graphics;

Check warning

Code scanning / InspectCode

Redundant using directive Warning

Using directive is not required by the code and can be safely removed
using Robust.Shared.Network;
using Robust.Shared.Timing;

Expand All @@ -20,6 +21,7 @@ public sealed class DummyDreamInterfaceManager : IDreamInterfaceManager {
public ViewRange View => new(5);
public bool ShowPopupMenus => true;
public int IconSize => 32;
public CursorHolder Cursors => new();

[Dependency] private readonly IClientNetManager _netManager = default!;

Expand Down
18 changes: 18 additions & 0 deletions OpenDreamClient/Resources/ResourceTypes/DMIResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Robust.Client.Graphics;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;

namespace OpenDreamClient.Resources.ResourceTypes;

Expand Down Expand Up @@ -50,6 +51,23 @@ private void ProcessDMIData() {
return _states[stateName];
}

public Image<Rgba32>? GetStateAsImage(string? stateName, AtomDirection dir) {
using Stream dmiStream = new MemoryStream(Data);
DMIParser.ParsedDMIDescription description = DMIParser.ParseDMI(dmiStream);

dmiStream.Seek(0, SeekOrigin.Begin);

Image<Rgba32> image = Image.Load<Rgba32>(dmiStream);
if (!(description.GetStateOrDefault(stateName)?.Directions.TryGetValue(dir, out var state) ?? false))
return null;

var result = image.Clone(clone => {
clone.Resize(new Size(description.Width, description.Height));
clone.Crop(new Rectangle(state[0].X, state[0].Y, state[0].X + description.Width, state[0].Y + description.Height));
});
return result;
}

public struct State {
public Dictionary<AtomDirection, AtlasTexture[]> Frames;

Expand Down
32 changes: 32 additions & 0 deletions OpenDreamRuntime/AtomManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@ public bool IsValidAppearanceVar(string name) {
case "maptext_height":
case "maptext_x":
case "maptext_y":
case "mouse_drag_pointer":
case "mouse_drop_pointer":
case "mouse_drop_zone":
case "mouse_over_pointer":
return true;

// Get/SetAppearanceVar doesn't handle filters right now
Expand Down Expand Up @@ -422,6 +426,18 @@ public void SetAppearanceVar(MutableAppearance appearance, string varName, Dream
case "maptext_y":
value.TryGetValueAsInteger(out appearance.MaptextOffset.Y);
break;
case "mouse_drag_pointer":
value.TryGetValueAsInteger(out appearance.MouseDragPointer);
break;
case "mouse_drop_pointer":
value.TryGetValueAsInteger(out appearance.MouseDropPointer);
break;
case "mouse_drop_zone":
appearance.MouseDropZone = value.IsTruthy();
break;
case "mouse_over_pointer":
value.TryGetValueAsInteger(out appearance.MouseOverPointer);
break;
case "appearance":
throw new Exception("Cannot assign the appearance var on an appearance");

Expand Down Expand Up @@ -530,6 +546,14 @@ public DreamValue GetAppearanceVar(ImmutableAppearance appearance, string varNam
return new(appearance.MaptextOffset.X);
case "maptext_y":
return new(appearance.MaptextOffset.Y);
case "mouse_drag_pointer":
return new(appearance.MouseDragPointer);
case "mouse_drop_pointer":
return new(appearance.MouseDropPointer);
case "mouse_drop_zone":
return appearance.MouseDropZone ? DreamValue.True : DreamValue.False;
case "mouse_over_pointer":
return new(appearance.MouseOverPointer);
case "appearance":
MutableAppearance appearanceCopy = appearance.ToMutable(); // Return a copy
return new(appearanceCopy);
Expand Down Expand Up @@ -730,6 +754,10 @@ public MutableAppearance GetAppearanceFromDefinition(DreamObjectDefinition def)
def.TryGetVariable("maptext_height", out var maptextHeightVar);
def.TryGetVariable("maptext_x", out var maptextXVar);
def.TryGetVariable("maptext_y", out var maptextYVar);
def.TryGetVariable("mouse_over_pointer", out var mouseOverPointer);
def.TryGetVariable("mouse_drag_pointer", out var mouseDragPointer);
def.TryGetVariable("mouse_drop_pointer", out var mouseDropPointer);
def.TryGetVariable("mouse_drop_zone", out var mouseDropZone);

appearance = MutableAppearance.Get();
SetAppearanceVar(appearance, "name", nameVar);
Expand All @@ -756,6 +784,10 @@ public MutableAppearance GetAppearanceFromDefinition(DreamObjectDefinition def)
SetAppearanceVar(appearance, "maptext_height", maptextHeightVar);
SetAppearanceVar(appearance, "maptext_x", maptextXVar);
SetAppearanceVar(appearance, "maptext_y", maptextYVar);
SetAppearanceVar(appearance, "mouse_over_pointer", mouseOverPointer);
SetAppearanceVar(appearance, "mouse_drag_pointer", mouseDragPointer);
SetAppearanceVar(appearance, "mouse_drop_pointer", mouseDropPointer);
SetAppearanceVar(appearance, "mouse_drop_zone", mouseDropZone);

if (def.TryGetVariable("transform", out var transformVar) && transformVar.TryGetValueAsDreamObject<DreamObjectMatrix>(out var transformMatrix)) {
appearance.Transform = DreamObjectMatrix.MatrixToTransformFloatArray(transformMatrix);
Expand Down
Loading
Loading