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}"); } } } }
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}"); } } } }
private void OnWideAttack(EntityUid uid, MeleeWeaponComponent comp, WideAttackEvent args) { args.Handled = true; var curTime = _gameTiming.CurTime; if (curTime < comp.CooldownEnd) { return; } var owner = EntityManager.GetEntity(uid); var location = args.User.Transform.Coordinates; var diff = args.ClickLocation.ToMapPos(owner.EntityManager) - location.ToMapPos(owner.EntityManager); var angle = Angle.FromWorldVec(diff); // This should really be improved. GetEntitiesInArc uses pos instead of bounding boxes. var entities = ArcRayCast(args.User.Transform.WorldPosition, angle, comp.ArcWidth, comp.Range, owner.Transform.MapID, args.User); var hitEntities = new List <IEntity>(); foreach (var entity in entities) { if (!entity.Transform.IsMapTransform || entity == args.User) { continue; } if (ComponentManager.HasComponent <IDamageableComponent>(entity.Uid)) { hitEntities.Add(entity); } } // Raise event before doing damage so we can cancel damage if handled var hitEvent = new MeleeHitEvent(hitEntities, args.User); RaiseLocalEvent(uid, hitEvent, false); SendAnimation(comp.Arc, angle, args.User, owner, hitEntities); if (!hitEvent.Handled) { if (entities.Count != 0) { SoundSystem.Play(Filter.Pvs(owner), comp.HitSound.GetSound(), entities.First().Transform.Coordinates); } else { SoundSystem.Play(Filter.Pvs(owner), comp.MissSound.GetSound(), args.User.Transform.Coordinates); } foreach (var entity in hitEntities) { if (entity.TryGetComponent <IDamageableComponent>(out var damageComponent)) { damageComponent.TryChangeDamage(comp.DamageType, comp.Damage); } } } comp.LastAttackTime = curTime; comp.CooldownEnd = comp.LastAttackTime + TimeSpan.FromSeconds(comp.ArcCooldownTime); RaiseLocalEvent(uid, new RefreshItemCooldownEvent(comp.LastAttackTime, comp.CooldownEnd), false); }
public void DoAttack(IEntity user, EntityCoordinates coordinates, bool wideAttack, EntityUid targetUid = default) { if (!ValidateInteractAndFace(user, coordinates)) { return; } if (!Get <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 he is currently holding in his 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); } }