switch (action)
{
case WorldTargetActionComponent mapTarget:
- return TryTargetWorld(args, actionId, mapTarget, user, comp) || !mapTarget.InteractOnMiss;
+ return TryTargetWorld(args, actionId, mapTarget, user, comp) || !mapTarget.InteractOnMiss;
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:
Logger.Error($"Unknown targeting action: {actionId.GetType()}");
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()
{
if (ActionButton == null)
SubscribeLocalEvent<InstantActionComponent, MapInitEvent>(OnActionMapInit);
SubscribeLocalEvent<EntityTargetActionComponent, MapInitEvent>(OnActionMapInit);
SubscribeLocalEvent<WorldTargetActionComponent, MapInitEvent>(OnActionMapInit);
+ SubscribeLocalEvent<EntityWorldTargetActionComponent, MapInitEvent>(OnActionMapInit);
SubscribeLocalEvent<InstantActionComponent, ComponentShutdown>(OnActionShutdown);
SubscribeLocalEvent<EntityTargetActionComponent, ComponentShutdown>(OnActionShutdown);
SubscribeLocalEvent<WorldTargetActionComponent, ComponentShutdown>(OnActionShutdown);
+ SubscribeLocalEvent<EntityWorldTargetActionComponent, ComponentShutdown>(OnActionShutdown);
SubscribeLocalEvent<ActionsComponent, DidEquipEvent>(OnDidEquip);
SubscribeLocalEvent<ActionsComponent, DidEquipHandEvent>(OnHandEquipped);
SubscribeLocalEvent<InstantActionComponent, ComponentGetState>(OnInstantGetState);
SubscribeLocalEvent<EntityTargetActionComponent, ComponentGetState>(OnEntityTargetGetState);
SubscribeLocalEvent<WorldTargetActionComponent, ComponentGetState>(OnWorldTargetGetState);
+ SubscribeLocalEvent<EntityWorldTargetActionComponent, ComponentGetState>(OnEntityWorldTargetGetState);
SubscribeLocalEvent<InstantActionComponent, GetActionDataEvent>(OnGetActionData);
SubscribeLocalEvent<EntityTargetActionComponent, GetActionDataEvent>(OnGetActionData);
SubscribeLocalEvent<WorldTargetActionComponent, GetActionDataEvent>(OnGetActionData);
+ SubscribeLocalEvent<EntityWorldTargetActionComponent, GetActionDataEvent>(OnGetActionData);
SubscribeAllEvent<RequestPerformActionEvent>(OnActionRequest);
}
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
{
args.Action = component;
}
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:
if (action.CheckCanInteract && !_actionBlockerSystem.CanInteract(user, null))
return;
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;
var ev = new ValidateActionEntityTargetEvent(user, target);
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;
- if (_whitelistSystem.IsWhitelistFail(action.Whitelist, target))
+ if (_whitelistSystem.IsWhitelistFail(whitelist, target))
return false;
- if (action.CheckCanInteract && !_actionBlockerSystem.CanInteract(user, target))
+ if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target))
return false;
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.
var xform = Transform(user);
if (xform.MapID != targetXform.MapID)
return false;
- if (action.Range <= 0)
+ if (range <= 0)
return true;
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)
{
- if (!ValidateWorldTargetBase(user, coords, action))
+ var comp = action.Comp;
+ if (!ValidateWorldTargetBase(user, coords, comp.CheckCanInteract, comp.CheckCanAccess, comp.Range))
return false;
var ev = new ValidateActionWorldTargetEvent(user, coords);
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;
+
+ if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, null))
return false;
- if (!action.CheckCanAccess)
+ if (!checkCanAccess)
{
// even if we don't check for obstructions, we may still need to check the range.
var xform = Transform(user);
if (xform.MapID != coords.GetMapId(EntityManager))
return false;
- if (action.Range <= 0)
+ if (range <= 0)
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)