@@ -27,6 +27,9 @@ internal sealed class MouseInputSystem : SharedMouseInputSystem {
2727 [ Dependency ] private readonly IConfigurationManager _configurationManager = default ! ;
2828 [ Dependency ] private readonly IDreamInterfaceManager _dreamInterfaceManager = default ! ;
2929 [ Dependency ] private readonly ClientAppearanceSystem _appearanceSystem = default ! ;
30+ [ Dependency ] private readonly IClyde _clyde = default ! ;
31+ [ Dependency ] private readonly ILogManager _logManager = default ! ;
32+ private ISawmill _sawmill = default ! ;
3033
3134 private DreamViewOverlay ? _dreamViewOverlay ;
3235 private ContextMenuPopup _contextMenu = default ! ;
@@ -41,6 +44,7 @@ private sealed class EntityClickInformation(ClientObjectReference atom, ScreenCo
4144
4245 public override void Initialize ( ) {
4346 UpdatesOutsidePrediction = true ;
47+ _sawmill = _logManager . GetSawmill ( "opendream.mouseinput" ) ;
4448
4549 _contextMenu = new ContextMenuPopup ( ) ;
4650 _userInterfaceManager . ModalRoot . AddChild ( _contextMenu ) ;
@@ -54,8 +58,11 @@ public override void Update(float frameTime) {
5458 var currentMousePos = _inputManager . MouseScreenPosition . Position ;
5559 var distance = ( currentMousePos - _selectedEntity . InitialMousePos . Position ) . Length ( ) ;
5660
57- if ( distance > 3f )
61+ if ( distance > 3f ) {
5862 _selectedEntity . IsDrag = true ;
63+ if ( _dreamInterfaceManager . DefaultMap is { } map )
64+ UpdateMouseCursor ( map . Viewport , _selectedEntity . Atom ) ;
65+ }
5966 }
6067 }
6168
@@ -79,13 +86,15 @@ public void HandleStatClick(string atomRef, bool isRight, bool isMiddle) {
7986 }
8087
8188 public void HandleAtomMouseEntered ( ScalingViewport viewport , Vector2 relativePos , ClientObjectReference atomRef , Vector2i iconPos ) {
89+ UpdateMouseCursor ( viewport , atomRef ) ;
8290 if ( ! HasMouseEventEnabled ( atomRef , AtomMouseEvents . Enter ) )
8391 return ;
8492
8593 RaiseNetworkEvent ( new MouseEnteredEvent ( atomRef , CreateClickParams ( viewport , relativePos , iconPos ) ) ) ;
8694 }
8795
8896 public void HandleAtomMouseExited ( ScalingViewport viewport , ClientObjectReference atomRef ) {
97+ UpdateMouseCursor ( viewport , null ) ;
8998 if ( ! HasMouseEventEnabled ( atomRef , AtomMouseEvents . Exit ) )
9099 return ;
91100
@@ -178,21 +187,71 @@ private bool OnPress(ScalingViewport viewport, GUIBoundKeyEventArgs args, Contro
178187 }
179188
180189 private bool OnRelease ( ScalingViewport viewport , GUIBoundKeyEventArgs args ) {
181- if ( _selectedEntity == null )
190+ if ( _selectedEntity == null ) {
191+ UpdateMouseCursor ( viewport , null ) ;
182192 return false ;
193+ }
183194
195+ var overAtom = GetAtomUnderMouse ( viewport , args . RelativePixelPosition , args . PointerLocation ) ;
184196 if ( ! _selectedEntity . IsDrag ) {
185197 RaiseNetworkEvent ( new AtomClickedEvent ( _selectedEntity . Atom , _selectedEntity . ClickParams ) ) ;
186198 } else {
187- var overAtom = GetAtomUnderMouse ( viewport , args . RelativePixelPosition , args . PointerLocation ) ;
188-
189199 RaiseNetworkEvent ( new AtomDraggedEvent ( _selectedEntity . Atom , overAtom ? . Atom , _selectedEntity . ClickParams ) ) ;
190200 }
191201
192202 _selectedEntity = null ;
203+ UpdateMouseCursor ( viewport , overAtom ? . Atom ) ;
193204 return true ;
194205 }
195206
207+ private void UpdateMouseCursor ( ScalingViewport viewport , ClientObjectReference ? mouseOver ) {
208+ var isDragging = _selectedEntity ? . IsDrag ?? false ;
209+ if ( ! mouseOver . HasValue || ! _appearanceSystem . TryGetAppearance ( mouseOver . Value , out var mouseOverAppearance ) ) {
210+ if ( ! isDragging )
211+ SetCursorFromDefine ( 1 , _dreamInterfaceManager . Cursors . BaseCursor , viewport ) ;
212+ return ;
213+ }
214+
215+ if ( isDragging ) {
216+ if ( ! _appearanceSystem . TryGetAppearance ( _selectedEntity ! . Atom , out var draggingAppearance ) ) {
217+ SetCursorFromDefine ( 1 , _dreamInterfaceManager . Cursors . DragCursor , viewport ) ;
218+ return ;
219+ }
220+
221+ var define = mouseOverAppearance . MouseDropZone
222+ ? mouseOverAppearance . MouseDropPointer
223+ : draggingAppearance . MouseDragPointer ;
224+ var cursor = mouseOverAppearance . MouseDropZone
225+ ? _dreamInterfaceManager . Cursors . DropCursor
226+ : _dreamInterfaceManager . Cursors . DragCursor ;
227+ SetCursorFromDefine ( define , cursor , viewport ) ;
228+ } else {
229+ SetCursorFromDefine ( mouseOverAppearance . MouseOverPointer , _dreamInterfaceManager . Cursors . OverCursor , viewport ) ;
230+ }
231+ }
232+
233+ private void SetCursorFromDefine ( int define , ICursor ? activeCursor , ScalingViewport viewport ) {
234+ _sawmill . Verbose ( $ "SetCursor { define } { activeCursor } ") ;
235+
236+ if ( _dreamInterfaceManager . Cursors . AllStateSet ) {
237+ viewport . CustomCursorShape = _dreamInterfaceManager . Cursors . BaseCursor ;
238+ } else {
239+ viewport . CustomCursorShape = define switch {
240+ 0 => _dreamInterfaceManager . Cursors . BaseCursor , //MOUSE_INACTIVE_POINTER
241+ 1 => activeCursor , //MOUSE_ACTIVE_POINTER
242+ //skipping 2 is intentional, it's what byond does
243+ 3 => _clyde . GetStandardCursor ( StandardCursorShape . Crosshair ) , //MOUSE_DRAG_POINTER
244+ 4 => _clyde . GetStandardCursor ( StandardCursorShape . Hand ) , //MOUSE_DROP_POINTER
245+ 5 => _clyde . GetStandardCursor ( StandardCursorShape . Arrow ) , //MOUSE_ARROW_POINTER
246+ 6 => _clyde . GetStandardCursor ( StandardCursorShape . Crosshair ) , //MOUSE_CROSSHAIRS_POINTER
247+ 7 => _clyde . GetStandardCursor ( StandardCursorShape . Hand ) , //MOUSE_HAND_POINTER
248+ _ => null
249+ } ;
250+ }
251+
252+ _clyde . SetCursor ( viewport . CustomCursorShape ) ;
253+ }
254+
196255 private ClickParams CreateClickParams ( ScalingViewport viewport , GUIBoundKeyEventArgs args , Vector2i iconPos ) {
197256 bool right = args . Function == EngineKeyFunctions . UIRightClick ;
198257 bool middle = args . Function == OpenDreamKeyFunctions . MouseMiddle ;
@@ -202,7 +261,7 @@ private ClickParams CreateClickParams(ScalingViewport viewport, GUIBoundKeyEvent
202261 UIBox2i viewportBox = viewport . GetDrawBox ( ) ;
203262 Vector2 screenLocPos = ( args . RelativePixelPosition - viewportBox . TopLeft ) / viewportBox . Size * viewport . ViewportSize ;
204263 float screenLocY = viewport . ViewportSize . Y - screenLocPos . Y ; // Flip the Y
205- ScreenLocation screenLoc = new ScreenLocation ( ( int ) screenLocPos . X , ( int ) screenLocY , 32 ) ; // TODO: icon_size other than 32
264+ ScreenLocation screenLoc = new ScreenLocation ( ( int ) screenLocPos . X , ( int ) screenLocY , 32 ) ; // TODO: icon_size other than 32
206265
207266 // TODO: Take icon transformations into account for iconPos
208267 return new ( screenLoc , right , middle , shift , ctrl , alt , iconPos . X , iconPos . Y ) ;
0 commit comments