1
1
using System . Linq ;
2
2
using System . Numerics ;
3
+ using System . Runtime . InteropServices ;
3
4
using Content . Client . Actions ;
4
5
using Content . Client . Construction ;
5
6
using Content . Client . Gameplay ;
@@ -50,14 +51,17 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
50
51
[ UISystemDependency ] private readonly TargetOutlineSystem ? _targetOutline = default ;
51
52
[ UISystemDependency ] private readonly SpriteSystem _spriteSystem = default ! ;
52
53
54
+ private const int DefaultPageIndex = 0 ;
53
55
private ActionButtonContainer ? _container ;
54
- private readonly List < EntityUid ? > _actions = new ( ) ;
56
+ private readonly List < ActionPage > _pages = new ( ) ;
57
+ private int _currentPageIndex = DefaultPageIndex ;
55
58
private readonly DragDropHelper < ActionButton > _menuDragHelper ;
56
59
private readonly TextureRect _dragShadow ;
57
60
private ActionsWindow ? _window ;
58
61
59
62
private ActionsBar ? ActionsBar => UIManager . GetActiveUIWidgetOrNull < ActionsBar > ( ) ;
60
63
private MenuButton ? ActionButton => UIManager . GetActiveUIWidgetOrNull < MenuBar . Widgets . GameTopMenuBar > ( ) ? . ActionButton ;
64
+ private ActionPage CurrentPage => _pages [ _currentPageIndex ] ;
61
65
62
66
public bool IsDragging => _menuDragHelper . IsDragging ;
63
67
@@ -75,8 +79,13 @@ public ActionUIController()
75
79
Stretch = StretchMode . Scale ,
76
80
Visible = false ,
77
81
SetSize = new Vector2 ( 64 , 64 ) ,
78
- MouseFilter = MouseFilterMode . Ignore
82
+ MouseFilter = MouseFilterMode . Ignore ,
79
83
} ;
84
+
85
+ var pageCount = ContentKeyFunctions . GetLoadoutBoundKeys ( ) . Length ;
86
+ var buttonCount = ContentKeyFunctions . GetHotbarBoundKeys ( ) . Length ;
87
+ for ( var i = 0 ; i < pageCount ; i ++ )
88
+ _pages . Add ( new ActionPage ( buttonCount ) ) ;
80
89
}
81
90
82
91
public override void Initialize ( )
@@ -129,6 +138,15 @@ public void OnStateEntered(GameplayState state)
129
138
} , false , true ) ) ;
130
139
}
131
140
141
+ var loadoutKeys = ContentKeyFunctions . GetLoadoutBoundKeys ( ) ;
142
+ for ( var i = 0 ; i < loadoutKeys . Length ; i ++ )
143
+ {
144
+ var boundId = i ; // This is needed, because the lambda captures it.
145
+ var boundKey = loadoutKeys [ i ] ;
146
+ builder = builder . Bind ( boundKey ,
147
+ InputCmdHandler . FromDelegate ( _ => ChangePage ( boundId ) ) ) ;
148
+ }
149
+
132
150
builder
133
151
. Bind ( ContentKeyFunctions . OpenActionsMenu ,
134
152
InputCmdHandler . FromDelegate ( _ => ToggleWindow ( ) ) )
@@ -315,34 +333,80 @@ public void OnStateExited(GameplayState state)
315
333
private void TriggerAction ( int index )
316
334
{
317
335
if ( _actionsSystem == null ||
318
- ! _actions . TryGetValue ( index , out var actionId ) ||
336
+ CurrentPage [ index ] is not { } actionId ||
319
337
! _actionsSystem . TryGetActionData ( actionId , out var baseAction ) )
320
- {
321
338
return ;
322
- }
323
339
324
340
if ( baseAction is BaseTargetActionComponent action )
325
- ToggleTargeting ( actionId . Value , action ) ;
341
+ ToggleTargeting ( actionId , action ) ;
326
342
else
327
- _actionsSystem ? . TriggerAction ( actionId . Value , baseAction ) ;
343
+ _actionsSystem ? . TriggerAction ( actionId , baseAction ) ;
344
+ }
345
+
346
+ private void ChangePage ( int index )
347
+ {
348
+ if ( _actionsSystem == null )
349
+ return ;
350
+
351
+ var lastPage = _pages . Count - 1 ;
352
+ if ( index < 0 )
353
+ index = lastPage ;
354
+ else if ( index > lastPage )
355
+ index = 0 ;
356
+
357
+ _currentPageIndex = index ;
358
+ var page = _pages [ _currentPageIndex ] ;
359
+ _container ? . SetActionData ( _actionsSystem , page ) ;
360
+
361
+ ActionsBar ! . PageButtons . Label . Text = $ "{ _currentPageIndex + 1 } ";
362
+ }
363
+
364
+ private void OnLeftArrowPressed ( ButtonEventArgs args ) => ChangePage ( _currentPageIndex - 1 ) ;
365
+
366
+ private void OnRightArrowPressed ( ButtonEventArgs args ) => ChangePage ( _currentPageIndex + 1 ) ;
367
+
368
+ private void AppendAction ( EntityUid action )
369
+ {
370
+ if ( _container == null )
371
+ return ;
372
+
373
+ foreach ( var button in _container . GetButtons ( ) )
374
+ {
375
+ if ( button . ActionId != null )
376
+ continue ;
377
+
378
+ SetAction ( button , action ) ;
379
+ return ;
380
+ }
381
+
382
+ foreach ( var page in _pages )
383
+ for ( var i = 0 ; i < page . Size ; i ++ )
384
+ {
385
+ var pageAction = page [ i ] ;
386
+ if ( pageAction != null )
387
+ continue ;
388
+
389
+ page [ i ] = action ;
390
+ return ;
391
+ }
328
392
}
329
393
330
394
private void OnActionAdded ( EntityUid actionId )
331
395
{
332
396
if ( _actionsSystem == null ||
333
397
! _actionsSystem . TryGetActionData ( actionId , out var action ) )
334
- {
335
398
return ;
336
- }
337
399
338
400
// if the action is toggled when we add it, start targeting
339
401
if ( action is BaseTargetActionComponent targetAction && action . Toggled )
340
402
StartTargeting ( actionId , targetAction ) ;
341
403
342
- if ( _actions . Contains ( actionId ) )
343
- return ;
404
+ foreach ( var page in _pages )
405
+ for ( var i = 0 ; i < page . Size ; i ++ )
406
+ if ( page [ i ] == actionId )
407
+ return ;
344
408
345
- _actions . Add ( actionId ) ;
409
+ AppendAction ( actionId ) ;
346
410
}
347
411
348
412
private void OnActionRemoved ( EntityUid actionId )
@@ -353,19 +417,24 @@ private void OnActionRemoved(EntityUid actionId)
353
417
if ( actionId == SelectingTargetFor )
354
418
StopTargeting ( ) ;
355
419
356
- _actions . RemoveAll ( x => x == actionId ) ;
420
+ foreach ( var page in _pages )
421
+ for ( var i = 0 ; i < page . Size ; i ++ )
422
+ if ( page [ i ] == actionId )
423
+ page [ i ] = null ;
357
424
}
358
425
359
426
private void OnActionsUpdated ( )
360
427
{
361
428
QueueWindowUpdate ( ) ;
429
+ if ( _container == null )
430
+ return ;
362
431
363
432
// TODO ACTIONS allow buttons to persist across state applications
364
433
// Then we don't have to interrupt drags any time the buttons get rebuilt.
365
434
_menuDragHelper . EndDrag ( ) ;
366
435
367
436
if ( _actionsSystem != null )
368
- _container ? . SetActionData ( _actionsSystem , _actions . ToArray ( ) ) ;
437
+ _container ? . SetActionData ( _actionsSystem , _pages [ _currentPageIndex ] ) ;
369
438
}
370
439
371
440
private void ActionButtonPressed ( ButtonEventArgs args )
@@ -512,7 +581,7 @@ private void SearchAndDisplay()
512
581
PopulateActions ( actions ) ;
513
582
}
514
583
515
- private void SetAction ( ActionButton button , EntityUid ? actionId , bool updateSlots = true )
584
+ private void SetAction ( ActionButton button , EntityUid ? actionId )
516
585
{
517
586
if ( _actionsSystem == null )
518
587
return ;
@@ -523,27 +592,28 @@ private void SetAction(ActionButton button, EntityUid? actionId, bool updateSlot
523
592
{
524
593
button . ClearData ( ) ;
525
594
if ( _container ? . TryGetButtonIndex ( button , out position ) ?? false )
526
- {
527
- if ( _actions . Count > position && position >= 0 )
528
- _actions . RemoveAt ( position ) ;
529
- }
595
+ CurrentPage [ position ] = null ;
530
596
}
531
597
else if ( button . TryReplaceWith ( actionId . Value , _actionsSystem ) &&
532
598
_container != null &&
533
599
_container . TryGetButtonIndex ( button , out position ) )
534
- {
535
- if ( position >= _actions . Count )
536
- {
537
- _actions . Add ( actionId ) ;
538
- }
600
+ if ( position >= 0 && position < CurrentPage . Size )
601
+ CurrentPage [ position ] = actionId ;
539
602
else
540
603
{
541
- _actions [ position ] = actionId ;
604
+ if ( _pages . Count <= _currentPageIndex )
605
+ return ;
606
+ // Add the button to the next page if there's no space on the current one
607
+ var nextPage = _pages [ _currentPageIndex + 1 ] ;
608
+ int i ;
609
+ for ( i = 0 ; i < nextPage . Size ; i ++ )
610
+ if ( nextPage [ i ] == null )
611
+ {
612
+ nextPage [ i ] = actionId ;
613
+ break ;
614
+ }
615
+ ChangePage ( _currentPageIndex + 1 ) ; //TODO: Make this a client config?
542
616
}
543
- }
544
-
545
- if ( updateSlots )
546
- _container ? . SetActionData ( _actionsSystem , _actions . ToArray ( ) ) ;
547
617
}
548
618
549
619
private void DragAction ( )
@@ -559,14 +629,14 @@ private void DragAction()
559
629
if ( currentlyHovered is ActionButton button )
560
630
{
561
631
swapAction = button . ActionId ;
562
- SetAction ( button , action , false ) ;
632
+ SetAction ( button , action ) ;
563
633
}
564
634
565
635
if ( dragged . Parent is ActionButtonContainer )
566
- SetAction ( dragged , swapAction , false ) ;
636
+ SetAction ( dragged , swapAction ) ;
567
637
568
638
if ( _actionsSystem != null )
569
- _container ? . SetActionData ( _actionsSystem , _actions . ToArray ( ) ) ;
639
+ _container ? . SetActionData ( _actionsSystem , _pages [ _currentPageIndex ] ) ;
570
640
571
641
_menuDragHelper . EndDrag ( ) ;
572
642
}
@@ -717,9 +787,10 @@ private void UnloadGui()
717
787
_actionsSystem ? . UnlinkAllActions ( ) ;
718
788
719
789
if ( ActionsBar == null )
720
- {
721
790
return ;
722
- }
791
+
792
+ ActionsBar . PageButtons . LeftArrow . OnPressed -= OnLeftArrowPressed ;
793
+ ActionsBar . PageButtons . RightArrow . OnPressed -= OnRightArrowPressed ;
723
794
724
795
if ( _window != null )
725
796
{
@@ -747,9 +818,10 @@ private void LoadGui()
747
818
_window . FilterButton . OnItemSelected += OnFilterSelected ;
748
819
749
820
if ( ActionsBar == null )
750
- {
751
821
return ;
752
- }
822
+
823
+ ActionsBar . PageButtons . LeftArrow . OnPressed += OnLeftArrowPressed ;
824
+ ActionsBar . PageButtons . RightArrow . OnPressed += OnRightArrowPressed ;
753
825
754
826
RegisterActionContainer ( ActionsBar . ActionsContainer ) ;
755
827
@@ -779,13 +851,10 @@ private void AssignSlots(List<SlotAssignment> assignments)
779
851
if ( _actionsSystem == null )
780
852
return ;
781
853
782
- _actions . Clear ( ) ;
783
- foreach ( var assign in assignments )
784
- {
785
- _actions . Add ( assign . ActionId ) ;
786
- }
854
+ foreach ( ref var assignment in CollectionsMarshal . AsSpan ( assignments ) )
855
+ _pages [ assignment . Hotbar ] [ assignment . Slot ] = assignment . ActionId ;
787
856
788
- _container ? . SetActionData ( _actionsSystem , _actions . ToArray ( ) ) ;
857
+ _container ? . SetActionData ( _actionsSystem , _pages [ _currentPageIndex ] ) ;
789
858
}
790
859
791
860
public void RemoveActionContainer ( )
@@ -822,7 +891,7 @@ private void OnComponentLinked(ActionsComponent component)
822
891
return ;
823
892
824
893
LoadDefaultActions ( ) ;
825
- _container ? . SetActionData ( _actionsSystem , _actions . ToArray ( ) ) ;
894
+ _container ? . SetActionData ( _actionsSystem , _pages [ _currentPageIndex ] ) ;
826
895
QueueWindowUpdate ( ) ;
827
896
}
828
897
@@ -841,11 +910,27 @@ private void LoadDefaultActions()
841
910
var actions = _actionsSystem . GetClientActions ( ) . Where ( action => action . Comp . AutoPopulate ) . ToList ( ) ;
842
911
actions . Sort ( ActionComparer ) ;
843
912
844
- _actions . Clear ( ) ;
845
- foreach ( var ( action , _) in actions )
913
+ var offset = 0 ;
914
+ var totalPages = _pages . Count ;
915
+ var pagesLeft = totalPages ;
916
+ var currentPage = DefaultPageIndex ;
917
+ while ( pagesLeft > 0 )
846
918
{
847
- if ( ! _actions . Contains ( action ) )
848
- _actions . Add ( action ) ;
919
+ var page = _pages [ currentPage ] ;
920
+ var pageSize = page . Size ;
921
+
922
+ for ( var slot = 0 ; slot < pageSize ; slot ++ )
923
+ if ( slot + offset < actions . Count )
924
+ page [ slot ] = actions [ slot + offset ] . Id ;
925
+ else
926
+ page [ slot ] = null ;
927
+
928
+ offset += pageSize ;
929
+ currentPage ++ ;
930
+ if ( currentPage == totalPages )
931
+ currentPage = 0 ;
932
+
933
+ pagesLeft -- ;
849
934
}
850
935
}
851
936
@@ -956,4 +1041,22 @@ private void StopTargeting()
956
1041
handOverlay . IconOverride = null ;
957
1042
handOverlay . EntityOverride = null ;
958
1043
}
1044
+
1045
+ //TODO: Serialize this shit
1046
+ private sealed class ActionPage ( int size )
1047
+ {
1048
+ public readonly EntityUid ? [ ] Data = new EntityUid ? [ size ] ;
1049
+
1050
+ public EntityUid ? this [ int index ]
1051
+ {
1052
+ get => Data [ index ] ;
1053
+ set => Data [ index ] = value ;
1054
+ }
1055
+
1056
+ public static implicit operator EntityUid ? [ ] ( ActionPage p ) => p . Data . ToArray ( ) ;
1057
+
1058
+ public void Clear ( ) => Array . Fill ( Data , null ) ;
1059
+
1060
+ public int Size => Data . Length ;
1061
+ }
959
1062
}
0 commit comments