コード例 #1
0
        /// <summary>
        /// Allows a user to pick up entities by clicking them, or pick up all entities in a certain radius
        /// around a click.
        /// </summary>
        /// <returns></returns>
        private async void AfterInteract(EntityUid uid, ServerStorageComponent storageComp, AfterInteractEvent eventArgs)
        {
            if (!eventArgs.CanReach)
            {
                return;
            }

            if (storageComp.CancelToken != null)
            {
                storageComp.CancelToken.Cancel();
                storageComp.CancelToken = null;
                return;
            }

            // Pick up all entities in a radius around the clicked location.
            // The last half of the if is because carpets exist and this is terrible
            if (storageComp.AreaInsert && (eventArgs.Target == null || !HasComp <ItemComponent>(eventArgs.Target.Value)))
            {
                var validStorables = new List <EntityUid>();
                foreach (var entity in _entityLookupSystem.GetEntitiesInRange(eventArgs.ClickLocation, storageComp.AreaInsertRadius, LookupFlags.None))
                {
                    if (entity == eventArgs.User ||
                        !HasComp <ItemComponent>(entity) ||
                        !_interactionSystem.InRangeUnobstructed(eventArgs.User, entity))
                    {
                        continue;
                    }

                    validStorables.Add(entity);
                }

                //If there's only one then let's be generous
                if (validStorables.Count > 1)
                {
                    storageComp.CancelToken = new CancellationTokenSource();
                    var doAfterArgs = new DoAfterEventArgs(eventArgs.User, 0.2f * validStorables.Count, storageComp.CancelToken.Token, uid)
                    {
                        BreakOnStun     = true,
                        BreakOnDamage   = true,
                        BreakOnUserMove = true,
                        NeedHand        = true,
                    };

                    await _doAfterSystem.WaitDoAfter(doAfterArgs);
                }

                // TODO: Make it use the event DoAfter
                var successfullyInserted          = new List <EntityUid>();
                var successfullyInsertedPositions = new List <EntityCoordinates>();
                foreach (var entity in validStorables)
                {
                    // Check again, situation may have changed for some entities, but we'll still pick up any that are valid
                    if (_containerSystem.IsEntityInContainer(entity) ||
                        entity == eventArgs.User ||
                        !HasComp <ItemComponent>(entity))
                    {
                        continue;
                    }

                    if (TryComp <TransformComponent>(uid, out var transformOwner) && TryComp <TransformComponent>(entity, out var transformEnt))
                    {
                        var position = EntityCoordinates.FromMap(transformOwner.Parent?.Owner ?? uid, transformEnt.MapPosition);

                        if (PlayerInsertEntityInWorld(uid, eventArgs.User, entity, storageComp))
                        {
                            successfullyInserted.Add(entity);
                            successfullyInsertedPositions.Add(position);
                        }
                    }
                }

                // If we picked up atleast one thing, play a sound and do a cool animation!
                if (successfullyInserted.Count > 0)
                {
                    if (storageComp.StorageInsertSound is not null)
                    {
                        SoundSystem.Play(storageComp.StorageInsertSound.GetSound(), Filter.Pvs(uid, entityManager: EntityManager), uid, AudioParams.Default);
                    }
                    RaiseNetworkEvent(new AnimateInsertingEntitiesEvent(uid, successfullyInserted, successfullyInsertedPositions));
                }
                return;
            }
            // Pick up the clicked entity
            else if (storageComp.QuickInsert)
            {
                if (eventArgs.Target is not {
                    Valid : true
                } target)
                {
                    return;
                }

                if (_containerSystem.IsEntityInContainer(target) ||
                    target == eventArgs.User ||
                    !HasComp <ItemComponent>(target))
                {
                    return;
                }

                if (TryComp <TransformComponent>(uid, out var transformOwner) && TryComp <TransformComponent>(target, out var transformEnt))
                {
                    var parent = transformOwner.ParentUid;

                    var position = EntityCoordinates.FromMap(
                        parent.IsValid() ? parent : uid,
                        transformEnt.MapPosition);
                    if (PlayerInsertEntityInWorld(uid, eventArgs.User, target, storageComp))
                    {
                        RaiseNetworkEvent(new AnimateInsertingEntitiesEvent(uid,
                                                                            new List <EntityUid> {
                            target
                        },
                                                                            new List <EntityCoordinates> {
                            position
                        }));
                    }
                }
            }
            return;
        }
コード例 #2
0
        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}");
                    }
                }
            }
        }