Ejemplo n.º 1
0
 public override bool IsValidPosition(EntityCoordinates position)
 {
     return(true);
 }
Ejemplo n.º 2
0
 public abstract IEntity?TakeProjectile(EntityCoordinates spawnAt);
        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;
                }
            }

            // 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 (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}");
                    }
                }
            }
        }
Ejemplo n.º 4
0
 public FirePosEvent(EntityCoordinates coordinates)
 {
     Coordinates = coordinates;
 }
 public PerformTargetPointItemActionMessage(ItemActionType actionType, EntityUid item, EntityCoordinates target) : base(actionType, item)
 {
     _target = target;
 }
Ejemplo n.º 6
0
        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 RequestDecalRemovalEvent(EntityCoordinates coordinates)
 {
     Coordinates = coordinates;
 }
 public static IEntity?SpawnIfUnobstructed(
     this IEntityManager entityManager,
     string?prototypeName,
     EntityCoordinates coordinates,
     CollisionGroup collisionLayer,
     in Box2?box = null,
Ejemplo n.º 9
0
        public async Task PuddlePauseTest()
        {
            var server = StartServer();

            await server.WaitIdleAsync();

            var sMapManager            = server.ResolveDependency <IMapManager>();
            var sTileDefinitionManager = server.ResolveDependency <ITileDefinitionManager>();
            var sGameTiming            = server.ResolveDependency <IGameTiming>();
            var entityManager          = server.ResolveDependency <IEntityManager>();

            MapId             sMapId = default;
            IMapGrid          sGrid;
            GridId            sGridId      = default;
            EntityUid         sGridEntity  = default;
            EntityCoordinates sCoordinates = default;

            // Spawn a paused map with one tile to spawn puddles on
            await server.WaitPost(() =>
            {
                sMapId = sMapManager.CreateMap();
                sMapManager.SetMapPaused(sMapId, true);
                sGrid       = sMapManager.CreateGrid(sMapId);
                sGridId     = sGrid.Index;
                sGridEntity = sGrid.GridEntityId;
                entityManager.GetComponent <MetaDataComponent>(sGridEntity).EntityPaused = true; // See https://github.com/space-wizards/RobustToolbox/issues/1444

                var tileDefinition = sTileDefinitionManager["underplating"];
                var tile           = new Tile(tileDefinition.TileId);
                sCoordinates       = sGrid.ToCoordinates();

                sGrid.SetTile(sCoordinates, tile);
            });

            // Check that the map and grid are paused
            await server.WaitAssertion(() =>
            {
                Assert.True(sMapManager.IsGridPaused(sGridId));
                Assert.True(sMapManager.IsMapPaused(sMapId));
            });

            float                evaporateTime = default;
            PuddleComponent      puddle        = null;
            MetaDataComponent    meta          = null;
            EvaporationComponent evaporation;

            var amount = 2;

            var entitySystemManager = server.ResolveDependency <IEntitySystemManager>();
            var spillSystem         = entitySystemManager.GetEntitySystem <SpillableSystem>();

            // Spawn a puddle
            await server.WaitAssertion(() =>
            {
                var solution = new Solution("Water", FixedPoint2.New(amount));
                puddle       = spillSystem.SpillAt(solution, sCoordinates, "PuddleSmear");
                meta         = entityManager.GetComponent <MetaDataComponent>(puddle.Owner);

                // Check that the puddle was created
                Assert.NotNull(puddle);

                evaporation = entityManager.GetComponent <EvaporationComponent>(puddle.Owner);

                meta.EntityPaused = true; // See https://github.com/space-wizards/RobustToolbox/issues/1445

                Assert.True(meta.EntityPaused);

                // Check that the puddle is going to evaporate
                Assert.Positive(evaporation.EvaporateTime);

                // Should have a timer component added to it for evaporation
                Assert.That(evaporation.Accumulator, Is.EqualTo(0f));

                evaporateTime = evaporation.EvaporateTime;
            });

            // Wait enough time for it to evaporate if it was unpaused
            var sTimeToWait = (5 + (int)Math.Ceiling(amount * evaporateTime * sGameTiming.TickRate));
            await server.WaitRunTicks(sTimeToWait);

            // No evaporation due to being paused
            await server.WaitAssertion(() =>
            {
                Assert.True(meta.EntityPaused);

                // Check that the puddle still exists
                Assert.False(meta.EntityDeleted);
            });

            // Unpause the map
            await server.WaitPost(() => { sMapManager.SetMapPaused(sMapId, false); });

            // Check that the map, grid and puddle are unpaused
            await server.WaitAssertion(() =>
            {
                Assert.False(sMapManager.IsMapPaused(sMapId));
                Assert.False(sMapManager.IsGridPaused(sGridId));
                Assert.False(meta.EntityPaused);

                // Check that the puddle still exists
                Assert.False(meta.EntityDeleted);
            });

            // Wait enough time for it to evaporate
            await server.WaitRunTicks(sTimeToWait);

            // Puddle evaporation should have ticked
            await server.WaitAssertion(() =>
            {
                // Check that puddle has been deleted
                Assert.True(puddle.Deleted);
            });
        }
        protected bool TryFindRandomTile(out Vector2i tile, out EntityUid targetStation, out EntityUid targetGrid, out EntityCoordinates targetCoords)
        {
            tile = default;

            targetCoords = EntityCoordinates.Invalid;
            if (StationSystem.Stations.Count == 0)
            {
                targetStation = EntityUid.Invalid;
                targetGrid    = EntityUid.Invalid;
                return(false);
            }
            targetStation = RobustRandom.Pick(StationSystem.Stations);
            var possibleTargets = Comp <StationDataComponent>(targetStation).Grids;

            if (possibleTargets.Count == 0)
            {
                targetGrid = EntityUid.Invalid;
                return(false);
            }

            targetGrid = RobustRandom.Pick(possibleTargets);

            if (!TryComp <IMapGridComponent>(targetGrid, out var gridComp))
            {
                return(false);
            }
            var grid = gridComp.Grid;

            var atmosphereSystem = Get <AtmosphereSystem>();
            var found            = false;
            var gridBounds       = grid.WorldAABB;
            var gridPos          = grid.WorldPosition;

            for (var i = 0; i < 10; i++)
            {
                var randomX = RobustRandom.Next((int)gridBounds.Left, (int)gridBounds.Right);
                var randomY = RobustRandom.Next((int)gridBounds.Bottom, (int)gridBounds.Top);

                tile = new Vector2i(randomX - (int)gridPos.X, randomY - (int)gridPos.Y);
                if (atmosphereSystem.IsTileSpace(grid.GridEntityId, Transform(targetGrid).MapUid, tile, mapGridComp:gridComp) ||
                    atmosphereSystem.IsTileAirBlocked(grid.GridEntityId, tile, mapGridComp:gridComp))
                {
                    continue;
                }
                found        = true;
                targetCoords = grid.GridTileToLocal(tile);
                break;
            }

            if (!found)
            {
                return(false);
            }

            return(true);
        }
        public override IEntity TakeProjectile(EntityCoordinates spawnAt)
        {
            var powerCellEntity = _powerCellContainer.ContainedEntity;

            if (powerCellEntity == null)
            {
                return(null);
            }

            var capacitor = powerCellEntity.GetComponent <BatteryComponent>();

            if (capacitor.CurrentCharge < _lowerChargeLimit)
            {
                return(null);
            }

            // Can fire confirmed
            // Multiply the entity's damage / whatever by the percentage of charge the shot has.
            IEntity entity;
            var     chargeChange = Math.Min(capacitor.CurrentCharge, _baseFireCost);

            if (capacitor.UseCharge(chargeChange) < _lowerChargeLimit)
            {
                // Handling of funny exploding cells.
                return(null);
            }
            var energyRatio = chargeChange / _baseFireCost;

            if (_ammoContainer.ContainedEntity != null)
            {
                entity = _ammoContainer.ContainedEntity;
                _ammoContainer.Remove(entity);
                entity.Transform.Coordinates = spawnAt;
            }
            else
            {
                entity = Owner.EntityManager.SpawnEntity(_ammoPrototype, spawnAt);
            }

            if (entity.TryGetComponent(out ProjectileComponent projectileComponent))
            {
                if (energyRatio < 1.0)
                {
                    var newDamages = new Dictionary <DamageType, int>(projectileComponent.Damages.Count);
                    foreach (var(damageType, damage) in projectileComponent.Damages)
                    {
                        newDamages.Add(damageType, (int)(damage * energyRatio));
                    }

                    projectileComponent.Damages = newDamages;
                }
            }
            else if (entity.TryGetComponent(out HitscanComponent hitscanComponent))
            {
                hitscanComponent.Damage       *= energyRatio;
                hitscanComponent.ColorModifier = energyRatio;
            }
            else
            {
                throw new InvalidOperationException("Ammo doesn't have hitscan or projectile?");
            }

            Dirty();
            UpdateAppearance();
            return(entity);
        }
 public AnimatePickupEntityMessage(EntityUid entity, EntityCoordinates entityPosition)
 {
     Directed       = true;
     EntityId       = entity;
     EntityPosition = entityPosition;
 }
Ejemplo n.º 13
0
        /// <summary>
        /// Gets all entities intersecting the given position.
        ///
        /// Static alternative to GetEntitiesUnderPosition to cut out
        /// some of the boilerplate needed to get state manager and check the current state.
        /// </summary>
        /// <param name="stateManager">state manager to use to get the current game screen</param>
        /// <param name="coordinates">coordinates to check</param>
        /// <returns>the entities under the position, empty list if none found</returns>
        public static IList<IEntity> GetEntitiesUnderPosition(IStateManager stateManager, EntityCoordinates coordinates)
        {
            if (stateManager.CurrentState is GameScreenBase gameScreenBase)
            {
                return gameScreenBase.GetEntitiesUnderPosition(coordinates);
            }

            return ImmutableList<IEntity>.Empty;
        }
Ejemplo n.º 14
0
 public IList<IEntity> GetEntitiesUnderPosition(EntityCoordinates coordinates)
 {
     return GetEntitiesUnderPosition(coordinates.ToMap(EntityManager));
 }
Ejemplo n.º 15
0
    /// <summary>
    /// Return the slot position relative to a particular coordinates as "center".
    /// and perspective.
    /// </summary>
    /// <param name="coord"></param>
    /// <param name="reverseCoordinates"></param>
    /// <returns></returns>
    private Vector3 GetSlotPosition(EntityCoordinates coord, Vector2 center, bool reverseCoordinates)
    {
        Vector3 pos = Vector3.zero;
        float xDiff = coord.x - center.x;
        float yDiff = center.y - coord.y;
        pos.x = (xDiff * horizontalSpacing);// + (horizontalSpacing / 2);

        pos.z = (yDiff * verticalSpacing) - (verticalSpacing / 2);
        if (reverseCoordinates)
        {
            pos.x = -pos.x;
            pos.z = -pos.z;
            if (coord.x % 2 == 0)
            {
                pos.z -= verticalSpacing / 2;
            }
        }
        else
        {
            if (coord.x % 2 == 0)
            {
                pos.z += verticalSpacing / 2;
            }
        }
        return pos;
    }
        /// <summary>
        /// Makes a string of text float up from a location on a grid.
        /// </summary>
        /// <param name="coordinates">Location on a grid that the message floats up from.</param>
        /// <param name="viewer">The client attached entity that the message is being sent to.</param>
        /// <param name="message">Text contents of the message.</param>
        public static void PopupMessage(this EntityCoordinates coordinates, EntityUid viewer, string message)
        {
            var popupSystem = EntitySystem.Get <SharedPopupSystem>();

            popupSystem.PopupCoordinates(message, coordinates, Filter.Entities(viewer));
        }
Ejemplo n.º 17
0
        /// <summary>
        /// Will have two behaviors, either "uses" the used entity at range on the target entity if it is capable of accepting that action
        /// Or it will use the used entity itself on the position clicked, regardless of what was there
        /// </summary>
        public async Task <bool> InteractUsingRanged(EntityUid user, EntityUid used, EntityUid target, EntityCoordinates clickLocation, bool inRangeUnobstructed)
        {
            if (InteractDoBefore(user, used, inRangeUnobstructed ? target : null, clickLocation, false))
            {
                return(true);
            }

            if (target != default)
            {
                var rangedMsg = new RangedInteractEvent(user, used, target, clickLocation);
                RaiseLocalEvent(target, rangedMsg);
                if (rangedMsg.Handled)
                {
                    return(true);
                }

                var rangedInteractions         = EntityManager.GetComponents <IRangedInteract>(target).ToList();
                var rangedInteractionEventArgs = new RangedInteractEventArgs(user, used, clickLocation);

                // See if we have a ranged interaction
                foreach (var t in rangedInteractions)
                {
                    // If an InteractUsingRanged returns a status completion we finish our interaction
#pragma warning disable 618
                    if (t.RangedInteract(rangedInteractionEventArgs))
#pragma warning restore 618
                    {
                        return(true);
                    }
                }
            }

            return(await InteractDoAfter(user, used, inRangeUnobstructed?target : null, clickLocation, false));
        }
 private void PlaceAt(IMapGrid mapGrid, EntityCoordinates location, ushort tileId, float offset = 0)
 {
     mapGrid.SetTile(location.Offset(new Vector2(offset, offset)), new Tile(tileId));
     EntitySystem.Get <AudioSystem>().PlayAtCoords("/Audio/Items/genhit.ogg", location, AudioHelpers.WithVariation(0.125f));
 }
 public RequestDecalPlacementEvent(Decal decal, EntityCoordinates coordinates)
 {
     Decal       = decal;
     Coordinates = coordinates;
 }
Ejemplo n.º 20
0
        /// <summary>
        /// Will have two behaviors, either "uses" the used entity at range on the target entity if it is capable of accepting that action
        /// Or it will use the used entity itself on the position clicked, regardless of what was there
        /// </summary>
        public override async Task <bool> InteractUsingRanged(EntityUid user, EntityUid used, EntityUid?target, EntityCoordinates clickLocation, bool inRangeUnobstructed)
        {
            // TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction.
            if (InteractDoBefore(user, used, inRangeUnobstructed ? target : null, clickLocation, false))
            {
                return(true);
            }

            if (target != null)
            {
                var rangedMsg = new RangedInteractEvent(user, used, target.Value, clickLocation);
                RaiseLocalEvent(target.Value, rangedMsg);
                if (rangedMsg.Handled)
                {
                    return(true);
                }

                var rangedInteractions         = AllComps <IRangedInteract>(target.Value).ToList();
                var rangedInteractionEventArgs = new RangedInteractEventArgs(user, used, clickLocation);

                // See if we have a ranged interaction
                foreach (var t in rangedInteractions)
                {
                    // If an InteractUsingRanged returns a status completion we finish our interaction
#pragma warning disable 618
                    if (t.RangedInteract(rangedInteractionEventArgs))
#pragma warning restore 618
                    {
                        return(true);
                    }
                }
            }

            return(await InteractDoAfter(user, used, inRangeUnobstructed?target : null, clickLocation, false));
        }
Ejemplo n.º 21
0
 public EntityUid Spawn(string?prototype, EntityCoordinates coordinates)
 {
     return(EntityManager.SpawnEntity(prototype, coordinates));
 }
Ejemplo n.º 22
0
 public AfterInteractUsingEvent(EntityUid user, EntityUid used, EntityUid?target,
                                EntityCoordinates clickLocation, bool canReach) : base(user, used, target, clickLocation, canReach)
 {
 }
 public PerformTargetPointActionMessage(ActionType actionType, EntityCoordinates target) : base(actionType)
 {
     _target = target;
 }
Ejemplo n.º 24
0
 public virtual bool HijackPlacementRequest(EntityCoordinates coordinates)
 {
     return(false);
 }
 public DragDropRequestEvent(EntityCoordinates dropLocation, EntityUid dropped, EntityUid target)
 {
     DropLocation = dropLocation;
     Dropped      = dropped;
     Target       = target;
 }
Ejemplo n.º 26
0
 public virtual bool HijackDeletion(EntityCoordinates coordinates)
 {
     return(false);
 }
 public TeleportMessage(EntityCoordinates target)
 {
     Target = target;
 }
Ejemplo n.º 28
0
        public override void AlignPlacementMode(ScreenCoordinates mouseScreen)
        {
            const float SearchBoxSize = 1.5f; // size of search box in meters

            MouseCoords = ScreenToCursorGrid(mouseScreen);

            var gridId = MouseCoords.GetGridId(pManager.EntityManager);

            IMapGrid?mapGrid = null;

            if (!gridId.IsValid() || !pManager.MapManager.TryGetGrid(gridId, out mapGrid))
            {
                // create a box around the cursor
                var gridSearchBox = Box2.UnitCentered.Scale(SearchBoxSize).Translated(MouseCoords.Position);

                // find grids in search box
                var gridsInArea = pManager.MapManager.FindGridsIntersecting(MouseCoords.GetMapId(pManager.EntityManager), gridSearchBox);

                // find closest grid intersecting our search box.
                IMapGrid?closest   = null;
                var      distance  = float.PositiveInfinity;
                var      intersect = new Box2();
                foreach (var grid in gridsInArea)
                {
                    // figure out closest intersect
                    var gridIntersect = gridSearchBox.Intersect(grid.WorldBounds);
                    var gridDist      = (gridIntersect.Center - MouseCoords.Position).LengthSquared;

                    if (gridDist >= distance)
                    {
                        continue;
                    }

                    distance  = gridDist;
                    closest   = grid;
                    intersect = gridIntersect;
                }

                if (closest != null) // stick to existing grid
                {
                    // round to nearest cardinal dir
                    var normal = new Angle(MouseCoords.Position - intersect.Center).GetCardinalDir().ToVec();

                    // round coords to center of tile
                    var tileIndices     = closest.WorldToTile(intersect.Center);
                    var tileCenterWorld = closest.GridTileToWorldPos(tileIndices);

                    // move mouse one tile out along normal
                    var newTilePos = tileCenterWorld + normal * closest.TileSize;

                    MouseCoords = new EntityCoordinates(closest.GridEntityId, closest.WorldToLocal(newTilePos));
                    mapGrid     = closest;
                }
                //else free place
            }

            if (mapGrid == null)
            {
                return;
            }

            CurrentTile = mapGrid.GetTileRef(MouseCoords);
            float tileSize = mapGrid.TileSize; //convert from ushort to float

            GridDistancing = tileSize;

            if (pManager.CurrentPermission !.IsTile)
            {
                MouseCoords = new EntityCoordinates(MouseCoords.EntityId, (CurrentTile.X + tileSize / 2,
                                                                           CurrentTile.Y + tileSize / 2));
            }
Ejemplo n.º 29
0
        public async Task GrantsAndRevokesItemActions()
        {
            var serverOptions = new ServerIntegrationOptions {
                ExtraPrototypes = Prototypes
            };
            var clientOptions = new ClientIntegrationOptions {
                ExtraPrototypes = Prototypes
            };

            var(client, server) = await StartConnectedServerClientPair(serverOptions : serverOptions, clientOptions : clientOptions);

            await server.WaitIdleAsync();

            await client.WaitIdleAsync();

            var serverPlayerManager = server.ResolveDependency <IPlayerManager>();
            var serverEntManager    = server.ResolveDependency <IEntityManager>();
            var serverGameTiming    = server.ResolveDependency <IGameTiming>();

            var cooldown = Cooldowns.SecondsFromNow(30, serverGameTiming);

            ServerActionsComponent serverActionsComponent = null;
            ClientActionsComponent clientActionsComponent = null;
            IEntity serverPlayerEnt  = null;
            IEntity serverFlashlight = null;

            await server.WaitAssertion(() =>
            {
                serverPlayerEnt        = serverPlayerManager.GetAllPlayers().Single().AttachedEntity;
                serverActionsComponent = serverPlayerEnt !.GetComponent <ServerActionsComponent>();

                // spawn and give them an item that has actions
                serverFlashlight = serverEntManager.SpawnEntity("TestFlashlight",
                                                                new EntityCoordinates(new EntityUid(1), (0, 0)));
                Assert.That(serverFlashlight.TryGetComponent <ItemActionsComponent>(out var itemActions));
                // we expect this only to have a toggle light action initially
                var actionConfigs = itemActions.ActionConfigs.ToList();
                Assert.That(actionConfigs.Count == 1);
                Assert.That(actionConfigs[0].ActionType == ItemActionType.ToggleLight);
                Assert.That(actionConfigs[0].Enabled);

                // grant an extra item action, before pickup, initially disabled
                itemActions.GrantOrUpdate(ItemActionType.DebugToggle, false);
                serverPlayerEnt.GetComponent <HandsComponent>().PutInHand(serverFlashlight.GetComponent <ItemComponent>(), false);
                // grant an extra item action, after pickup, with a cooldown
                itemActions.GrantOrUpdate(ItemActionType.DebugInstant, cooldown: cooldown);

                Assert.That(serverActionsComponent.TryGetItemActionStates(serverFlashlight.Uid, out var state));
                // they should have been granted all 3 actions
                Assert.That(state.Count == 3);
                Assert.That(state.TryGetValue(ItemActionType.ToggleLight, out var toggleLightState));
                Assert.That(toggleLightState.Equals(new ActionState(true)));
                Assert.That(state.TryGetValue(ItemActionType.DebugInstant, out var debugInstantState));
                Assert.That(debugInstantState.Equals(new ActionState(true, cooldown: cooldown)));
                Assert.That(state.TryGetValue(ItemActionType.DebugToggle, out var debugToggleState));
                Assert.That(debugToggleState.Equals(new ActionState(false)));
            });

            await server.WaitRunTicks(5);

            await client.WaitRunTicks(5);

            // check that client has the actions, and toggle the light on via the action slot it was auto-assigned to
            var       clientPlayerMgr  = client.ResolveDependency <Robust.Client.Player.IPlayerManager>();
            var       clientUIMgr      = client.ResolveDependency <IUserInterfaceManager>();
            EntityUid clientFlashlight = default;
            await client.WaitAssertion(() =>
            {
                var local              = clientPlayerMgr.LocalPlayer;
                var controlled         = local !.ControlledEntity;
                clientActionsComponent = controlled !.GetComponent <ClientActionsComponent>();

                var lightEntry = clientActionsComponent.ItemActionStates()
                                 .Where(entry => entry.Value.ContainsKey(ItemActionType.ToggleLight))
                                 .FirstOrNull();
                clientFlashlight = lightEntry !.Value.Key;
                Assert.That(lightEntry, Is.Not.Null);
                Assert.That(lightEntry.Value.Value.TryGetValue(ItemActionType.ToggleLight, out var lightState));
                Assert.That(lightState.Equals(new ActionState(true)));
                Assert.That(lightEntry.Value.Value.TryGetValue(ItemActionType.DebugInstant, out var debugInstantState));
                Assert.That(debugInstantState.Equals(new ActionState(true, cooldown: cooldown)));
                Assert.That(lightEntry.Value.Value.TryGetValue(ItemActionType.DebugToggle, out var debugToggleState));
                Assert.That(debugToggleState.Equals(new ActionState(false)));

                var actionsUI = clientUIMgr.StateRoot.Children.FirstOrDefault(c => c is ActionsUI) as ActionsUI;
                Assert.That(actionsUI, Is.Not.Null);

                var toggleLightSlot = actionsUI.Slots.FirstOrDefault(slot => slot.Action is ItemActionPrototype
                {
                    ActionType: ItemActionType.ToggleLight
                });
                Assert.That(toggleLightSlot, Is.Not.Null);

                clientActionsComponent.AttemptAction(toggleLightSlot);
            });
Ejemplo n.º 30
0
        /// <summary>
        ///     Resolves user interactions with objects.
        /// </summary>
        /// <remarks>
        ///     Checks Whether combat mode is enabled and whether the user can actually interact with the given entity.
        /// </remarks>
        /// <param name="altInteract">Whether to use default or alternative interactions (usually as a result of
        /// alt+clicking). If combat mode is enabled, the alternative action is to perform the default non-combat
        /// interaction. Having an item in the active hand also disables alternative interactions.</param>
        public async void UserInteraction(EntityUid user, EntityCoordinates coordinates, EntityUid target, bool altInteract = false)
        {
            // TODO COMBAT Consider using alt-interact for advanced combat? maybe alt-interact disarms?
            if (!altInteract && EntityManager.TryGetComponent(user, out CombatModeComponent? combatMode) && combatMode.IsInCombatMode)
            {
                DoAttack(user, coordinates, false, target);
                return;
            }

            if (!ValidateInteractAndFace(user, coordinates))
            {
                return;
            }

            if (!_actionBlockerSystem.CanInteract(user))
            {
                return;
            }

            // Check if interacted entity is in the same container, the direct child, or direct parent of the user.
            // This is bypassed IF the interaction happened through an item slot (e.g., backpack UI)
            if (target != default && !user.IsInSameOrParentContainer(target) && !CanAccessViaStorage(user, target))
            {
                Logger.WarningS("system.interaction",
                                $"User entity named {EntityManager.GetComponent<MetaDataComponent>(user).EntityName} clicked on object {EntityManager.GetComponent<MetaDataComponent>(target).EntityName} that isn't the parent, child, or in the same container");
                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))
            {
                return;
            }

            var item = hands.GetActiveHand?.Owner;

            // TODO: Replace with body interaction range when we get something like arm length or telekinesis or something.
            var inRangeUnobstructed = user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true);

            if (target == default || !inRangeUnobstructed)
            {
                if (item == null)
                {
                    return;
                }

                if (!await InteractUsingRanged(user, item.Value, target, coordinates, inRangeUnobstructed) &&
                    !inRangeUnobstructed)
                {
                    var message = Loc.GetString("interaction-system-user-interaction-cannot-reach");
                    user.PopupMessage(message);
                }

                return;
            }
            else
            {
                // We are close to the nearby object.
                if (altInteract)
                {
                    // Perform alternative interactions, using context menu verbs.
                    AltInteract(user, target);
                }
                else if (item != null && item != target)
                {
                    // We are performing a standard interaction with an item, and the target isn't the same as the item
                    // currently in our hand. We will use the item in our hand on the nearby object via InteractUsing
                    await InteractUsing(user, item.Value, target, coordinates);
                }
                else if (item == null)
                {
                    // Since our hand is empty we will use InteractHand/Activate
                    InteractHand(user, target);
                }
            }
        }
Ejemplo n.º 31
0
 public AtmosExposedUpdateEvent(EntityCoordinates coordinates, GasMixture mixture)
 {
     Coordinates = coordinates;
     GasMixture  = mixture;
 }
Ejemplo n.º 32
0
 public UnitSlot GetSlot(EntityCoordinates coord)
 {
     return unitSlots[coord.x, coord.y];
 }