Add EntityWorldTargetAction (#29819)
* Add EntityWorldTargetAction initial implementation * Update obsolete methods * Partially working EntityWorldTargetAction * Fix entity selection * Move and clean up AfterInteract * Fix building new walls * Readd no entity or coordinates error * Consolidate action validation code * Add summaries to component --------- Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com>
This commit is contained in:
@@ -48,6 +48,7 @@ namespace Content.Client.Actions
|
|||||||
SubscribeLocalEvent<InstantActionComponent, ComponentHandleState>(OnInstantHandleState);
|
SubscribeLocalEvent<InstantActionComponent, ComponentHandleState>(OnInstantHandleState);
|
||||||
SubscribeLocalEvent<EntityTargetActionComponent, ComponentHandleState>(OnEntityTargetHandleState);
|
SubscribeLocalEvent<EntityTargetActionComponent, ComponentHandleState>(OnEntityTargetHandleState);
|
||||||
SubscribeLocalEvent<WorldTargetActionComponent, ComponentHandleState>(OnWorldTargetHandleState);
|
SubscribeLocalEvent<WorldTargetActionComponent, ComponentHandleState>(OnWorldTargetHandleState);
|
||||||
|
SubscribeLocalEvent<EntityWorldTargetActionComponent, ComponentHandleState>(OnEntityWorldTargetHandleState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnInstantHandleState(EntityUid uid, InstantActionComponent component, ref ComponentHandleState args)
|
private void OnInstantHandleState(EntityUid uid, InstantActionComponent component, ref ComponentHandleState args)
|
||||||
@@ -76,6 +77,18 @@ namespace Content.Client.Actions
|
|||||||
BaseHandleState<WorldTargetActionComponent>(uid, component, state);
|
BaseHandleState<WorldTargetActionComponent>(uid, component, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnEntityWorldTargetHandleState(EntityUid uid,
|
||||||
|
EntityWorldTargetActionComponent component,
|
||||||
|
ref ComponentHandleState args)
|
||||||
|
{
|
||||||
|
if (args.Current is not EntityWorldTargetActionComponentState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
component.Whitelist = state.Whitelist;
|
||||||
|
component.CanTargetSelf = state.CanTargetSelf;
|
||||||
|
BaseHandleState<EntityWorldTargetActionComponent>(uid, component, state);
|
||||||
|
}
|
||||||
|
|
||||||
private void BaseHandleState<T>(EntityUid uid, BaseActionComponent component, BaseActionComponentState state) where T : BaseActionComponent
|
private void BaseHandleState<T>(EntityUid uid, BaseActionComponent component, BaseActionComponentState state) where T : BaseActionComponent
|
||||||
{
|
{
|
||||||
// TODO ACTIONS use auto comp states
|
// TODO ACTIONS use auto comp states
|
||||||
|
|||||||
@@ -189,6 +189,9 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||||||
case EntityTargetActionComponent entTarget:
|
case EntityTargetActionComponent entTarget:
|
||||||
return TryTargetEntity(args, actionId, entTarget, user, comp) || !entTarget.InteractOnMiss;
|
return TryTargetEntity(args, actionId, entTarget, user, comp) || !entTarget.InteractOnMiss;
|
||||||
|
|
||||||
|
case EntityWorldTargetActionComponent entMapTarget:
|
||||||
|
return TryTargetEntityWorld(args, actionId, entMapTarget, user, comp) || !entMapTarget.InteractOnMiss;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Logger.Error($"Unknown targeting action: {actionId.GetType()}");
|
Logger.Error($"Unknown targeting action: {actionId.GetType()}");
|
||||||
return false;
|
return false;
|
||||||
@@ -266,6 +269,47 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool TryTargetEntityWorld(in PointerInputCmdArgs args,
|
||||||
|
EntityUid actionId,
|
||||||
|
EntityWorldTargetActionComponent action,
|
||||||
|
EntityUid user,
|
||||||
|
ActionsComponent actionComp)
|
||||||
|
{
|
||||||
|
if (_actionsSystem == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var entity = args.EntityUid;
|
||||||
|
var coords = args.Coordinates;
|
||||||
|
|
||||||
|
if (!_actionsSystem.ValidateEntityWorldTarget(user, entity, coords, (actionId, action)))
|
||||||
|
{
|
||||||
|
if (action.DeselectOnMiss)
|
||||||
|
StopTargeting();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.ClientExclusive)
|
||||||
|
{
|
||||||
|
if (action.Event != null)
|
||||||
|
{
|
||||||
|
action.Event.Entity = entity;
|
||||||
|
action.Event.Coords = coords;
|
||||||
|
action.Event.Performer = user;
|
||||||
|
action.Event.Action = actionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
_actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
EntityManager.RaisePredictiveEvent(new RequestPerformActionEvent(EntityManager.GetNetEntity(actionId), EntityManager.GetNetEntity(args.EntityUid), EntityManager.GetNetCoordinates(coords)));
|
||||||
|
|
||||||
|
if (!action.Repeat)
|
||||||
|
StopTargeting();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public void UnloadButton()
|
public void UnloadButton()
|
||||||
{
|
{
|
||||||
if (ActionButton == null)
|
if (ActionButton == null)
|
||||||
|
|||||||
@@ -105,6 +105,31 @@ public sealed class ActionOnInteractSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Then EntityWorld target actions
|
||||||
|
var entWorldOptions = GetValidActions<EntityWorldTargetActionComponent>(actionEnts, args.CanReach);
|
||||||
|
for (var i = entWorldOptions.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var action = entWorldOptions[i];
|
||||||
|
if (!_actions.ValidateEntityWorldTarget(args.User, args.Target, args.ClickLocation, action))
|
||||||
|
entWorldOptions.RemoveAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entWorldOptions.Count > 0)
|
||||||
|
{
|
||||||
|
var (entActId, entAct) = _random.Pick(entWorldOptions);
|
||||||
|
if (entAct.Event != null)
|
||||||
|
{
|
||||||
|
entAct.Event.Performer = args.User;
|
||||||
|
entAct.Event.Action = entActId;
|
||||||
|
entAct.Event.Entity = args.Target;
|
||||||
|
entAct.Event.Coords = args.ClickLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
_actions.PerformAction(args.User, null, entActId, entAct, entAct.Event, _timing.CurTime, false);
|
||||||
|
args.Handled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// else: try world target actions
|
// else: try world target actions
|
||||||
var options = GetValidActions<WorldTargetActionComponent>(component.ActionEntities, args.CanReach);
|
var options = GetValidActions<WorldTargetActionComponent>(component.ActionEntities, args.CanReach);
|
||||||
for (var i = options.Count - 1; i >= 0; i--)
|
for (var i = options.Count - 1; i >= 0; i--)
|
||||||
|
|||||||
@@ -101,6 +101,13 @@ public sealed class RequestPerformActionEvent : EntityEventArgs
|
|||||||
Action = action;
|
Action = action;
|
||||||
EntityCoordinatesTarget = entityCoordinatesTarget;
|
EntityCoordinatesTarget = entityCoordinatesTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RequestPerformActionEvent(NetEntity action, NetEntity entityTarget, NetCoordinates entityCoordinatesTarget)
|
||||||
|
{
|
||||||
|
Action = action;
|
||||||
|
EntityTarget = entityTarget;
|
||||||
|
EntityCoordinatesTarget = entityCoordinatesTarget;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -144,6 +151,27 @@ public abstract partial class WorldTargetActionEvent : BaseActionEvent
|
|||||||
public EntityCoordinates Target;
|
public EntityCoordinates Target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is the type of event that gets raised when an <see cref="EntityWorldTargetActionComponent"/> is performed.
|
||||||
|
/// The <see cref="BaseActionEvent.Performer"/>, <see cref="Entity"/>, and <see cref="Coords"/>
|
||||||
|
/// fields will automatically be filled out by the <see cref="SharedActionsSystem"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// To define a new action for some system, you need to create an event that inherits from this class.
|
||||||
|
/// </remarks>
|
||||||
|
public abstract partial class EntityWorldTargetActionEvent : BaseActionEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The entity that the user targeted.
|
||||||
|
/// </summary>
|
||||||
|
public EntityUid? Entity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The coordinates of the location that the user targeted.
|
||||||
|
/// </summary>
|
||||||
|
public EntityCoordinates? Coords;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base class for events that are raised when an action gets performed. This should not generally be used outside of the action
|
/// Base class for events that are raised when an action gets performed. This should not generally be used outside of the action
|
||||||
/// system.
|
/// system.
|
||||||
|
|||||||
42
Content.Shared/Actions/EntityWorldTargetActionComponent.cs
Normal file
42
Content.Shared/Actions/EntityWorldTargetActionComponent.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using Content.Shared.Whitelist;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Actions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used on action entities to define an action that triggers when targeting an entity or entity coordinates.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class EntityWorldTargetActionComponent : BaseTargetActionComponent
|
||||||
|
{
|
||||||
|
public override BaseActionEvent? BaseEvent => Event;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local-event to raise when this action is performed.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
[NonSerialized]
|
||||||
|
public EntityWorldTargetActionEvent? Event;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines which entities are valid targets for this action.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>No whitelist check when null.</remarks>
|
||||||
|
[DataField] public EntityWhitelist? Whitelist;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this action considers the user as a valid target entity when using this action.
|
||||||
|
/// </summary>
|
||||||
|
[DataField] public bool CanTargetSelf = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class EntityWorldTargetActionComponentState(
|
||||||
|
EntityWorldTargetActionComponent component,
|
||||||
|
IEntityManager entManager)
|
||||||
|
: BaseActionComponentState(component, entManager)
|
||||||
|
{
|
||||||
|
public EntityWhitelist? Whitelist = component.Whitelist;
|
||||||
|
public bool CanTargetSelf = component.CanTargetSelf;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
|
namespace Content.Shared.Actions.Events;
|
||||||
|
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct ValidateActionEntityWorldTargetEvent(
|
||||||
|
EntityUid User,
|
||||||
|
EntityUid? Target,
|
||||||
|
EntityCoordinates? Coords,
|
||||||
|
bool Cancelled = false);
|
||||||
@@ -38,10 +38,12 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<InstantActionComponent, MapInitEvent>(OnActionMapInit);
|
SubscribeLocalEvent<InstantActionComponent, MapInitEvent>(OnActionMapInit);
|
||||||
SubscribeLocalEvent<EntityTargetActionComponent, MapInitEvent>(OnActionMapInit);
|
SubscribeLocalEvent<EntityTargetActionComponent, MapInitEvent>(OnActionMapInit);
|
||||||
SubscribeLocalEvent<WorldTargetActionComponent, MapInitEvent>(OnActionMapInit);
|
SubscribeLocalEvent<WorldTargetActionComponent, MapInitEvent>(OnActionMapInit);
|
||||||
|
SubscribeLocalEvent<EntityWorldTargetActionComponent, MapInitEvent>(OnActionMapInit);
|
||||||
|
|
||||||
SubscribeLocalEvent<InstantActionComponent, ComponentShutdown>(OnActionShutdown);
|
SubscribeLocalEvent<InstantActionComponent, ComponentShutdown>(OnActionShutdown);
|
||||||
SubscribeLocalEvent<EntityTargetActionComponent, ComponentShutdown>(OnActionShutdown);
|
SubscribeLocalEvent<EntityTargetActionComponent, ComponentShutdown>(OnActionShutdown);
|
||||||
SubscribeLocalEvent<WorldTargetActionComponent, ComponentShutdown>(OnActionShutdown);
|
SubscribeLocalEvent<WorldTargetActionComponent, ComponentShutdown>(OnActionShutdown);
|
||||||
|
SubscribeLocalEvent<EntityWorldTargetActionComponent, ComponentShutdown>(OnActionShutdown);
|
||||||
|
|
||||||
SubscribeLocalEvent<ActionsComponent, DidEquipEvent>(OnDidEquip);
|
SubscribeLocalEvent<ActionsComponent, DidEquipEvent>(OnDidEquip);
|
||||||
SubscribeLocalEvent<ActionsComponent, DidEquipHandEvent>(OnHandEquipped);
|
SubscribeLocalEvent<ActionsComponent, DidEquipHandEvent>(OnHandEquipped);
|
||||||
@@ -56,10 +58,12 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<InstantActionComponent, ComponentGetState>(OnInstantGetState);
|
SubscribeLocalEvent<InstantActionComponent, ComponentGetState>(OnInstantGetState);
|
||||||
SubscribeLocalEvent<EntityTargetActionComponent, ComponentGetState>(OnEntityTargetGetState);
|
SubscribeLocalEvent<EntityTargetActionComponent, ComponentGetState>(OnEntityTargetGetState);
|
||||||
SubscribeLocalEvent<WorldTargetActionComponent, ComponentGetState>(OnWorldTargetGetState);
|
SubscribeLocalEvent<WorldTargetActionComponent, ComponentGetState>(OnWorldTargetGetState);
|
||||||
|
SubscribeLocalEvent<EntityWorldTargetActionComponent, ComponentGetState>(OnEntityWorldTargetGetState);
|
||||||
|
|
||||||
SubscribeLocalEvent<InstantActionComponent, GetActionDataEvent>(OnGetActionData);
|
SubscribeLocalEvent<InstantActionComponent, GetActionDataEvent>(OnGetActionData);
|
||||||
SubscribeLocalEvent<EntityTargetActionComponent, GetActionDataEvent>(OnGetActionData);
|
SubscribeLocalEvent<EntityTargetActionComponent, GetActionDataEvent>(OnGetActionData);
|
||||||
SubscribeLocalEvent<WorldTargetActionComponent, GetActionDataEvent>(OnGetActionData);
|
SubscribeLocalEvent<WorldTargetActionComponent, GetActionDataEvent>(OnGetActionData);
|
||||||
|
SubscribeLocalEvent<EntityWorldTargetActionComponent, GetActionDataEvent>(OnGetActionData);
|
||||||
|
|
||||||
SubscribeAllEvent<RequestPerformActionEvent>(OnActionRequest);
|
SubscribeAllEvent<RequestPerformActionEvent>(OnActionRequest);
|
||||||
}
|
}
|
||||||
@@ -102,6 +106,11 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
args.State = new WorldTargetActionComponentState(component, EntityManager);
|
args.State = new WorldTargetActionComponentState(component, EntityManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnEntityWorldTargetGetState(EntityUid uid, EntityWorldTargetActionComponent component, ref ComponentGetState args)
|
||||||
|
{
|
||||||
|
args.State = new EntityWorldTargetActionComponentState(component, EntityManager);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnGetActionData<T>(EntityUid uid, T component, ref GetActionDataEvent args) where T : BaseActionComponent
|
private void OnGetActionData<T>(EntityUid uid, T component, ref GetActionDataEvent args) where T : BaseActionComponent
|
||||||
{
|
{
|
||||||
args.Action = component;
|
args.Action = component;
|
||||||
@@ -442,6 +451,34 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
case EntityWorldTargetActionComponent entityWorldAction:
|
||||||
|
{
|
||||||
|
var actionEntity = GetEntity(ev.EntityTarget);
|
||||||
|
var actionCoords = GetCoordinates(ev.EntityCoordinatesTarget);
|
||||||
|
|
||||||
|
if (actionEntity is null && actionCoords is null)
|
||||||
|
{
|
||||||
|
Log.Error($"Attempted to perform an entity-world-targeted action without an entity or world coordinates! Action: {name}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var entWorldAction = new Entity<EntityWorldTargetActionComponent>(actionEnt, entityWorldAction);
|
||||||
|
|
||||||
|
if (!ValidateEntityWorldTarget(user, actionEntity, actionCoords, entWorldAction))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_adminLogger.Add(LogType.Action,
|
||||||
|
$"{ToPrettyString(user):user} is performing the {name:action} action (provided by {ToPrettyString(action.Container ?? user):provider}) targeted at {ToPrettyString(actionEntity):target} {actionCoords:target}.");
|
||||||
|
|
||||||
|
if (entityWorldAction.Event != null)
|
||||||
|
{
|
||||||
|
entityWorldAction.Event.Entity = actionEntity;
|
||||||
|
entityWorldAction.Event.Coords = actionCoords;
|
||||||
|
Dirty(actionEnt, entityWorldAction);
|
||||||
|
performEvent = entityWorldAction.Event;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case InstantActionComponent instantAction:
|
case InstantActionComponent instantAction:
|
||||||
if (action.CheckCanInteract && !_actionBlockerSystem.CanInteract(user, null))
|
if (action.CheckCanInteract && !_actionBlockerSystem.CanInteract(user, null))
|
||||||
return;
|
return;
|
||||||
@@ -465,7 +502,14 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
|
|
||||||
public bool ValidateEntityTarget(EntityUid user, EntityUid target, Entity<EntityTargetActionComponent> actionEnt)
|
public bool ValidateEntityTarget(EntityUid user, EntityUid target, Entity<EntityTargetActionComponent> actionEnt)
|
||||||
{
|
{
|
||||||
if (!ValidateEntityTargetBase(user, target, actionEnt))
|
var comp = actionEnt.Comp;
|
||||||
|
if (!ValidateEntityTargetBase(user,
|
||||||
|
target,
|
||||||
|
comp.Whitelist,
|
||||||
|
comp.CheckCanInteract,
|
||||||
|
comp.CanTargetSelf,
|
||||||
|
comp.CheckCanAccess,
|
||||||
|
comp.Range))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var ev = new ValidateActionEntityTargetEvent(user, target);
|
var ev = new ValidateActionEntityTargetEvent(user, target);
|
||||||
@@ -473,21 +517,27 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
return !ev.Cancelled;
|
return !ev.Cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ValidateEntityTargetBase(EntityUid user, EntityUid target, EntityTargetActionComponent action)
|
private bool ValidateEntityTargetBase(EntityUid user,
|
||||||
|
EntityUid? targetEntity,
|
||||||
|
EntityWhitelist? whitelist,
|
||||||
|
bool checkCanInteract,
|
||||||
|
bool canTargetSelf,
|
||||||
|
bool checkCanAccess,
|
||||||
|
float range)
|
||||||
{
|
{
|
||||||
if (!target.IsValid() || Deleted(target))
|
if (targetEntity is not { } target || !target.IsValid() || Deleted(target))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (_whitelistSystem.IsWhitelistFail(action.Whitelist, target))
|
if (_whitelistSystem.IsWhitelistFail(whitelist, target))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (action.CheckCanInteract && !_actionBlockerSystem.CanInteract(user, target))
|
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (user == target)
|
if (user == target)
|
||||||
return action.CanTargetSelf;
|
return canTargetSelf;
|
||||||
|
|
||||||
if (!action.CheckCanAccess)
|
if (!checkCanAccess)
|
||||||
{
|
{
|
||||||
// even if we don't check for obstructions, we may still need to check the range.
|
// even if we don't check for obstructions, we may still need to check the range.
|
||||||
var xform = Transform(user);
|
var xform = Transform(user);
|
||||||
@@ -496,19 +546,20 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
if (xform.MapID != targetXform.MapID)
|
if (xform.MapID != targetXform.MapID)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (action.Range <= 0)
|
if (range <= 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var distance = (_transformSystem.GetWorldPosition(xform) - _transformSystem.GetWorldPosition(targetXform)).Length();
|
var distance = (_transformSystem.GetWorldPosition(xform) - _transformSystem.GetWorldPosition(targetXform)).Length();
|
||||||
return distance <= action.Range;
|
return distance <= range;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _interactionSystem.InRangeAndAccessible(user, target, range: action.Range);
|
return _interactionSystem.InRangeAndAccessible(user, target, range: range);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ValidateWorldTarget(EntityUid user, EntityCoordinates coords, Entity<WorldTargetActionComponent> action)
|
public bool ValidateWorldTarget(EntityUid user, EntityCoordinates coords, Entity<WorldTargetActionComponent> action)
|
||||||
{
|
{
|
||||||
if (!ValidateWorldTargetBase(user, coords, action))
|
var comp = action.Comp;
|
||||||
|
if (!ValidateWorldTargetBase(user, coords, comp.CheckCanInteract, comp.CheckCanAccess, comp.Range))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var ev = new ValidateActionWorldTargetEvent(user, coords);
|
var ev = new ValidateActionWorldTargetEvent(user, coords);
|
||||||
@@ -516,12 +567,19 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
return !ev.Cancelled;
|
return !ev.Cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ValidateWorldTargetBase(EntityUid user, EntityCoordinates coords, WorldTargetActionComponent action)
|
private bool ValidateWorldTargetBase(EntityUid user,
|
||||||
|
EntityCoordinates? entityCoordinates,
|
||||||
|
bool checkCanInteract,
|
||||||
|
bool checkCanAccess,
|
||||||
|
float range)
|
||||||
{
|
{
|
||||||
if (action.CheckCanInteract && !_actionBlockerSystem.CanInteract(user, null))
|
if (entityCoordinates is not { } coords)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!action.CheckCanAccess)
|
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, null))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!checkCanAccess)
|
||||||
{
|
{
|
||||||
// even if we don't check for obstructions, we may still need to check the range.
|
// even if we don't check for obstructions, we may still need to check the range.
|
||||||
var xform = Transform(user);
|
var xform = Transform(user);
|
||||||
@@ -529,13 +587,40 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
if (xform.MapID != coords.GetMapId(EntityManager))
|
if (xform.MapID != coords.GetMapId(EntityManager))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (action.Range <= 0)
|
if (range <= 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return _transformSystem.InRange(coords, Transform(user).Coordinates, action.Range);
|
return coords.InRange(EntityManager, _transformSystem, Transform(user).Coordinates, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _interactionSystem.InRangeUnobstructed(user, coords, range: action.Range);
|
return _interactionSystem.InRangeUnobstructed(user, coords, range: range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ValidateEntityWorldTarget(EntityUid user,
|
||||||
|
EntityUid? entity,
|
||||||
|
EntityCoordinates? coords,
|
||||||
|
Entity<EntityWorldTargetActionComponent> action)
|
||||||
|
{
|
||||||
|
var comp = action.Comp;
|
||||||
|
var entityValidated = ValidateEntityTargetBase(user,
|
||||||
|
entity,
|
||||||
|
comp.Whitelist,
|
||||||
|
comp.CheckCanInteract,
|
||||||
|
comp.CanTargetSelf,
|
||||||
|
comp.CheckCanAccess,
|
||||||
|
comp.Range);
|
||||||
|
|
||||||
|
var worldValidated
|
||||||
|
= ValidateWorldTargetBase(user, coords, comp.CheckCanInteract, comp.CheckCanAccess, comp.Range);
|
||||||
|
|
||||||
|
if (!entityValidated && !worldValidated)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var ev = new ValidateActionEntityWorldTargetEvent(user,
|
||||||
|
entityValidated ? entity : null,
|
||||||
|
worldValidated ? coords : null);
|
||||||
|
RaiseLocalEvent(action, ref ev);
|
||||||
|
return !ev.Cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PerformAction(EntityUid performer, ActionsComponent? component, EntityUid actionId, BaseActionComponent action, BaseActionEvent? actionEvent, TimeSpan curTime, bool predicted = true)
|
public void PerformAction(EntityUid performer, ActionsComponent? component, EntityUid actionId, BaseActionComponent action, BaseActionEvent? actionEvent, TimeSpan curTime, bool predicted = true)
|
||||||
|
|||||||
Reference in New Issue
Block a user