using System.IO;
using System.Linq;
-using Content.Client.Popups;
using Content.Shared.Actions;
using Content.Shared.Actions.ActionTypes;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Player;
-using Robust.Shared.Audio;
using Robust.Shared.ContentPack;
using Robust.Shared.GameStates;
using Robust.Shared.Input.Binding;
-using Robust.Shared.Player;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Markdown;
using Robust.Shared.Serialization.Markdown.Mapping;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IResourceManager _resources = default!;
[Dependency] private readonly ISerializationManager _serialization = default!;
- [Dependency] private readonly SharedAudioSystem _audio = default!;
-
- [Dependency] private readonly PopupSystem _popupSystem = default!;
public event Action<ActionType>? ActionAdded;
public event Action<ActionType>? ActionRemoved;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers;
using Robust.Client.UserInterface.Controls;
-using Robust.Client.Utility;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.Timing;
public sealed class ActionUIController : UIController, IOnStateChanged<GameplayState>, IOnSystemChanged<ActionsSystem>
{
- [Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IOverlayManager _overlays = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[UISystemDependency] private readonly ActionsSystem? _actionsSystem = default;
[UISystemDependency] private readonly InteractionOutlineSystem? _interactionOutline = default;
[UISystemDependency] private readonly TargetOutlineSystem? _targetOutline = default;
+ [UISystemDependency] private readonly SpriteSystem _spriteSystem = default!;
private const int DefaultPageIndex = 0;
private ActionButtonContainer? _container;
/// <summary>
/// Action slot we are currently selecting a target for.
/// </summary>
- public TargetedAction? SelectingTargetFor { get; private set; } = null;
+ public TargetedAction? SelectingTargetFor { get; private set; }
public ActionUIController()
{
if (!_timing.IsFirstTimePredicted || _actionsSystem == null || SelectingTargetFor is not { } action)
return false;
- if (_playerManager.LocalPlayer?.ControlledEntity is not EntityUid user)
+ if (_playerManager.LocalPlayer?.ControlledEntity is not { } user)
return false;
- if (!_entities.TryGetComponent(user, out ActionsComponent? comp))
+ if (!EntityManager.TryGetComponent(user, out ActionsComponent? comp))
return false;
// Is the action currently valid?
if (!action.Enabled
- || action.Charges != null && action.Charges == 0
+ || action.Charges is 0
|| action.Cooldown.HasValue && action.Cooldown.Value.End > _timing.CurTime)
{
// The user is targeting with this action, but it is not valid. Maybe mark this click as
_actionsSystem.PerformAction(user, actionComp, action, action.Event, _timing.CurTime);
}
else
- _entities.RaisePredictiveEvent(new RequestPerformActionEvent(action, coords));
+ EntityManager.RaisePredictiveEvent(new RequestPerformActionEvent(action, coords));
if (!action.Repeat)
StopTargeting();
_actionsSystem.PerformAction(user, actionComp, action, action.Event, _timing.CurTime);
}
else
- _entities.RaisePredictiveEvent(new RequestPerformActionEvent(action, args.EntityUid));
+ EntityManager.RaisePredictiveEvent(new RequestPerformActionEvent(action, args.EntityUid));
if (!action.Repeat)
StopTargeting();
private void OnActionAdded(ActionType action)
{
+ // if the action is toggled when we add it, start targetting
+ if (action is TargetedAction targetAction && action.Toggled)
+ StartTargeting(targetAction);
+
foreach (var page in _pages)
{
for (var i = 0; i < page.Size; i++)
if (_container == null)
return;
+ // stop targeting if the action is removed
+ if (action == SelectingTargetFor)
+ StopTargeting();
+
foreach (var button in _container.GetButtons())
{
if (button.Action == action)
return filter switch
{
Filters.Enabled => action.Enabled,
- Filters.Item => action.Provider != null && action.Provider != _actionsSystem?.PlayerActions?.Owner,
- Filters.Innate => action.Provider == null || action.Provider == _actionsSystem?.PlayerActions?.Owner,
+ 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,
_ => throw new ArgumentOutOfRangeException(nameof(filter), filter, null)
if (action.DisplayName.Contains(search, StringComparison.OrdinalIgnoreCase))
return true;
- if (action.Provider == null || action.Provider == _actionsSystem?.PlayerActions?.Owner)
+ if (action.Provider == null || action.Provider == _playerManager.LocalPlayer?.ControlledEntity)
return false;
- var name = _entities.GetComponent<MetaDataComponent>(action.Provider.Value).EntityName;
+ var name = EntityManager.GetComponent<MetaDataComponent>(action.Provider.Value).EntityName;
return name.Contains(search, StringComparison.OrdinalIgnoreCase);
});
{
if (action.EntityIcon != null)
{
- _dragShadow.Texture = _entities.GetComponent<SpriteComponent>(action.EntityIcon.Value).Icon?
+ _dragShadow.Texture = EntityManager.GetComponent<SpriteComponent>(action.EntityIcon.Value).Icon?
.GetFrame(RSI.State.Direction.South, 0);
}
else if (action.Icon != null)
{
- _dragShadow.Texture = action.Icon!.Frame0();
+ _dragShadow.Texture = _spriteSystem.Frame0(action.Icon);
}
else
{
_dragShadow.Visible = false;
}
- public void ReloadActionContainer()
- {
- UnloadGui();
- LoadGui();
- }
-
- public void UnloadGui()
+ private void UnloadGui()
{
_actionsSystem?.UnlinkAllActions();
ActionsBar.PageButtons.RightArrow.OnPressed -= OnRightArrowPressed;
}
- public void LoadGui()
+ private void LoadGui()
{
if (ActionsBar == null)
{
_container.ActionUnpressed += OnActionUnpressed;
}
- public void ClearActions()
+ private void ClearActions()
{
_container?.ClearActionData();
}
/// If currently targeting with no slot or a different slot, switches to
/// targeting with the specified slot.
/// </summary>
- public void ToggleTargeting(TargetedAction action)
+ private void ToggleTargeting(TargetedAction action)
{
if (SelectingTargetFor == action)
{
handOverlay.EntityOverride = action.Provider;
}
else if (action.Toggled && action.IconOn != null)
- handOverlay.IconOverride = action.IconOn.Frame0();
+ handOverlay.IconOverride = _spriteSystem.Frame0(action.IconOn);
else if (action.Icon != null)
- handOverlay.IconOverride = action.Icon.Frame0();
+ handOverlay.IconOverride = _spriteSystem.Frame0(action.Icon);
}
// TODO: allow world-targets to check valid positions. E.g., maybe:
/// <summary>
/// Switch out of targeting mode if currently selecting target for an action
/// </summary>
- public void StopTargeting()
+ private void StopTargeting()
{
if (SelectingTargetFor == null)
return;
using Content.Shared.Actions.ActionTypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
-namespace Content.Server.Magic;
+namespace Content.Server.Magic.Components;
/// <summary>
/// Spellbooks for having an entity learn spells as long as they've read the book and it's in their hand.
/// The three fields below is just used for initialization.
/// </summary>
[DataField("worldSpells", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<int, WorldTargetActionPrototype>))]
+ [ViewVariables(VVAccess.ReadWrite)]
public readonly Dictionary<string, int> WorldSpells = new();
[DataField("entitySpells", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<int, EntityTargetActionPrototype>))]
+ [ViewVariables(VVAccess.ReadWrite)]
public readonly Dictionary<string, int> EntitySpells = new();
[DataField("instantSpells", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<int, InstantActionPrototype>))]
+ [ViewVariables(VVAccess.ReadWrite)]
public readonly Dictionary<string, int> InstantSpells = new();
[DataField("learnTime")]
+ [ViewVariables(VVAccess.ReadWrite)]
public float LearnTime = .75f;
+
+ /// <summary>
+ /// If true, the spell action stays even after the book is removed
+ /// </summary>
+ [DataField("learnPermanently")]
+ [ViewVariables(VVAccess.ReadWrite)]
+ public bool LearnPermanently;
}
using Content.Server.Chat.Systems;
using Content.Server.Coordinates.Helpers;
using Content.Server.Doors.Systems;
+using Content.Server.Magic.Components;
using Content.Server.Magic.Events;
using Content.Server.Weapons.Ranged.Systems;
using Content.Shared.Actions;
if (args.Handled || args.Cancelled)
return;
- _actionsSystem.AddActions(args.Args.User, component.Spells, uid);
+ _actionsSystem.AddActions(args.Args.User, component.Spells, component.LearnPermanently ? null : uid);
args.Handled = true;
}
{
// If applicable, this ensures the projectile is parented to grid on spawn, instead of the map.
var mapPos = pos.ToMap(EntityManager);
- EntityCoordinates spawnCoords = _mapManager.TryFindGridAt(mapPos, out var grid)
+ var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var grid)
? pos.WithEntityId(grid.Owner, EntityManager)
: new(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position);
var ent = Spawn(ev.Prototype, spawnCoords);
- _gunSystem.ShootProjectile(ent, ev.Target.Position - mapPos.Position, userVelocity, ev.Performer);
+ var direction = ev.Target.ToMapPos(EntityManager, _transformSystem) -
+ spawnCoords.ToMapPos(EntityManager, _transformSystem);
+ _gunSystem.ShootProjectile(ent, direction, userVelocity, ev.Performer);
}
}
public sealed class ActionsComponent : Component
{
[ViewVariables]
- [Access(typeof(SharedActionsSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
+ [Access(typeof(SharedActionsSystem), Other = AccessPermissions.ReadExecute)]
+ // FIXME Friends
public SortedSet<ActionType> Actions = new();
public override bool SendOnlyToOwner => true;
using Content.Shared.Hands;
using Content.Shared.Interaction;
using Content.Shared.Inventory.Events;
-using Content.Shared.Popups;
-using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
-using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using System.Linq;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!;
- [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
public override void Initialize()
{
/// </summary>
private void OnActionRequest(RequestPerformActionEvent ev, EntitySessionEventArgs args)
{
- if (args.SenderSession.AttachedEntity is not EntityUid user)
+ if (args.SenderSession.AttachedEntity is not { } user)
return;
if (!TryComp(user, out ActionsComponent? component))
return;
}
- _rotateToFaceSystem.TryFaceCoordinates(user, Transform(entityTarget).WorldPosition);
+ var targetWorldPos = _transformSystem.GetWorldPosition(entityTarget);
+ _rotateToFaceSystem.TryFaceCoordinates(user, targetWorldPos);
if (!ValidateEntityTarget(user, entityTarget, entityAction))
return;
case WorldTargetAction worldAction:
- if (ev.EntityCoordinatesTarget is not EntityCoordinates entityCoordinatesTarget)
+ if (ev.EntityCoordinatesTarget is not { } entityCoordinatesTarget)
{
Logger.Error($"Attempted to perform a world-targeted action without a target! Action: {worldAction.DisplayName}");
return;
if (action.Range <= 0)
return true;
- return (xform.WorldPosition - targetXform.WorldPosition).Length <= action.Range;
+ var distance = (_transformSystem.GetWorldPosition(xform) - _transformSystem.GetWorldPosition(targetXform)).Length;
+ return distance <= action.Range;
}
if (_interactionSystem.InRangeUnobstructed(user, target, range: action.Range)
if (action.Range <= 0)
return true;
- return coords.InRange(EntityManager, Transform(user).Coordinates, action.Range);
+ return coords.InRange(EntityManager, _transformSystem, Transform(user).Coordinates, action.Range);
}
return _interactionSystem.InRangeUnobstructed(user, coords, range: action.Range);
{
comp ??= EnsureComp<ActionsComponent>(uid);
- bool allClientExclusive = true;
+ var allClientExclusive = true;
foreach (var action in actions)
{
private void OnDidEquip(EntityUid uid, ActionsComponent component, DidEquipEvent args)
{
var ev = new GetItemActionsEvent(args.SlotFlags);
- RaiseLocalEvent(args.Equipment, ev, false);
+ RaiseLocalEvent(args.Equipment, ev);
if (ev.Actions.Count == 0)
return;
private void OnHandEquipped(EntityUid uid, ActionsComponent component, DidEquipHandEvent args)
{
var ev = new GetItemActionsEvent();
- RaiseLocalEvent(args.Equipped, ev, false);
+ RaiseLocalEvent(args.Equipped, ev);
if (ev.Actions.Count == 0)
return;
FlashRune: -1
ExplosionRune: -1
IgniteRune: -1
- StunRune: -1
\ No newline at end of file
+ StunRune: -1