Refactor actions to be entities with components (#19900)

This commit is contained in:
DrSmugleaf
2023-09-08 18:16:05 -07:00
committed by GitHub
parent e18f731b91
commit c71f97e3a2
210 changed files with 10693 additions and 11714 deletions

View File

@@ -13,7 +13,6 @@ using Content.Client.UserInterface.Systems.Actions.Widgets;
using Content.Client.UserInterface.Systems.Actions.Windows;
using Content.Client.UserInterface.Systems.Gameplay;
using Content.Shared.Actions;
using Content.Shared.Actions.ActionTypes;
using Content.Shared.Input;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
@@ -65,7 +64,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
/// <summary>
/// Action slot we are currently selecting a target for.
/// </summary>
public TargetedAction? SelectingTargetFor { get; private set; }
public EntityUid? SelectingTargetFor { get; private set; }
public ActionUIController()
{
@@ -175,7 +174,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
/// </summary>
private bool TargetingOnUse(in PointerInputCmdArgs args)
{
if (!_timing.IsFirstTimePredicted || _actionsSystem == null || SelectingTargetFor is not { } action)
if (!_timing.IsFirstTimePredicted || _actionsSystem == null || SelectingTargetFor is not { } actionId)
return false;
if (_playerManager.LocalPlayer?.ControlledEntity is not { } user)
@@ -184,6 +183,12 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
if (!EntityManager.TryGetComponent(user, out ActionsComponent? comp))
return false;
if (!_actionsSystem.TryGetActionData(actionId, out var baseAction) ||
baseAction is not BaseTargetActionComponent action)
{
return false;
}
// Is the action currently valid?
if (!action.Enabled
|| action.Charges is 0
@@ -196,19 +201,19 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
switch (action)
{
case WorldTargetAction mapTarget:
return TryTargetWorld(args, mapTarget, user, comp) || !action.InteractOnMiss;
case WorldTargetActionComponent mapTarget:
return TryTargetWorld(args, actionId, mapTarget, user, comp) || !mapTarget.InteractOnMiss;
case EntityTargetAction entTarget:
return TryTargetEntity(args, entTarget, user, comp) || !action.InteractOnMiss;
case EntityTargetActionComponent entTarget:
return TryTargetEntity(args, actionId, entTarget, user, comp) || !entTarget.InteractOnMiss;
default:
Logger.Error($"Unknown targeting action: {action.GetType()}");
Logger.Error($"Unknown targeting action: {actionId.GetType()}");
return false;
}
}
private bool TryTargetWorld(in PointerInputCmdArgs args, WorldTargetAction action, EntityUid user, ActionsComponent actionComp)
private bool TryTargetWorld(in PointerInputCmdArgs args, EntityUid actionId, WorldTargetActionComponent action, EntityUid user, ActionsComponent actionComp)
{
if (_actionsSystem == null)
return false;
@@ -232,10 +237,10 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
action.Event.Performer = user;
}
_actionsSystem.PerformAction(user, actionComp, action, action.Event, _timing.CurTime);
_actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime);
}
else
EntityManager.RaisePredictiveEvent(new RequestPerformActionEvent(action, coords));
EntityManager.RaisePredictiveEvent(new RequestPerformActionEvent(actionId, coords));
if (!action.Repeat)
StopTargeting();
@@ -243,7 +248,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
return true;
}
private bool TryTargetEntity(in PointerInputCmdArgs args, EntityTargetAction action, EntityUid user, ActionsComponent actionComp)
private bool TryTargetEntity(in PointerInputCmdArgs args, EntityUid actionId, EntityTargetActionComponent action, EntityUid user, ActionsComponent actionComp)
{
if (_actionsSystem == null)
return false;
@@ -264,10 +269,10 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
action.Event.Performer = user;
}
_actionsSystem.PerformAction(user, actionComp, action, action.Event, _timing.CurTime);
_actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime);
}
else
EntityManager.RaisePredictiveEvent(new RequestPerformActionEvent(action, args.EntityUid));
EntityManager.RaisePredictiveEvent(new RequestPerformActionEvent(actionId, args.EntityUid));
if (!action.Repeat)
StopTargeting();
@@ -322,13 +327,17 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
private void TriggerAction(int index)
{
if (CurrentPage[index] is not { } type)
if (_actionsSystem == null ||
CurrentPage[index] is not { } actionId ||
!_actionsSystem.TryGetActionData(actionId, out var baseAction))
{
return;
}
if (type is TargetedAction action)
ToggleTargeting(action);
if (baseAction is BaseTargetActionComponent action)
ToggleTargeting(actionId, action);
else
_actionsSystem?.TriggerAction(type);
_actionsSystem?.TriggerAction(actionId, baseAction);
}
private void ChangePage(int index)
@@ -360,14 +369,14 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
ChangePage(_currentPageIndex + 1);
}
private void AppendAction(ActionType action)
private void AppendAction(EntityUid action)
{
if (_container == null)
return;
foreach (var button in _container.GetButtons())
{
if (button.Action != null)
if (button.ActionId != null)
continue;
SetAction(button, action);
@@ -388,39 +397,45 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
}
}
private void OnActionAdded(ActionType action)
private void OnActionAdded(EntityUid actionId)
{
if (_actionsSystem == null ||
!_actionsSystem.TryGetActionData(actionId, out var action))
{
return;
}
// if the action is toggled when we add it, start targeting
if (action is TargetedAction targetAction && action.Toggled)
StartTargeting(targetAction);
if (action is BaseTargetActionComponent targetAction && action.Toggled)
StartTargeting(actionId, targetAction);
foreach (var page in _pages)
{
for (var i = 0; i < page.Size; i++)
{
if (page[i] == action)
if (page[i] == actionId)
{
return;
}
}
}
AppendAction(action);
AppendAction(actionId);
SearchAndDisplay();
}
private void OnActionRemoved(ActionType action)
private void OnActionRemoved(EntityUid actionId)
{
if (_container == null)
return;
// stop targeting if the action is removed
if (action == SelectingTargetFor)
if (actionId == SelectingTargetFor)
StopTargeting();
foreach (var button in _container.GetButtons())
{
if (button.Action == action)
if (button.ActionId == actionId)
{
SetAction(button, null);
}
@@ -430,7 +445,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
{
for (var i = 0; i < page.Size; i++)
{
if (page[i] == action)
if (page[i] == actionId)
{
page[i] = null;
}
@@ -440,15 +455,15 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
SearchAndDisplay();
}
private void OnActionReplaced(ActionType existing, ActionType action)
private void OnActionReplaced(EntityUid actionId)
{
if (_container == null)
return;
foreach (var button in _container.GetButtons())
{
if (button.Action == existing)
button.UpdateData(action);
if (button.ActionId == actionId)
button.UpdateData(actionId);
}
}
@@ -499,15 +514,15 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
}
}
private bool MatchesFilter(ActionType action, Filters filter)
private bool MatchesFilter(BaseActionComponent action, Filters filter)
{
return filter switch
{
Filters.Enabled => action.Enabled,
Filters.Item => action.Provider != null && action.Provider != _playerManager.LocalPlayer?.ControlledEntity,
Filters.Innate => action.Provider == null || action.Provider == _playerManager.LocalPlayer?.ControlledEntity,
Filters.Instant => action is InstantAction,
Filters.Targeted => action is TargetedAction,
Filters.Instant => action is InstantActionComponent,
Filters.Targeted => action is BaseTargetActionComponent,
_ => throw new ArgumentOutOfRangeException(nameof(filter), filter, null)
};
}
@@ -518,7 +533,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
_window.ResultsGrid.RemoveAllChildren();
}
private void PopulateActions(IEnumerable<ActionType> actions)
private void PopulateActions(IEnumerable<(EntityUid Id, BaseActionComponent Comp)> actions)
{
if (_window == null)
return;
@@ -529,7 +544,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
{
var button = new ActionButton {Locked = true};
button.UpdateData(action);
button.UpdateData(action.Id);
button.ActionPressed += OnWindowActionPressed;
button.ActionUnpressed += OnWindowActionUnPressed;
button.ActionFocusExited += OnWindowActionFocusExisted;
@@ -538,16 +553,14 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
}
}
private void SearchAndDisplay(ActionsComponent? component = null)
private void SearchAndDisplay()
{
if (_window == null)
if (_window is not { Disposed: false } || _actionsSystem == null)
return;
var search = _window.SearchBar.Text;
var filters = _window.FilterButton.SelectedKeys;
IEnumerable<ActionType>? actions = (component ?? _actionsSystem?.PlayerActions)?.Actions;
actions ??= Array.Empty<ActionType>();
var actions = _actionsSystem.GetClientActions();
if (filters.Count == 0 && string.IsNullOrWhiteSpace(search))
{
@@ -557,45 +570,46 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
actions = actions.Where(action =>
{
if (filters.Count > 0 && filters.Any(filter => !MatchesFilter(action, filter)))
if (filters.Count > 0 && filters.Any(filter => !MatchesFilter(action.Comp, filter)))
return false;
if (action.Keywords.Any(keyword => search.Contains(keyword, StringComparison.OrdinalIgnoreCase)))
if (action.Comp.Keywords.Any(keyword => search.Contains(keyword, StringComparison.OrdinalIgnoreCase)))
return true;
if (action.DisplayName.Contains(search, StringComparison.OrdinalIgnoreCase))
var name = EntityManager.GetComponent<MetaDataComponent>(action.Id).EntityName;
if (name.Contains(search, StringComparison.OrdinalIgnoreCase))
return true;
if (action.Provider == null || action.Provider == _playerManager.LocalPlayer?.ControlledEntity)
if (action.Comp.Provider == null || action.Comp.Provider == _playerManager.LocalPlayer?.ControlledEntity)
return false;
var name = EntityManager.GetComponent<MetaDataComponent>(action.Provider.Value).EntityName;
return name.Contains(search, StringComparison.OrdinalIgnoreCase);
var providerName = EntityManager.GetComponent<MetaDataComponent>(action.Comp.Provider.Value).EntityName;
return providerName.Contains(search, StringComparison.OrdinalIgnoreCase);
});
PopulateActions(actions);
}
private void SetAction(ActionButton button, ActionType? type)
private void SetAction(ActionButton button, EntityUid? actionId)
{
int position;
if (type == null)
if (actionId == null)
{
button.ClearData();
if (_container?.TryGetButtonIndex(button, out position) ?? false)
{
CurrentPage[position] = type;
CurrentPage[position] = actionId;
}
return;
}
if (button.TryReplaceWith(type) &&
if (button.TryReplaceWith(actionId.Value) &&
_container != null &&
_container.TryGetButtonIndex(button, out position))
{
CurrentPage[position] = type;
CurrentPage[position] = actionId;
}
}
@@ -603,7 +617,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
{
if (UIManager.CurrentlyHovered is ActionButton button)
{
if (!_menuDragHelper.IsDragging || _menuDragHelper.Dragged?.Action is not { } type)
if (!_menuDragHelper.IsDragging || _menuDragHelper.Dragged?.ActionId is not { } type)
{
_menuDragHelper.EndDrag();
return;
@@ -669,7 +683,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
{
if (args.Function == EngineKeyFunctions.UIClick)
{
if (button.Action == null)
if (button.ActionId == null)
{
var ev = new FillActionSlotEvent();
EntityManager.EventBus.RaiseEvent(EventSource.Local, ev);
@@ -692,25 +706,28 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
private void OnActionUnpressed(GUIBoundKeyEventArgs args, ActionButton button)
{
if (args.Function != EngineKeyFunctions.UIClick)
if (args.Function != EngineKeyFunctions.UIClick || _actionsSystem == null)
return;
if (UIManager.CurrentlyHovered == button)
{
_menuDragHelper.EndDrag();
if (button.Action is TargetedAction action)
if (_actionsSystem.TryGetActionData(button.ActionId, out var baseAction))
{
// for target actions, we go into "select target" mode, we don't
// message the server until we actually pick our target.
if (baseAction is BaseTargetActionComponent action)
{
// for target actions, we go into "select target" mode, we don't
// message the server until we actually pick our target.
// if we're clicking the same thing we're already targeting for, then we simply cancel
// targeting
ToggleTargeting(action);
return;
// if we're clicking the same thing we're already targeting for, then we simply cancel
// targeting
ToggleTargeting(button.ActionId.Value, action);
return;
}
_actionsSystem?.TriggerAction(button.ActionId.Value, baseAction);
}
_actionsSystem?.TriggerAction(button.Action);
}
else
{
@@ -722,7 +739,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
private bool OnMenuBeginDrag()
{
if (_menuDragHelper.Dragged?.Action is { } action)
if (_actionsSystem != null && _actionsSystem.TryGetActionData(_menuDragHelper.Dragged?.ActionId, out var action))
{
if (action.EntityIcon != null)
{
@@ -828,7 +845,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
{
foreach (ref var assignment in CollectionsMarshal.AsSpan(assignments))
{
_pages[assignment.Hotbar][assignment.Slot] = assignment.Action;
_pages[assignment.Hotbar][assignment.Slot] = assignment.ActionId;
}
_container?.SetActionData(_pages[_currentPageIndex]);
@@ -864,7 +881,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
{
LoadDefaultActions(component);
_container?.SetActionData(_pages[DefaultPageIndex]);
SearchAndDisplay(component);
SearchAndDisplay();
}
private void OnComponentUnlinked()
@@ -876,7 +893,10 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
private void LoadDefaultActions(ActionsComponent component)
{
var actions = component.Actions.Where(actionType => actionType.AutoPopulate).ToList();
if (_actionsSystem == null)
return;
var actions = _actionsSystem.GetClientActions().Where(action => action.Comp.AutoPopulate).ToList();
var offset = 0;
var totalPages = _pages.Count;
@@ -892,7 +912,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
var actionIndex = slot + offset;
if (actionIndex < actions.Count)
{
page[slot] = actions[slot + offset];
page[slot] = actions[slot + offset].Id;
}
else
{
@@ -916,26 +936,26 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
/// If currently targeting with no slot or a different slot, switches to
/// targeting with the specified slot.
/// </summary>
private void ToggleTargeting(TargetedAction action)
private void ToggleTargeting(EntityUid actionId, BaseTargetActionComponent action)
{
if (SelectingTargetFor == action)
if (SelectingTargetFor == actionId)
{
StopTargeting();
return;
}
StartTargeting(action);
StartTargeting(actionId, action);
}
/// <summary>
/// Puts us in targeting mode, where we need to pick either a target point or entity
/// </summary>
private void StartTargeting(TargetedAction action)
private void StartTargeting(EntityUid actionId, BaseTargetActionComponent action)
{
// If we were targeting something else we should stop
StopTargeting();
SelectingTargetFor = action;
SelectingTargetFor = actionId;
// override "held-item" overlay
if (action.TargetingIndicator && _overlays.TryGetOverlay<ShowHandItemOverlay>(out var handOverlay))
@@ -955,7 +975,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
// - Add a yes/no checkmark where the HandItemOverlay usually is
// Highlight valid entity targets
if (action is not EntityTargetAction entityAction)
if (action is not EntityTargetActionComponent entityAction)
return;
Func<EntityUid, bool>? predicate = null;
@@ -992,20 +1012,20 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
//TODO: Serialize this shit
private sealed class ActionPage
{
private readonly ActionType?[] _data;
private readonly EntityUid?[] _data;
public ActionPage(int size)
{
_data = new ActionType?[size];
_data = new EntityUid?[size];
}
public ActionType? this[int index]
public EntityUid? this[int index]
{
get => _data[index];
set => _data[index] = value;
}
public static implicit operator ActionType?[](ActionPage p)
public static implicit operator EntityUid?[](ActionPage p)
{
return p._data.ToArray();
}