Decouple interactions from hands, cleanup old events, add new fears (#28393)

* ok basic shit

* second part

* pretend it isn't real it can't hurt you.

* 👁️ 👁️

* shadowcommander review
This commit is contained in:
Nemanja
2024-05-31 16:26:19 -04:00
committed by GitHub
parent c4291920a6
commit a1a8f04036
72 changed files with 312 additions and 165 deletions

View File

@@ -76,6 +76,7 @@ namespace Content.Shared.Interaction
private EntityQuery<WallMountComponent> _wallMountQuery;
private EntityQuery<UseDelayComponent> _delayQuery;
private EntityQuery<ActivatableUIComponent> _uiQuery;
private EntityQuery<ComplexInteractionComponent> _complexInteractionQuery;
private const CollisionGroup InRangeUnobstructedMask = CollisionGroup.Impassable | CollisionGroup.InteractImpassable;
@@ -98,6 +99,7 @@ namespace Content.Shared.Interaction
_wallMountQuery = GetEntityQuery<WallMountComponent>();
_delayQuery = GetEntityQuery<UseDelayComponent>();
_uiQuery = GetEntityQuery<ActivatableUIComponent>();
_complexInteractionQuery = GetEntityQuery<ComplexInteractionComponent>();
SubscribeLocalEvent<BoundUserInterfaceCheckRangeEvent>(HandleUserInterfaceRangeCheck);
SubscribeLocalEvent<BoundUserInterfaceMessageAttempt>(OnBoundInterfaceInteractAttempt);
@@ -360,8 +362,13 @@ namespace Content.Shared.Interaction
// TODO this needs to be handled better. This probably bypasses many complex can-interact checks in weird roundabout ways.
if (_actionBlockerSystem.CanInteract(user, target))
{
UserInteraction(relay.RelayEntity.Value, coordinates, target, altInteract, checkCanInteract,
checkAccess, checkCanUse);
UserInteraction(relay.RelayEntity.Value,
coordinates,
target,
altInteract,
checkCanInteract,
checkAccess,
checkCanUse);
return;
}
}
@@ -398,25 +405,10 @@ namespace Content.Shared.Interaction
? !checkAccess || InRangeUnobstructed(user, coordinates)
: !checkAccess || InRangeUnobstructed(user, target.Value); // permits interactions with wall mounted entities
// Does the user have hands?
if (!_handsQuery.TryComp(user, out var hands) || hands.ActiveHand == null)
{
var ev = new InteractNoHandEvent(user, target, coordinates);
RaiseLocalEvent(user, ev);
if (target != null)
{
var interactedEv = new InteractedNoHandEvent(target.Value, user, coordinates);
RaiseLocalEvent(target.Value, interactedEv);
DoContactInteraction(user, target.Value, ev);
}
return;
}
// empty-hand interactions
// combat mode hand interactions will always be true here -- since
// they check this earlier before returning in
if (hands.ActiveHandEntity is not { } held)
if (!TryGetUsedEntity(user, out var used, checkCanUse))
{
if (inRangeUnobstructed && target != null)
InteractHand(user, target.Value);
@@ -424,11 +416,7 @@ namespace Content.Shared.Interaction
return;
}
// Can the user use the held entity?
if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user))
return;
if (target == held)
if (target == used)
{
UseInHandInteraction(user, target.Value, checkCanUse: false, checkCanInteract: false);
return;
@@ -438,7 +426,7 @@ namespace Content.Shared.Interaction
{
InteractUsing(
user,
held,
used.Value,
target.Value,
coordinates,
checkCanInteract: false,
@@ -449,7 +437,7 @@ namespace Content.Shared.Interaction
InteractUsingRanged(
user,
held,
used.Value,
target,
coordinates,
inRangeUnobstructed);
@@ -457,6 +445,18 @@ namespace Content.Shared.Interaction
public void InteractHand(EntityUid user, EntityUid target)
{
var complexInteractions = SupportsComplexInteractions(user);
if (!complexInteractions)
{
InteractionActivate(user,
target,
checkCanInteract: false,
checkUseDelay: true,
checkAccess: false,
complexInteractions: complexInteractions);
return;
}
// allow for special logic before main interaction
var ev = new BeforeInteractHandEvent(target);
RaiseLocalEvent(user, ev);
@@ -475,10 +475,12 @@ namespace Content.Shared.Interaction
return;
// Else we run Activate.
InteractionActivate(user, target,
InteractionActivate(user,
target,
checkCanInteract: false,
checkUseDelay: true,
checkAccess: false);
checkAccess: false,
complexInteractions: complexInteractions);
}
public void InteractUsingRanged(EntityUid user, EntityUid used, EntityUid? target,
@@ -921,7 +923,7 @@ namespace Content.Shared.Interaction
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target))
return;
if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user))
if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user, used))
return;
if (RangedInteractDoBefore(user, used, target, clickLocation, true))
@@ -1001,7 +1003,8 @@ namespace Content.Shared.Interaction
EntityUid used,
bool checkCanInteract = true,
bool checkUseDelay = true,
bool checkAccess = true)
bool checkAccess = true,
bool? complexInteractions = null)
{
_delayQuery.TryComp(used, out var delayComponent);
if (checkUseDelay && delayComponent != null && _useDelay.IsDelayed((used, delayComponent)))
@@ -1018,13 +1021,12 @@ namespace Content.Shared.Interaction
if (checkAccess && !IsAccessible(user, used))
return false;
// Does the user have hands?
if (!_handsQuery.HasComp(user))
return false;
var activateMsg = new ActivateInWorldEvent(user, used);
complexInteractions ??= SupportsComplexInteractions(user);
var activateMsg = new ActivateInWorldEvent(user, used, complexInteractions.Value);
RaiseLocalEvent(used, activateMsg, true);
if (!activateMsg.Handled)
var userEv = new UserActivateInWorldEvent(user, used, complexInteractions.Value);
RaiseLocalEvent(user, userEv, true);
if (!activateMsg.Handled && !userEv.Handled)
return false;
DoContactInteraction(user, used, activateMsg);
@@ -1059,7 +1061,7 @@ namespace Content.Shared.Interaction
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used))
return false;
if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user))
if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user, used))
return false;
var useMsg = new UseInHandEvent(user);
@@ -1259,6 +1261,39 @@ namespace Content.Shared.Interaction
? BoundUserInterfaceRangeResult.Pass
: BoundUserInterfaceRangeResult.Fail;
}
/// <summary>
/// Gets the entity that is currently being "used" for the interaction.
/// In most cases, this refers to the entity in the character's active hand.
/// </summary>
/// <returns>If there is an entity being used.</returns>
public bool TryGetUsedEntity(EntityUid user, [NotNullWhen(true)] out EntityUid? used, bool checkCanUse = true)
{
var ev = new GetUsedEntityEvent();
RaiseLocalEvent(user, ref ev);
used = ev.Used;
if (!ev.Handled)
return false;
// Can the user use the held entity?
if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user, ev.Used!.Value))
{
used = null;
return false;
}
return ev.Handled;
}
/// <summary>
/// Checks if a given entity is able to do specific complex interactions.
/// This is used to gate manipulation to general humanoids. If a mouse shouldn't be able to do something, then it's complex.
/// </summary>
public bool SupportsComplexInteractions(EntityUid user)
{
return _complexInteractionQuery.HasComp(user);
}
}
/// <summary>
@@ -1284,6 +1319,24 @@ namespace Content.Shared.Interaction
}
}
/// <summary>
/// Raised directed by-ref on an entity to determine what item will be used in interactions.
/// </summary>
[ByRefEvent]
public record struct GetUsedEntityEvent()
{
public EntityUid? Used = null;
public bool Handled => Used != null;
};
/// <summary>
/// Raised directed by-ref on an item and a user to determine if interactions can occur.
/// </summary>
/// <param name="Cancelled">Whether the hand interaction should be cancelled.</param>
[ByRefEvent]
public record struct AttemptUseInteractEvent(EntityUid User, EntityUid Used, bool Cancelled = false);
/// <summary>
/// Raised directed by-ref on an item to determine if hand interactions should go through.
/// Defaults to allowing hand interactions to go through. Cancel to force the item to be attacked instead.