Пример #1
0
        private async void HandleStartStructureConstruction(TryStartStructureConstructionMessage ev, EntitySessionEventArgs args)
        {
            if (!_prototypeManager.TryIndex(ev.PrototypeName, out ConstructionPrototype constructionPrototype))
            {
                Logger.Error($"Tried to start construction of invalid recipe '{ev.PrototypeName}'!");
                RaiseNetworkEvent(new AckStructureConstructionMessage(ev.Ack));
                return;
            }

            if (!_prototypeManager.TryIndex(constructionPrototype.Graph, out ConstructionGraphPrototype constructionGraph))
            {
                Logger.Error($"Invalid construction graph '{constructionPrototype.Graph}' in recipe '{ev.PrototypeName}'!");
                RaiseNetworkEvent(new AckStructureConstructionMessage(ev.Ack));
                return;
            }

            var startNode  = constructionGraph.Nodes[constructionPrototype.StartNode];
            var targetNode = constructionGraph.Nodes[constructionPrototype.TargetNode];
            var pathFind   = constructionGraph.Path(startNode.Name, targetNode.Name);

            var user = args.SenderSession.AttachedEntity;

            if (_beingBuilt.TryGetValue(args.SenderSession, out var set))
            {
                if (!set.Add(ev.Ack))
                {
                    user.PopupMessageCursor(Loc.GetString("You are already building that!"));
                    return;
                }
            }
            else
            {
                var newSet = new HashSet <int> {
                    ev.Ack
                };
                _beingBuilt[args.SenderSession] = newSet;
            }

            foreach (var condition in constructionPrototype.Conditions)
            {
                if (!condition.Condition(user, ev.Location, ev.Angle.GetCardinalDir()))
                {
                    Cleanup();
                    return;
                }
            }

            void Cleanup()
            {
                _beingBuilt[args.SenderSession].Remove(ev.Ack);
            }

            if (user == null ||
                !ActionBlockerSystem.CanInteract(user) ||
                !user.TryGetComponent(out HandsComponent? hands) || hands.GetActiveHand == null ||
                !user.InRangeUnobstructed(ev.Location, ignoreInsideBlocker:constructionPrototype.CanBuildInImpassable))
            {
                Cleanup();
                return;
            }

            if (pathFind == null)
            {
                throw new InvalidDataException($"Can't find path from starting node to target node in construction! Recipe: {ev.PrototypeName}");
            }

            var edge = startNode.GetEdge(pathFind[0].Name);

            if (edge == null)
            {
                throw new InvalidDataException($"Can't find edge from starting node to the next node in pathfinding! Recipe: {ev.PrototypeName}");
            }

            var valid   = false;
            var holding = hands.GetActiveHand?.Owner;

            if (holding == null)
            {
                Cleanup();
                return;
            }

            // No support for conditions here!

            foreach (var step in edge.Steps)
            {
                switch (step)
                {
                case EntityInsertConstructionGraphStep entityInsert:
                    if (entityInsert.EntityValid(holding))
                    {
                        valid = true;
                    }
                    break;

                case ToolConstructionGraphStep _:
                case NestedConstructionGraphStep _:
                    throw new InvalidDataException("Invalid first step for item recipe!");
                }

                if (valid)
                {
                    break;
                }
            }

            if (!valid)
            {
                Cleanup();
                return;
            }

            var structure = await Construct(user, (ev.Ack + constructionPrototype.GetHashCode()).ToString(), constructionGraph, edge, targetNode);

            if (structure == null)
            {
                Cleanup();
                return;
            }

            structure.Transform.Coordinates   = ev.Location;
            structure.Transform.LocalRotation = constructionPrototype.CanRotate ? ev.Angle : Angle.South;

            RaiseNetworkEvent(new AckStructureConstructionMessage(ev.Ack));

            Cleanup();
        }
Пример #2
0
        private void UserInteraction(IEntity player, GridCoordinates coordinates, EntityUid clickedUid)
        {
            // Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null
            if (!EntityManager.TryGetEntity(clickedUid, out var attacked))
            {
                attacked = null;
            }

            // Verify player has a transform component
            if (!player.TryGetComponent <ITransformComponent>(out var playerTransform))
            {
                return;
            }

            // Verify player is on the same map as the entity he clicked on
            if (_mapManager.GetGrid(coordinates.GridID).ParentMapId != playerTransform.MapID)
            {
                Logger.WarningS("system.interaction",
                                $"Player named {player.Name} clicked on a map he isn't located on");
                return;
            }

            // Verify player has a hand, and find what object he is currently holding in his active hand
            if (!player.TryGetComponent <IHandsComponent>(out var hands))
            {
                return;
            }

            var item = hands.GetActiveHand?.Owner;

            if (!ActionBlockerSystem.CanInteract(player))
            {
                return;
            }

            playerTransform.LocalRotation = new Angle(coordinates.ToMapPos(_mapManager) - playerTransform.MapPosition.Position);

            // TODO: Check if client should be able to see that object to click on it in the first place

            // Clicked on empty space behavior, try using ranged attack
            if (attacked == null)
            {
                if (item != null)
                {
                    // After attack: Check if we clicked on an empty location, if so the only interaction we can do is AfterAttack
                    InteractAfterAttack(player, item, coordinates);
                }

                return;
            }

            // Verify attacked object is on the map if we managed to click on it somehow
            if (!attacked.Transform.IsMapTransform)
            {
                Logger.WarningS("system.interaction",
                                $"Player named {player.Name} clicked on object {attacked.Name} that isn't currently on the map somehow");
                return;
            }

            // Check if ClickLocation is in object bounds here, if not lets log as warning and see why
            if (attacked.TryGetComponent(out ICollidableComponent collideComp))
            {
                if (!collideComp.WorldAABB.Contains(coordinates.ToMapPos(_mapManager)))
                {
                    Logger.WarningS("system.interaction",
                                    $"Player {player.Name} clicked {attacked.Name} outside of its bounding box component somehow");
                    return;
                }
            }

            // RangedAttack/AfterAttack: Check distance between user and clicked item, if too large parse it in the ranged function
            // TODO: have range based upon the item being used? or base it upon some variables of the player himself?
            var distance = (playerTransform.WorldPosition - attacked.Transform.WorldPosition).LengthSquared;

            if (distance > InteractionRangeSquared)
            {
                if (item != null)
                {
                    RangedInteraction(player, item, attacked, coordinates);
                    return;
                }

                return; // Add some form of ranged AttackHand here if you need it someday, or perhaps just ways to modify the range of AttackHand
            }

            // We are close to the nearby object and the object isn't contained in our active hand
            // AttackBy/AfterAttack: We will either use the item on the nearby object
            if (item != null)
            {
                Interaction(player, item, attacked, coordinates);
            }
            // AttackHand/Activate: Since our hand is empty we will use AttackHand/Activate
            else
            {
                Interaction(player, attacked);
            }
        }
Пример #3
0
        private async void HandleStartItemConstruction(TryStartItemConstructionMessage ev, EntitySessionEventArgs args)
        {
            if (!_prototypeManager.TryIndex(ev.PrototypeName, out ConstructionPrototype constructionPrototype))
            {
                Logger.Error($"Tried to start construction of invalid recipe '{ev.PrototypeName}'!");
                return;
            }

            if (!_prototypeManager.TryIndex(constructionPrototype.Graph, out ConstructionGraphPrototype constructionGraph))
            {
                Logger.Error($"Invalid construction graph '{constructionPrototype.Graph}' in recipe '{ev.PrototypeName}'!");
                return;
            }

            var startNode  = constructionGraph.Nodes[constructionPrototype.StartNode];
            var targetNode = constructionGraph.Nodes[constructionPrototype.TargetNode];
            var pathFind   = constructionGraph.Path(startNode.Name, targetNode.Name);

            var user = args.SenderSession.AttachedEntity;

            if (user == null || !ActionBlockerSystem.CanInteract(user))
            {
                return;
            }

            if (!user.TryGetComponent(out HandsComponent? hands))
            {
                return;
            }

            foreach (var condition in constructionPrototype.Conditions)
            {
                if (!condition.Condition(user, user.ToCoordinates(), Direction.South))
                {
                    return;
                }
            }

            if (pathFind == null)
            {
                throw new InvalidDataException($"Can't find path from starting node to target node in construction! Recipe: {ev.PrototypeName}");
            }

            var edge = startNode.GetEdge(pathFind[0].Name);

            if (edge == null)
            {
                throw new InvalidDataException($"Can't find edge from starting node to the next node in pathfinding! Recipe: {ev.PrototypeName}");
            }

            // No support for conditions here!

            foreach (var step in edge.Steps)
            {
                switch (step)
                {
                case ToolConstructionGraphStep _:
                case NestedConstructionGraphStep _:
                    throw new InvalidDataException("Invalid first step for construction recipe!");
                }
            }

            var item = await Construct(user, "item_construction", constructionGraph, edge, targetNode);

            if (item != null && item.TryGetComponent(out ItemComponent? itemComp))
            {
                hands.PutInHandOrDrop(itemComp);
            }
        }
Пример #4
0
        private void UserInteraction(IEntity player, GridCoordinates coordinates, EntityUid clickedUid)
        {
            //Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null
            if (!EntityManager.TryGetEntity(clickedUid, out var attacked))
            {
                attacked = null;
            }

            //Verify player has a transform component
            if (!player.TryGetComponent <ITransformComponent>(out var playerTransform))
            {
                return;
            }
            //Verify player is on the same map as the entity he clicked on
            else if (coordinates.MapID != playerTransform.MapID)
            {
                Logger.Warning(string.Format("Player named {0} clicked on a map he isn't located on", player.Name));
                return;
            }

            //Verify player has a hand, and find what object he is currently holding in his active hand
            if (!player.TryGetComponent <IHandsComponent>(out var hands))
            {
                return;
            }

            var item = hands.GetActiveHand?.Owner;

            if (!ActionBlockerSystem.CanInteract(player))
            {
                return;
            }
            //TODO: Check if client should be able to see that object to click on it in the first place, prevent using locaters by firing a laser or something


            //Clicked on empty space behavior, try using ranged attack
            if (attacked == null && item != null)
            {
                //AFTERATTACK: Check if we clicked on an empty location, if so the only interaction we can do is afterattack
                InteractAfterattack(player, item, coordinates);
                return;
            }
            else if (attacked == null)
            {
                return;
            }

            //Verify attacked object is on the map if we managed to click on it somehow
            if (!attacked.GetComponent <ITransformComponent>().IsMapTransform)
            {
                Logger.Warning(string.Format("Player named {0} clicked on object {1} that isn't currently on the map somehow", player.Name, attacked.Name));
                return;
            }

            //Check if ClickLocation is in object bounds here, if not lets log as warning and see why
            if (attacked.TryGetComponent(out BoundingBoxComponent boundingbox))
            {
                if (!boundingbox.WorldAABB.Contains(coordinates.Position))
                {
                    Logger.Warning(string.Format("Player {0} clicked {1} outside of its bounding box component somehow", player.Name, attacked.Name));
                    return;
                }
            }

            //RANGEDATTACK/AFTERATTACK: Check distance between user and clicked item, if too large parse it in the ranged function
            //TODO: have range based upon the item being used? or base it upon some variables of the player himself?
            var distance = (playerTransform.WorldPosition - attacked.GetComponent <ITransformComponent>().WorldPosition).LengthSquared;

            if (distance > INTERACTION_RANGE_SQUARED)
            {
                if (item != null)
                {
                    RangedInteraction(player, item, attacked, coordinates);
                    return;
                }
                return; //Add some form of ranged attackhand here if you need it someday, or perhaps just ways to modify the range of attackhand
            }

            //We are close to the nearby object and the object isn't contained in our active hand
            //ATTACKBY/AFTERATTACK: We will either use the item on the nearby object
            if (item != null)
            {
                Interaction(player, item, attacked, coordinates);
            }
            //ATTACKHAND: Since our hand is empty we will use attackhand
            else
            {
                Interaction(player, attacked);
            }
        }
Пример #5
0
        private void UpdateKinematics(ITransformComponent transform, IMoverComponent mover, PhysicsComponent physics, CollidableComponent collider = null)
        {
            if (physics.Controller == null)
            {
                // Set up controller
                physics.SetController <MoverController>();
            }

            var weightless = !transform.Owner.HasComponent <MovementIgnoreGravityComponent>() &&
                             _physicsManager.IsWeightless(transform.GridPosition);

            if (weightless && collider != null)
            {
                // No gravity: is our entity touching anything?
                var touching = IsAroundCollider(transform, mover, collider);

                if (!touching)
                {
                    return;
                }
            }

            if (mover.VelocityDir.LengthSquared < 0.001 || !ActionBlockerSystem.CanMove(mover.Owner) && !weightless)
            {
                (physics.Controller as MoverController)?.StopMoving();
            }
            else
            {
                if (weightless)
                {
                    (physics.Controller as MoverController)?.Push(mover.VelocityDir, mover.CurrentPushSpeed);
                    transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle();
                    return;
                }
                (physics.Controller as MoverController)?.Move(mover.VelocityDir,
                                                              mover.Sprinting ? mover.CurrentSprintSpeed : mover.CurrentWalkSpeed);
                transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle();

                // Handle footsteps.
                if (_mapManager.GridExists(mover.LastPosition.GridID))
                {
                    // Can happen when teleporting between grids.
                    var distance = transform.GridPosition.Distance(_mapManager, mover.LastPosition);
                    mover.StepSoundDistance += distance;
                }

                mover.LastPosition = transform.GridPosition;
                float distanceNeeded;
                if (mover.Sprinting)
                {
                    distanceNeeded = StepSoundMoveDistanceRunning;
                }
                else
                {
                    distanceNeeded = StepSoundMoveDistanceWalking;
                }
                if (mover.StepSoundDistance > distanceNeeded)
                {
                    mover.StepSoundDistance = 0;

                    if (!mover.Owner.HasComponent <FootstepSoundComponent>())
                    {
                        return;
                    }

                    if (mover.Owner.TryGetComponent <InventoryComponent>(out var inventory) &&
                        inventory.TryGetSlotItem <ItemComponent>(EquipmentSlotDefines.Slots.SHOES, out var item) &&
                        item.Owner.TryGetComponent <FootstepModifierComponent>(out var modifier))
                    {
                        modifier.PlayFootstep();
                    }
                    else
                    {
                        PlayFootstepSound(transform.GridPosition);
                    }
                }
            }
Пример #6
0
        public bool TryPoint(ICommonSession?session, GridCoordinates coords, EntityUid uid)
        {
            var player = session?.AttachedEntity;

            if (player == null)
            {
                return(false);
            }

            if (_pointers.TryGetValue(session !, out var lastTime) &&
                _gameTiming.CurTime < lastTime + PointDelay)
            {
                return(false);
            }

            if (!InRange(coords, player.Transform.GridPosition))
            {
                player.PopupMessage(player, Loc.GetString("You can't reach there!"));
                return(false);
            }

            if (ActionBlockerSystem.CanChangeDirection(player))
            {
                var diff = coords.ToMapPos(_mapManager) - player.Transform.MapPosition.Position;
                if (diff.LengthSquared > 0.01f)
                {
                    player.Transform.LocalRotation = new Angle(diff);
                }
            }

            var viewers = _playerManager.GetPlayersInRange(player.Transform.GridPosition, 15);

            EntityManager.SpawnEntity("pointingarrow", coords);

            string selfMessage;
            string viewerMessage;
            string?viewerPointedAtMessage = null;

            if (EntityManager.TryGetEntity(uid, out var pointed))
            {
                selfMessage = player == pointed
                    ? Loc.GetString("You point at yourself.")
                    : Loc.GetString("You point at {0:theName}.", pointed);

                viewerMessage = player == pointed
                    ? $"{player.Name} {Loc.GetString("points at {0:themself}.", player)}"
                    : $"{player.Name} {Loc.GetString("points at {0:theName}.", pointed)}";

                viewerPointedAtMessage = $"{player.Name} {Loc.GetString("points at you.")}";
            }
            else
            {
                var tileRef = _mapManager.GetGrid(coords.GridID).GetTileRef(coords);
                var tileDef = _tileDefinitionManager[tileRef.Tile.TypeId];

                selfMessage = Loc.GetString("You point at {0}.", tileDef.DisplayName);

                viewerMessage = $"{player.Name} {Loc.GetString("points at {0}.", tileDef.DisplayName)}";
            }

            _pointers[session !] = _gameTiming.CurTime;
Пример #7
0
        public bool TryPoint(ICommonSession?session, GridCoordinates coords, EntityUid uid)
        {
            var player = (session as IPlayerSession)?.ContentData()?.Mind?.CurrentEntity;

            if (player == null)
            {
                return(false);
            }

            if (_pointers.TryGetValue(session !, out var lastTime) &&
                _gameTiming.CurTime < lastTime + PointDelay)
            {
                return(false);
            }

            if (EntityManager.TryGetEntity(uid, out var entity) && entity.HasComponent <PointingArrowComponent>())
            {
                // this is a pointing arrow. no pointing here...
                return(false);
            }

            if (!InRange(coords, player.Transform.GridPosition))
            {
                player.PopupMessage(Loc.GetString("You can't reach there!"));
                return(false);
            }

            if (ActionBlockerSystem.CanChangeDirection(player))
            {
                var diff = coords.ToMapPos(_mapManager) - player.Transform.MapPosition.Position;
                if (diff.LengthSquared > 0.01f)
                {
                    player.Transform.LocalRotation = new Angle(diff);
                }
            }

            var arrow = EntityManager.SpawnEntity("pointingarrow", coords);

            var layer = (int)VisibilityFlags.Normal;

            if (player.TryGetComponent(out VisibilityComponent? playerVisibility))
            {
                var arrowVisibility = arrow.EnsureComponent <VisibilityComponent>();
                layer = arrowVisibility.Layer = playerVisibility.Layer;
            }

            // Get players that are in range and whose visibility layer matches the arrow's.
            var viewers = _playerManager.GetPlayersBy((playerSession) =>
            {
                if ((playerSession.VisibilityMask & layer) == 0)
                {
                    return(false);
                }

                var ent = playerSession.ContentData()?.Mind?.CurrentEntity;

                return(ent != null &&
                       ent.Transform.MapPosition.InRange(player.Transform.MapPosition, PointingRange));
            });

            string selfMessage;
            string viewerMessage;
            string?viewerPointedAtMessage = null;

            if (EntityManager.TryGetEntity(uid, out var pointed))
            {
                selfMessage = player == pointed
                    ? Loc.GetString("You point at yourself.")
                    : Loc.GetString("You point at {0:theName}.", pointed);

                viewerMessage = player == pointed
                    ? $"{player.Name} {Loc.GetString("points at {0:themself}.", player)}"
                    : $"{player.Name} {Loc.GetString("points at {0:theName}.", pointed)}";

                viewerPointedAtMessage = $"{player.Name} {Loc.GetString("points at you.")}";
            }
            else
            {
                var tileRef = _mapManager.GetGrid(coords.GridID).GetTileRef(coords);
                var tileDef = _tileDefinitionManager[tileRef.Tile.TypeId];

                selfMessage = Loc.GetString("You point at {0}.", tileDef.DisplayName);

                viewerMessage = $"{player.Name} {Loc.GetString("points at {0}.", tileDef.DisplayName)}";
            }

            _pointers[session !] = _gameTiming.CurTime;
Пример #8
0
        private void UpdateKinematics(ITransformComponent transform, IMoverComponent mover, PhysicsComponent physics, CollidableComponent collider = null)
        {
            bool weightless = false;

            var tile = _mapManager.GetGrid(transform.GridID).GetTileRef(transform.GridPosition).Tile;

            if ((!_mapManager.GetGrid(transform.GridID).HasGravity || tile.IsEmpty) && collider != null)
            {
                weightless = true;
                // No gravity: is our entity touching anything?
                var touching = false;
                foreach (var entity in _entityManager.GetEntitiesInRange(transform.Owner, mover.GrabRange, true))
                {
                    if (entity.TryGetComponent <CollidableComponent>(out var otherCollider))
                    {
                        if (otherCollider.Owner == transform.Owner)
                        {
                            continue;                                         // Don't try to push off of yourself!
                        }
                        touching |= ((collider.CollisionMask & otherCollider.CollisionLayer) != 0x0 ||
                                     (otherCollider.CollisionMask & collider.CollisionLayer) != 0x0) && // Ensure collision
                                    !entity.HasComponent <ItemComponent>();   // This can't be an item
                    }
                }
                if (!touching)
                {
                    return;
                }
            }
            if (mover.VelocityDir.LengthSquared < 0.001 || !ActionBlockerSystem.CanMove(mover.Owner))
            {
                if (physics.LinearVelocity != Vector2.Zero)
                {
                    physics.LinearVelocity = Vector2.Zero;
                }
            }
            else
            {
                if (weightless)
                {
                    physics.LinearVelocity  = mover.VelocityDir * mover.CurrentPushSpeed;
                    transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle();
                    return;
                }

                physics.LinearVelocity  = mover.VelocityDir * (mover.Sprinting ? mover.CurrentSprintSpeed : mover.CurrentWalkSpeed);
                transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle();

                // Handle footsteps.
                if (_mapManager.GridExists(mover.LastPosition.GridID))
                {
                    // Can happen when teleporting between grids.
                    var distance = transform.GridPosition.Distance(_mapManager, mover.LastPosition);
                    mover.StepSoundDistance += distance;
                }

                mover.LastPosition = transform.GridPosition;
                float distanceNeeded;
                if (mover.Sprinting)
                {
                    distanceNeeded = StepSoundMoveDistanceRunning;
                }
                else
                {
                    distanceNeeded = StepSoundMoveDistanceWalking;
                }
                if (mover.StepSoundDistance > distanceNeeded)
                {
                    mover.StepSoundDistance = 0;

                    if (!mover.Owner.HasComponent <FootstepSoundComponent>())
                    {
                        return;
                    }

                    if (mover.Owner.TryGetComponent <InventoryComponent>(out var inventory) &&
                        inventory.TryGetSlotItem <ItemComponent>(EquipmentSlotDefines.Slots.SHOES, out var item) &&
                        item.Owner.TryGetComponent <FootstepModifierComponent>(out var modifier))
                    {
                        modifier.PlayFootstep();
                    }
                    else
                    {
                        PlayFootstepSound(transform.GridPosition);
                    }
                }
            }