private void OnEntityActionPerform(EntityUid uid, SharedCombatModeComponent component, DisarmActionEvent args) { if (args.Handled) { return; } if (!_actionBlockerSystem.CanAttack(args.Performer)) { return; } var attemptEvent = new DisarmAttemptEvent(args.Target, args.Performer); RaiseLocalEvent(args.Target, attemptEvent); if (attemptEvent.Cancelled) { return; } var diff = Transform(args.Target).MapPosition.Position - Transform(args.Performer).MapPosition.Position; var angle = Angle.FromWorldVec(diff); var filterAll = Filter.Pvs(args.Performer); var filterOther = filterAll.RemoveWhereAttachedEntity(e => e == args.Performer); args.Handled = true; if (_random.Prob(component.DisarmFailChance)) { SoundSystem.Play(Filter.Pvs(args.Performer), component.DisarmFailSound.GetSound(), args.Performer, AudioHelpers.WithVariation(0.025f)); var targetName = Name(args.Target); var msgOther = Loc.GetString( "disarm-action-popup-message-other-clients", ("performerName", Name(args.Performer)), ("targetName", targetName)); var msgUser = Loc.GetString("disarm-action-popup-message-cursor", ("targetName", targetName)); _popupSystem.PopupEntity(msgOther, args.Performer, filterOther); _popupSystem.PopupEntity(msgUser, args.Performer, Filter.Entities(args.Performer)); _meleeWeaponSystem.SendLunge(angle, args.Performer); return; } _meleeWeaponSystem.SendAnimation("disarm", angle, args.Performer, args.Performer, new[] { args.Target }); SoundSystem.Play(filterAll, component.DisarmSuccessSound.GetSound(), args.Performer, AudioHelpers.WithVariation(0.025f)); _logSystem.Add(LogType.DisarmedAction, $"{ToPrettyString(args.Performer):user} used disarm on {ToPrettyString(args.Target):target}"); var eventArgs = new DisarmedEvent() { Target = args.Target, Source = args.Performer, PushProbability = component.DisarmPushChance }; RaiseLocalEvent(args.Target, eventArgs); }
private void OnEntityActionPerform(EntityUid uid, SharedCombatModeComponent component, DisarmActionEvent args) { if (args.Handled) { return; } if (!_actionBlockerSystem.CanAttack(args.Performer)) { return; } if (TryComp <HandsComponent>(args.Performer, out var hands) && hands.ActiveHand != null && !hands.ActiveHand.IsEmpty) { _popupSystem.PopupEntity(Loc.GetString("disarm-action-free-hand"), args.Performer, Filter.Entities(args.Performer)); return; } EntityUid?inTargetHand = null; if (TryComp <HandsComponent>(args.Target, out HandsComponent? targetHandsComponent) && targetHandsComponent.ActiveHand != null && !targetHandsComponent.ActiveHand.IsEmpty) { inTargetHand = targetHandsComponent.ActiveHand.HeldEntity !.Value; } var attemptEvent = new DisarmAttemptEvent(args.Target, args.Performer, inTargetHand); if (inTargetHand != null) { RaiseLocalEvent(inTargetHand.Value, attemptEvent, true); } RaiseLocalEvent(args.Target, attemptEvent, true); if (attemptEvent.Cancelled) { return; } var diff = Transform(args.Target).MapPosition.Position - Transform(args.Performer).MapPosition.Position; var angle = Angle.FromWorldVec(diff); var filterAll = Filter.Pvs(args.Performer); var filterOther = filterAll.RemoveWhereAttachedEntity(e => e == args.Performer); args.Handled = true; var chance = CalculateDisarmChance(args.Performer, args.Target, inTargetHand, component); if (_random.Prob(chance)) { SoundSystem.Play(component.DisarmFailSound.GetSound(), Filter.Pvs(args.Performer), args.Performer, AudioHelpers.WithVariation(0.025f)); var msgOther = Loc.GetString( "disarm-action-popup-message-other-clients", ("performerName", Identity.Entity(args.Performer, EntityManager)), ("targetName", Identity.Entity(args.Target, EntityManager))); var msgUser = Loc.GetString("disarm-action-popup-message-cursor", ("targetName", Identity.Entity(args.Target, EntityManager))); _popupSystem.PopupEntity(msgOther, args.Performer, filterOther); _popupSystem.PopupEntity(msgUser, args.Performer, Filter.Entities(args.Performer)); _meleeWeaponSystem.SendLunge(angle, args.Performer); return; } _meleeWeaponSystem.SendAnimation("disarm", angle, args.Performer, args.Performer, new[] { args.Target }); SoundSystem.Play(component.DisarmSuccessSound.GetSound(), filterAll, args.Performer, AudioHelpers.WithVariation(0.025f)); _adminLogger.Add(LogType.DisarmedAction, $"{ToPrettyString(args.Performer):user} used disarm on {ToPrettyString(args.Target):target}"); var eventArgs = new DisarmedEvent() { Target = args.Target, Source = args.Performer, PushProbability = HasComp <DisarmProneComponent>(args.Target) ? 1.0f : chance }; RaiseLocalEvent(args.Target, eventArgs, true); }
public override void DoAttack(EntityUid user, EntityCoordinates coordinates, bool wideAttack, EntityUid?target = null) { // TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction. if (!ValidateInteractAndFace(user, coordinates)) { return; } if (!_actionBlockerSystem.CanAttack(user, target)) { return; } if (!wideAttack) { // Check if interacted entity is in the same container, the direct child, or direct parent of the user. if (target != null && !Deleted(target.Value) && !ContainerSystem.IsInSameOrParentContainer(user, target.Value) && !CanAccessViaStorage(user, target.Value)) { Logger.WarningS("system.interaction", $"User entity {ToPrettyString(user):user} clicked on object {ToPrettyString(target.Value):target} that isn't the parent, child, or in the same container"); return; } // TODO: Replace with body attack range when we get something like arm length or telekinesis or something. var unobstructed = (target == null) ? InRangeUnobstructed(user, coordinates) : InRangeUnobstructed(user, target.Value); if (!unobstructed) { return; } } // Verify user has a hand, and find what object they are currently holding in their active hand if (TryComp(user, out HandsComponent? hands)) { var item = hands.GetActiveHandItem?.Owner; if (item != null && !Deleted(item.Value)) { if (wideAttack) { var ev = new WideAttackEvent(item.Value, user, coordinates); RaiseLocalEvent(item.Value, ev, false); if (ev.Handled) { _adminLogSystem.Add(LogType.AttackArmedWide, LogImpact.Medium, $"{ToPrettyString(user):user} wide attacked with {ToPrettyString(item.Value):used} at {coordinates}"); return; } } else { var ev = new ClickAttackEvent(item.Value, user, coordinates, target); RaiseLocalEvent(item.Value, ev, false); if (ev.Handled) { if (target != null) { _adminLogSystem.Add(LogType.AttackArmedClick, LogImpact.Medium, $"{ToPrettyString(user):user} attacked {ToPrettyString(target.Value):target} with {ToPrettyString(item.Value):used} at {coordinates}"); } else { _adminLogSystem.Add(LogType.AttackArmedClick, LogImpact.Medium, $"{ToPrettyString(user):user} attacked with {ToPrettyString(item.Value):used} at {coordinates}"); } return; } } } else if (!wideAttack && target != null && HasComp <SharedItemComponent>(target.Value)) { // We pick up items if our hand is empty, even if we're in combat mode. InteractHand(user, target.Value); return; } } // TODO: Make this saner? // Attempt to do unarmed combat. We don't check for handled just because at this point it doesn't matter. if (wideAttack) { var ev = new WideAttackEvent(user, user, coordinates); RaiseLocalEvent(user, ev, false); if (ev.Handled) { _adminLogSystem.Add(LogType.AttackUnarmedWide, $"{ToPrettyString(user):user} wide attacked at {coordinates}"); } } else { var ev = new ClickAttackEvent(user, user, coordinates, target); RaiseLocalEvent(user, ev, false); if (ev.Handled) { if (target != null) { _adminLogSystem.Add(LogType.AttackUnarmedClick, LogImpact.Medium, $"{ToPrettyString(user):user} attacked {ToPrettyString(target.Value):target} at {coordinates}"); } else { _adminLogSystem.Add(LogType.AttackUnarmedClick, LogImpact.Medium, $"{ToPrettyString(user):user} attacked at {coordinates}"); } } } }
private bool UserCanFire(IEntity user) { return((UserCanFireHandler == null || UserCanFireHandler(user)) && ActionBlockerSystem.CanAttack(user)); }
public void DoAttack(EntityUid user, EntityCoordinates coordinates, bool wideAttack, EntityUid targetUid = default) { if (!ValidateInteractAndFace(user, coordinates)) { return; } if (!_actionBlockerSystem.CanAttack(user)) { return; } if (!wideAttack) { // Check if interacted entity is in the same container, the direct child, or direct parent of the user. if (targetUid != default && !user.IsInSameOrParentContainer(targetUid) && !CanAccessViaStorage(user, targetUid)) { Logger.WarningS("system.interaction", $"User entity named {EntityManager.GetComponent<MetaDataComponent>(user).EntityName} clicked on object {EntityManager.GetComponent<MetaDataComponent>(targetUid).EntityName} that isn't the parent, child, or in the same container"); return; } // TODO: Replace with body attack range when we get something like arm length or telekinesis or something. if (!user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true)) { return; } } // Verify user has a hand, and find what object they are currently holding in their active hand if (EntityManager.TryGetComponent <HandsComponent?>(user, out var hands)) { if (hands.GetActiveHand?.Owner is { Valid : true } item) { if (wideAttack) { var ev = new WideAttackEvent(item, user, coordinates); RaiseLocalEvent(item, ev, false); if (ev.Handled) { _adminLogSystem.Add(LogType.AttackArmedWide, LogImpact.Medium, $"{user} wide attacked with {item} at {coordinates}"); return; } } else { var ev = new ClickAttackEvent(item, user, coordinates, targetUid); RaiseLocalEvent(item, ev, false); if (ev.Handled) { if (targetUid != default) { _adminLogSystem.Add(LogType.AttackArmedClick, LogImpact.Medium, $"{user} attacked {targetUid} with {item} at {coordinates}"); } else { _adminLogSystem.Add(LogType.AttackArmedClick, LogImpact.Medium, $"{user} attacked with {item} at {coordinates}"); } return; } } } else if (!wideAttack && targetUid != default && _entityManager.HasComponent <ItemComponent>(targetUid)) { // We pick up items if our hand is empty, even if we're in combat mode. InteractHand(user, targetUid); return; } }
public override void DoAttack(EntityUid user, EntityCoordinates coordinates, bool wideAttack, EntityUid?target = null) { // TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction. if (!ValidateInteractAndFace(user, coordinates)) { return; } // Check general interaction blocking. if (!_actionBlockerSystem.CanInteract(user, target)) { return; } // Check combat-specific action blocking. if (!_actionBlockerSystem.CanAttack(user, target)) { return; } if (!wideAttack) { // Check if interacted entity is in the same container, the direct child, or direct parent of the user. if (target != null && !Deleted(target.Value) && !ContainerSystem.IsInSameOrParentContainer(user, target.Value) && !CanAccessViaStorage(user, target.Value)) { Logger.WarningS("system.interaction", $"User entity {ToPrettyString(user):user} clicked on object {ToPrettyString(target.Value):target} that isn't the parent, child, or in the same container"); return; } // TODO: Replace with body attack range when we get something like arm length or telekinesis or something. var unobstructed = (target == null) ? InRangeUnobstructed(user, coordinates) : InRangeUnobstructed(user, target.Value); if (!unobstructed) { return; } } else if (ContainerSystem.IsEntityInContainer(user)) { // No wide attacking while in containers (holos, lockers, etc). // Can't think of a valid case where you would want this. return; } // Verify user has a hand, and find what object they are currently holding in their active hand if (TryComp(user, out HandsComponent? hands)) { var item = hands.ActiveHandEntity; if (!Deleted(item)) { var meleeVee = new MeleeAttackAttemptEvent(); RaiseLocalEvent(item.Value, ref meleeVee, true); if (meleeVee.Cancelled) { return; } if (wideAttack) { var ev = new WideAttackEvent(item.Value, user, coordinates); RaiseLocalEvent(item.Value, ev, false); if (ev.Handled) { _adminLogger.Add(LogType.AttackArmedWide, LogImpact.Low, $"{ToPrettyString(user):user} wide attacked with {ToPrettyString(item.Value):used} at {coordinates}"); return; } } else { var ev = new ClickAttackEvent(item.Value, user, coordinates, target); RaiseLocalEvent(item.Value, ev, false); if (ev.Handled) { if (target != null) { _adminLogger.Add(LogType.AttackArmedClick, LogImpact.Low, $"{ToPrettyString(user):user} attacked {ToPrettyString(target.Value):target} with {ToPrettyString(item.Value):used} at {coordinates}"); } else { _adminLogger.Add(LogType.AttackArmedClick, LogImpact.Low, $"{ToPrettyString(user):user} attacked with {ToPrettyString(item.Value):used} at {coordinates}"); } return; } } } else if (!wideAttack && target != null && HasComp <ItemComponent>(target.Value)) { // We pick up items if our hand is empty, even if we're in combat mode. InteractHand(user, target.Value); return; } } // TODO: Make this saner? // Attempt to do unarmed combat. We don't check for handled just because at this point it doesn't matter. var used = user; if (_inventory.TryGetSlotEntity(user, "gloves", out var gloves) && HasComp <MeleeWeaponComponent>(gloves)) { used = (EntityUid)gloves; } if (wideAttack) { var ev = new WideAttackEvent(used, user, coordinates); RaiseLocalEvent(used, ev, false); if (ev.Handled) { _adminLogger.Add(LogType.AttackUnarmedWide, LogImpact.Low, $"{ToPrettyString(user):user} wide attacked at {coordinates}"); } } else { var ev = new ClickAttackEvent(used, user, coordinates, target); RaiseLocalEvent(used, ev, false); if (ev.Handled) { if (target != null) { _adminLogger.Add(LogType.AttackUnarmedClick, LogImpact.Low, $"{ToPrettyString(user):user} attacked {ToPrettyString(target.Value):target} at {coordinates}"); } else { _adminLogger.Add(LogType.AttackUnarmedClick, LogImpact.Low, $"{ToPrettyString(user):user} attacked at {coordinates}"); } } } }
public void DoAttack(IEntity user, EntityCoordinates coordinates, bool wideAttack, EntityUid targetUid = default) { if (!ValidateInteractAndFace(user, coordinates)) { return; } if (!_actionBlockerSystem.CanAttack(user)) { return; } IEntity?targetEnt = null; if (!wideAttack) { // Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null EntityManager.TryGetEntity(targetUid, out targetEnt); // Check if interacted entity is in the same container, the direct child, or direct parent of the user. if (targetEnt != null && !user.IsInSameOrParentContainer(targetEnt)) { Logger.WarningS("system.interaction", $"User entity named {user.Name} clicked on object {targetEnt.Name} that isn't the parent, child, or in the same container"); return; } // TODO: Replace with body attack range when we get something like arm length or telekinesis or something. if (!user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true)) { return; } } // Verify user has a hand, and find what object they are currently holding in their active hand if (user.TryGetComponent <IHandsComponent>(out var hands)) { var item = hands.GetActiveHand?.Owner; if (item != null) { if (wideAttack) { var ev = new WideAttackEvent(item, user, coordinates); RaiseLocalEvent(item.Uid, ev, false); if (ev.Handled) { return; } } else { var ev = new ClickAttackEvent(item, user, coordinates, targetUid); RaiseLocalEvent(item.Uid, ev, false); if (ev.Handled) { return; } } } else if (!wideAttack && (targetEnt != null || EntityManager.TryGetEntity(targetUid, out targetEnt)) && targetEnt.HasComponent <ItemComponent>()) { // We pick up items if our hand is empty, even if we're in combat mode. InteractHand(user, targetEnt); return; } } // TODO: Make this saner? // Attempt to do unarmed combat. We don't check for handled just because at this point it doesn't matter. if (wideAttack) { RaiseLocalEvent(user.Uid, new WideAttackEvent(user, user, coordinates), false); } else { RaiseLocalEvent(user.Uid, new ClickAttackEvent(user, user, coordinates, targetUid), false); } }