Actions System + UI (#2710)

Co-authored-by: Vera Aguilera Puerto <6766154+Zumorica@users.noreply.github.com>
This commit is contained in:
chairbender
2020-12-13 14:28:20 -08:00
committed by GitHub
parent fd0df9a00a
commit 7a3c281f60
150 changed files with 7283 additions and 854 deletions

View File

@@ -100,7 +100,7 @@ namespace Content.Server.GameObjects.Components.Atmos
return;
}
status?.ShowAlert(AlertType.Fire, onClickAlert: OnClickAlert);
status?.ShowAlert(AlertType.Fire);
if (FireStacks > 0)
{
@@ -152,14 +152,6 @@ namespace Content.Server.GameObjects.Components.Atmos
}
}
private void OnClickAlert(ClickAlertEventArgs args)
{
if (args.Player.TryGetComponent(out FlammableComponent flammable))
{
flammable.Resist();
}
}
public void CollideWith(IEntity collidedWith)
{
if (!collidedWith.TryGetComponent(out FlammableComponent otherFlammable))

View File

@@ -5,18 +5,22 @@ using Content.Server.Explosions;
using Content.Server.GameObjects.Components.Body.Respiratory;
using Content.Server.Interfaces;
using Content.Server.Utility;
using Content.Shared.Actions;
using Content.Shared.Atmos;
using Content.Shared.Audio;
using Content.Shared.GameObjects.Components.Atmos.GasTank;
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Verbs;
using Content.Shared.Interfaces.GameObjects.Components;
using JetBrains.Annotations;
using Robust.Server.GameObjects.Components.UserInterface;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.ComponentDependencies;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Localization;
@@ -30,13 +34,15 @@ namespace Content.Server.GameObjects.Components.Atmos
[ComponentReference(typeof(IActivate))]
public class GasTankComponent : SharedGasTankComponent, IExamine, IGasMixtureHolder, IUse, IDropped, IActivate
{
private const float MaxExplosionRange = 14f;
private const float MaxExplosionRange = 14f;
private const float DefaultOutputPressure = Atmospherics.OneAtmosphere;
private float _pressureResistance;
private int _integrity = 3;
[ComponentDependency] private readonly ItemActionsComponent? _itemActions = null;
[ViewVariables] private BoundUserInterface? _userInterface;
[ViewVariables] public GasMixture? Air { get; set; }
@@ -191,14 +197,18 @@ namespace Content.Server.GameObjects.Components.Atmos
private void UpdateUserInterface(bool initialUpdate = false)
{
var internals = GetInternalsComponent();
_userInterface?.SetState(
new GasTankBoundUserInterfaceState
{
TankPressure = Air?.Pressure ?? 0,
OutputPressure = initialUpdate ? OutputPressure : (float?) null,
InternalsConnected = IsConnected,
CanConnectInternals = IsFunctional && GetInternalsComponent() != null
CanConnectInternals = IsFunctional && internals != null
});
if (internals == null) return;
_itemActions?.GrantOrUpdate(ItemActionType.ToggleInternals, IsFunctional, IsConnected);
}
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage message)
@@ -214,8 +224,9 @@ namespace Content.Server.GameObjects.Components.Atmos
}
}
private void ToggleInternals()
internal void ToggleInternals()
{
if (!ActionBlockerSystem.CanUse(GetInternalsComponent()?.Owner)) return;
if (IsConnected)
{
DisconnectFromInternals();
@@ -311,6 +322,11 @@ namespace Content.Server.GameObjects.Components.Atmos
_integrity++;
}
public void Dropped(DroppedEventArgs eventArgs)
{
DisconnectFromInternals(eventArgs.User);
}
/// <summary>
/// Open interaction window
/// </summary>
@@ -341,10 +357,21 @@ namespace Content.Server.GameObjects.Components.Atmos
component.OpenInterface(actor.playerSession);
}
}
}
public void Dropped(DroppedEventArgs eventArgs)
[UsedImplicitly]
public class ToggleInternalsAction : IToggleItemAction
{
public void ExposeData(ObjectSerializer serializer) {}
public bool DoToggleAction(ToggleItemActionEventArgs args)
{
DisconnectFromInternals(eventArgs.User);
if (!args.Item.TryGetComponent<GasTankComponent>(out var gasTankComponent)) return false;
// no change
if (gasTankComponent.IsConnected == args.ToggledOn) return false;
gasTankComponent.ToggleInternals();
// did we successfully toggle to the desired status?
return gasTankComponent.IsConnected == args.ToggledOn;
}
}
}

View File

@@ -108,8 +108,7 @@ namespace Content.Server.GameObjects.Components.Buckle
if (Buckled)
{
_serverAlertsComponent.ShowAlert(BuckledTo != null ? BuckledTo.BuckledAlertType : AlertType.Buckled,
onClickAlert: OnClickAlert);
_serverAlertsComponent.ShowAlert(BuckledTo?.BuckledAlertType ?? AlertType.Buckled);
}
else
{
@@ -117,14 +116,6 @@ namespace Content.Server.GameObjects.Components.Buckle
}
}
private void OnClickAlert(ClickAlertEventArgs args)
{
if (args.Player.TryGetComponent(out BuckleComponent? buckle))
{
buckle.TryUnbuckle(args.Player);
}
}
/// <summary>
/// Reattaches this entity to the strap, modifying its position and rotation.

View File

@@ -30,6 +30,7 @@ namespace Content.Server.GameObjects.Components.GUI
[RegisterComponent]
[ComponentReference(typeof(IHandsComponent))]
[ComponentReference(typeof(ISharedHandsComponent))]
[ComponentReference(typeof(SharedHandsComponent))]
public class HandsComponent : SharedHandsComponent, IHandsComponent, IBodyPartAdded, IBodyPartRemoved
{
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
@@ -82,7 +83,7 @@ namespace Content.Server.GameObjects.Components.GUI
}
}
public bool IsHolding(IEntity entity)
public override bool IsHolding(IEntity entity)
{
foreach (var hand in _hands)
{
@@ -165,6 +166,7 @@ namespace Content.Server.GameObjects.Components.GUI
}
Dirty();
var success = hand.Container.Insert(item.Owner);
if (success)
{
@@ -172,6 +174,9 @@ namespace Content.Server.GameObjects.Components.GUI
OnItemChanged?.Invoke();
}
_entitySystemManager.GetEntitySystem<InteractionSystem>().EquippedHandInteraction(Owner, item.Owner,
ToSharedHand(hand));
_entitySystemManager.GetEntitySystem<InteractionSystem>().HandSelectedInteraction(Owner, item.Owner);
return success;
@@ -266,6 +271,9 @@ namespace Content.Server.GameObjects.Components.GUI
return false;
}
_entitySystemManager.GetEntitySystem<InteractionSystem>().UnequippedHandInteraction(Owner, item.Owner,
ToSharedHand(hand));
if (doDropInteraction && !DroppedInteraction(item, false))
return false;
@@ -288,6 +296,61 @@ namespace Content.Server.GameObjects.Components.GUI
return true;
}
public bool Drop(string slot, BaseContainer targetContainer, bool doMobChecks = true, bool doDropInteraction = true)
{
if (slot == null)
{
throw new ArgumentNullException(nameof(slot));
}
if (targetContainer == null)
{
throw new ArgumentNullException(nameof(targetContainer));
}
var hand = GetHand(slot);
if (!CanDrop(slot, doMobChecks) || hand?.Entity == null)
{
return false;
}
if (!hand.Container.CanRemove(hand.Entity))
{
return false;
}
if (!targetContainer.CanInsert(hand.Entity))
{
return false;
}
var item = hand.Entity.GetComponent<ItemComponent>();
if (!hand.Container.Remove(hand.Entity))
{
throw new InvalidOperationException();
}
_entitySystemManager.GetEntitySystem<InteractionSystem>().UnequippedHandInteraction(Owner, item.Owner,
ToSharedHand(hand));
if (doDropInteraction && !DroppedInteraction(item, doMobChecks))
return false;
item.RemovedFromSlot();
if (!targetContainer.Insert(item.Owner))
{
throw new InvalidOperationException();
}
OnItemChanged?.Invoke();
Dirty();
return true;
}
public bool Drop(IEntity entity, EntityCoordinates coords, bool doMobChecks = true, bool doDropInteraction = true)
{
if (entity == null)
@@ -323,57 +386,6 @@ namespace Content.Server.GameObjects.Components.GUI
return Drop(slot, Owner.Transform.Coordinates, mobChecks, doDropInteraction);
}
public bool Drop(string slot, BaseContainer targetContainer, bool doMobChecks = true, bool doDropInteraction = true)
{
if (slot == null)
{
throw new ArgumentNullException(nameof(slot));
}
if (targetContainer == null)
{
throw new ArgumentNullException(nameof(targetContainer));
}
var hand = GetHand(slot);
if (!CanDrop(slot, doMobChecks) || hand?.Entity == null)
{
return false;
}
if (!hand.Container.CanRemove(hand.Entity))
{
return false;
}
if (!targetContainer.CanInsert(hand.Entity))
{
return false;
}
var item = hand.Entity.GetComponent<ItemComponent>();
if (!hand.Container.Remove(hand.Entity))
{
throw new InvalidOperationException();
}
if (doDropInteraction && !DroppedInteraction(item, doMobChecks))
return false;
item.RemovedFromSlot();
if (!targetContainer.Insert(item.Owner))
{
throw new InvalidOperationException();
}
OnItemChanged?.Invoke();
Dirty();
return true;
}
public bool Drop(IEntity entity, BaseContainer targetContainer, bool doMobChecks = true, bool doDropInteraction = true)
{
if (entity == null)
@@ -463,19 +475,28 @@ namespace Content.Server.GameObjects.Components.GUI
for (var i = 0; i < _hands.Count; i++)
{
var location = i == 0
? HandLocation.Right
: i == _hands.Count - 1
? HandLocation.Left
: HandLocation.Middle;
var hand = _hands[i].ToShared(i, location);
var hand = _hands[i].ToShared(i, IndexToHandLocation(i));
hands[i] = hand;
}
return new HandsComponentState(hands, ActiveHand);
}
private HandLocation IndexToHandLocation(int index)
{
return index == 0
? HandLocation.Right
: index == _hands.Count - 1
? HandLocation.Left
: HandLocation.Middle;
}
private SharedHand ToSharedHand(Hand hand)
{
var index = _hands.IndexOf(hand);
return hand.ToShared(index, IndexToHandLocation(index));
}
public void SwapHands()
{
if (ActiveHand == null)

View File

@@ -25,6 +25,7 @@ using static Content.Shared.GameObjects.Components.Inventory.SharedInventoryComp
namespace Content.Server.GameObjects.Components.GUI
{
[RegisterComponent]
[ComponentReference(typeof(SharedInventoryComponent))]
public class InventoryComponent : SharedInventoryComponent, IExAct, IEffectBlocker, IPressureProtection
{
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
@@ -572,5 +573,20 @@ namespace Content.Server.GameObjects.Components.GUI
}
}
}
public override bool IsEquipped(IEntity item)
{
if (item == null) return false;
foreach (var containerSlot in _slotContainers.Values)
{
// we don't want a recursive check here
if (containerSlot.Contains(item))
{
return true;
}
}
return false;
}
}
}

View File

@@ -1,18 +1,26 @@
#nullable enable
using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Atmos;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Clothing;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Power;
using Content.Shared.Actions;
using Content.Server.GameObjects.Components.Weapon.Ranged.Barrels;
using Content.Shared.GameObjects.Components;
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Verbs;
using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Utility;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.ComponentDependencies;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Localization;
@@ -45,6 +53,8 @@ namespace Content.Server.GameObjects.Components.Interactable
[ViewVariables(VVAccess.ReadWrite)] public string? TurnOnFailSound;
[ViewVariables(VVAccess.ReadWrite)] public string? TurnOffSound;
[ComponentDependency] private readonly ItemActionsComponent? _itemActions = null;
/// <summary>
/// Client-side ItemStatus level
/// </summary>
@@ -98,8 +108,9 @@ namespace Content.Server.GameObjects.Components.Interactable
/// Illuminates the light if it is not active, extinguishes it if it is active.
/// </summary>
/// <returns>True if the light's status was toggled, false otherwise.</returns>
private bool ToggleStatus(IEntity user)
public bool ToggleStatus(IEntity user)
{
if (!ActionBlockerSystem.CanUse(user)) return false;
return Activated ? TurnOff() : TurnOn(user);
}
@@ -112,6 +123,7 @@ namespace Content.Server.GameObjects.Components.Interactable
SetState(false);
Activated = false;
UpdateLightAction();
if (makeNoise)
{
@@ -132,6 +144,7 @@ namespace Content.Server.GameObjects.Components.Interactable
{
if (TurnOnFailSound != null) EntitySystem.Get<AudioSystem>().PlayFromEntity(TurnOnFailSound, Owner);
Owner.PopupMessage(user, Loc.GetString("Cell missing..."));
UpdateLightAction();
return false;
}
@@ -142,10 +155,12 @@ namespace Content.Server.GameObjects.Components.Interactable
{
if (TurnOnFailSound != null) EntitySystem.Get<AudioSystem>().PlayFromEntity(TurnOnFailSound, Owner);
Owner.PopupMessage(user, Loc.GetString("Dead cell..."));
UpdateLightAction();
return false;
}
Activated = true;
UpdateLightAction();
SetState(true);
if (TurnOnSound != null) EntitySystem.Get<AudioSystem>().PlayFromEntity(TurnOnSound, Owner);
@@ -175,6 +190,11 @@ namespace Content.Server.GameObjects.Components.Interactable
}
}
private void UpdateLightAction()
{
_itemActions?.Toggle(ItemActionType.ToggleLight, Activated);
}
public void OnUpdate(float frameTime)
{
if (Cell == null)
@@ -249,4 +269,17 @@ namespace Content.Server.GameObjects.Components.Interactable
}
}
}
[UsedImplicitly]
public class ToggleLightAction : IToggleItemAction
{
public void ExposeData(ObjectSerializer serializer) {}
public bool DoToggleAction(ToggleItemActionEventArgs args)
{
if (!args.Item.TryGetComponent<HandheldLightComponent>(out var lightComponent)) return false;
if (lightComponent.Activated == args.ToggledOn) return false;
return lightComponent.ToggleStatus(args.Performer);
}
}
}

View File

@@ -0,0 +1,35 @@
using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
namespace Content.Server.GameObjects.Components.Items
{
/// <summary>
/// Pops up a message when equipped / unequipped (including hands).
/// For debugging purposes.
/// </summary>
[RegisterComponent]
public class DebugEquipComponent : Component, IEquipped, IEquippedHand, IUnequipped, IUnequippedHand
{
public override string Name => "DebugEquip";
public void Equipped(EquippedEventArgs eventArgs)
{
eventArgs.User.PopupMessage("equipped " + Owner.Name);
}
public void EquippedHand(EquippedHandEventArgs eventArgs)
{
eventArgs.User.PopupMessage("equipped hand " + Owner.Name);
}
public void Unequipped(UnequippedEventArgs eventArgs)
{
eventArgs.User.PopupMessage("unequipped " + Owner.Name);
}
public void UnequippedHand(UnequippedHandEventArgs eventArgs)
{
eventArgs.User.PopupMessage("unequipped hand" + Owner.Name);
}
}
}

View File

@@ -0,0 +1,199 @@
#nullable enable
using System;
using Content.Shared.Actions;
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Players;
namespace Content.Server.GameObjects.Components.Mobs
{
[RegisterComponent]
[ComponentReference(typeof(SharedActionsComponent))]
public sealed class ServerActionsComponent : SharedActionsComponent
{
[Dependency] private readonly IServerEntityManager _entityManager = default!;
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null)
{
base.HandleNetworkMessage(message, netChannel, session);
if (message is not BasePerformActionMessage performActionMessage) return;
if (session == null)
{
throw new ArgumentNullException(nameof(session));
}
var player = session.AttachedEntity;
if (player != Owner) return;
var attempt = ActionAttempt(performActionMessage, session);
if (attempt == null) return;
if (!attempt.TryGetActionState(this, out var actionState) || !actionState.Enabled)
{
Logger.DebugS("action", "user {0} attempted to use" +
" action {1} which is not granted to them", player.Name,
attempt);
return;
}
if (actionState.IsOnCooldown(GameTiming))
{
Logger.DebugS("action", "user {0} attempted to use" +
" action {1} which is on cooldown", player.Name,
attempt);
return;
}
switch (performActionMessage.BehaviorType)
{
case BehaviorType.Instant:
attempt.DoInstantAction(player);
break;
case BehaviorType.Toggle:
if (performActionMessage is not IToggleActionMessage toggleMsg) return;
if (toggleMsg.ToggleOn == actionState.ToggledOn)
{
Logger.DebugS("action", "user {0} attempted to" +
" toggle action {1} to {2}, but it is already toggled {2}", player.Name,
attempt.Action.Name, toggleMsg.ToggleOn);
return;
}
if (attempt.DoToggleAction(player, toggleMsg.ToggleOn))
{
attempt.ToggleAction(this, toggleMsg.ToggleOn);
}
else
{
// if client predicted the toggle will work, need to reset
// that prediction
Dirty();
}
break;
case BehaviorType.TargetPoint:
if (performActionMessage is not ITargetPointActionMessage targetPointMsg) return;
if (!CheckRangeAndSetFacing(targetPointMsg.Target, player)) return;
attempt.DoTargetPointAction(player, targetPointMsg.Target);
break;
case BehaviorType.TargetEntity:
if (performActionMessage is not ITargetEntityActionMessage targetEntityMsg) return;
if (!EntityManager.TryGetEntity(targetEntityMsg.Target, out var entity))
{
Logger.DebugS("action", "user {0} attempted to" +
" perform target entity action {1} but could not find entity with " +
"provided uid {2}", player.Name, attempt.Action.Name,
targetEntityMsg.Target);
return;
}
if (!CheckRangeAndSetFacing(entity.Transform.Coordinates, player)) return;
attempt.DoTargetEntityAction(player, entity);
break;
case BehaviorType.None:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private IActionAttempt? ActionAttempt(BasePerformActionMessage message, ICommonSession session)
{
IActionAttempt? attempt;
switch (message)
{
case PerformActionMessage performActionMessage:
if (!ActionManager.TryGet(performActionMessage.ActionType, out var action))
{
Logger.DebugS("action", "user {0} attempted to perform" +
" unrecognized action {1}", session.AttachedEntity,
performActionMessage.ActionType);
return null;
}
attempt = new ActionAttempt(action);
break;
case PerformItemActionMessage performItemActionMessage:
if (!ActionManager.TryGet(performItemActionMessage.ActionType, out var itemAction))
{
Logger.DebugS("action", "user {0} attempted to perform" +
" unrecognized item action {1}",
session.AttachedEntity, performItemActionMessage.ActionType);
return null;
}
if (!EntityManager.TryGetEntity(performItemActionMessage.Item, out var item))
{
Logger.DebugS("action", "user {0} attempted to perform" +
" item action {1} for unknown item {2}",
session.AttachedEntity, performItemActionMessage.ActionType, performItemActionMessage.Item);
return null;
}
if (!item.TryGetComponent<ItemActionsComponent>(out var actionsComponent))
{
Logger.DebugS("action", "user {0} attempted to perform" +
" item action {1} for item {2} which has no ItemActionsComponent",
session.AttachedEntity, performItemActionMessage.ActionType, item);
return null;
}
if (actionsComponent.Holder != session.AttachedEntity)
{
Logger.DebugS("action", "user {0} attempted to perform" +
" item action {1} for item {2} which they are not holding",
session.AttachedEntity, performItemActionMessage.ActionType, item);
return null;
}
attempt = new ItemActionAttempt(itemAction, item, actionsComponent);
break;
default:
return null;
}
if (message.BehaviorType != attempt.Action.BehaviorType)
{
Logger.DebugS("action", "user {0} attempted to" +
" perform action {1} as a {2} behavior, but this action is actually a" +
" {3} behavior", session.AttachedEntity, attempt, message.BehaviorType,
attempt.Action.BehaviorType);
return null;
}
return attempt;
}
private bool CheckRangeAndSetFacing(EntityCoordinates target, IEntity player)
{
// ensure it's within their clickable range
var targetWorldPos = target.ToMapPos(EntityManager);
var rangeBox = new Box2(player.Transform.WorldPosition, player.Transform.WorldPosition)
.Enlarged(_entityManager.MaxUpdateRange);
if (!rangeBox.Contains(targetWorldPos))
{
Logger.DebugS("action", "user {0} attempted to" +
" perform target action further than allowed range",
player.Name);
return false;
}
if (!ActionBlockerSystem.CanChangeDirection(player)) return true;
// don't set facing unless they clicked far enough away
var diff = targetWorldPos - player.Transform.WorldPosition;
if (diff.LengthSquared > 0.01f)
{
player.Transform.LocalRotation = new Angle(diff);
}
return true;
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Alert;
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
@@ -42,11 +43,6 @@ namespace Content.Server.GameObjects.Components.Mobs
base.OnRemove();
}
public override ComponentState GetComponentState()
{
return new AlertsComponentState(CreateAlertStatesArray());
}
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession session = null)
{
base.HandleNetworkMessage(message, netChannel, session);
@@ -67,14 +63,21 @@ namespace Content.Server.GameObjects.Components.Mobs
break;
}
// TODO: Implement clicking other status effects in the HUD
if (AlertManager.TryDecode(msg.EncodedAlert, out var alert))
if (!IsShowingAlert(msg.AlertType))
{
PerformAlertClickCallback(alert, player);
Logger.DebugS("alert", "user {0} attempted to" +
" click alert {1} which is not currently showing for them",
player.Name, msg.AlertType);
break;
}
if (AlertManager.TryGet(msg.AlertType, out var alert))
{
alert.OnClick.AlertClicked(new ClickAlertEventArgs(player, alert));
}
else
{
Logger.WarningS("alert", "unrecognized encoded alert {0}", msg.EncodedAlert);
Logger.WarningS("alert", "unrecognized encoded alert {0}", msg.AlertType);
}
break;

View File

@@ -145,15 +145,7 @@ namespace Content.Server.GameObjects.Components.Movement
mind.Mind.Visit(Owner);
_controller = entity;
status.ShowAlert(_pilotingAlertType, onClickAlert: OnClickAlert);
}
private void OnClickAlert(ClickAlertEventArgs args)
{
if (args.Player.TryGetComponent(out ShuttleControllerComponent? controller))
{
controller.RemoveController();
}
status.ShowAlert(_pilotingAlertType);
}
/// <summary>