Exemple #1
0
        private static void Initialize()
        {
            if (isInitialized)
            {
                throw new Exception("Already initialized");
            }

            isInitialized = true;

            defaultGroup = CollisionGroup.GetDefault();
            defaultGroup.SetCollidesWith(defaultGroup);

            hitboxMelee = new CollisionGroup("Hitbox Melee", isSensor: true);
            //hitboxMelee.SetCollidesWith(defaultGroup);
            hitboxMelee.SetCollidesWith(HitboxMelee);

            hitboxRanged = new CollisionGroup("Hitbox Ranged", isSensor: true);
            //hitboxRanged.SetCollidesWith(defaultGroup);
            hitboxRanged.SetCollidesWith(HitboxRanged);

            characterInteractionArea = new CollisionGroup("Interaction Area", isSensor: true);

            clickArea = new CollisionGroup("Click Area", isSensor: true);
            clickArea.SetCollidesWith(clickArea);
            clickArea.SetCollidesWith(characterInteractionArea);
        }
        private static bool AnyObstaclesBetween(ICharacter npc, ICharacter player)
        {
            var physicsSpace          = npc.PhysicsBody.PhysicsSpace;
            var npcCharacterCenter    = npc.Position + npc.PhysicsBody.CenterOffset;
            var playerCharacterCenter = player.Position + player.PhysicsBody.CenterOffset;

            using var obstaclesOnTheWay = physicsSpace.TestLine(
                      npcCharacterCenter,
                      playerCharacterCenter,
                      CollisionGroup.GetDefault(),
                      sendDebugEvent: false);

            foreach (var test in obstaclesOnTheWay.AsList())
            {
                var testPhysicsBody = test.PhysicsBody;
                if (testPhysicsBody.AssociatedProtoTile is null)
                {
                    continue;
                }

                var tile = ServerWorldService.GetTile(testPhysicsBody.Position.ToVector2Ushort());
                if (!tile.IsSlope)
                {
                    // cliff tile on the way
                    return(true);
                }
            }

            return(false);
        }
        private static bool AnyObstaclesBetween(ICharacter npc, ICharacter player)
        {
            var physicsSpace          = npc.PhysicsBody.PhysicsSpace;
            var npcCharacterCenter    = npc.Position + npc.PhysicsBody.CenterOffset;
            var playerCharacterCenter = player.Position + player.PhysicsBody.CenterOffset;

            using var obstaclesOnTheWay = physicsSpace.TestLine(
                      npcCharacterCenter,
                      playerCharacterCenter,
                      CollisionGroup.GetDefault(),
                      sendDebugEvent: false);
            foreach (var test in obstaclesOnTheWay)
            {
                var testPhysicsBody = test.PhysicsBody;
                if (testPhysicsBody.AssociatedProtoTile != null)
                {
                    // obstacle tile on the way
                    return(true);
                }

                var testWorldObject = testPhysicsBody.AssociatedWorldObject;
                if (testWorldObject == npc ||
                    testWorldObject == player)
                {
                    // not an obstacle - it's one of the characters
                    continue;
                }

                // obstacle object on the way
                return(true);
            }

            return(false);
        }
Exemple #4
0
        private static bool SharedHasObstacle(
            ICharacter character,
            IStaticWorldObject forStructureRelocation,
            Vector2D position)
        {
            if (PveSystem.SharedIsPve(false))
            {
                return(false);
            }

            var characterCenter = character.Position + character.PhysicsBody.CenterOffset;

            return(TestHasObstacle(position));

            // local method for testing if there is an obstacle from current to the specified position
            bool TestHasObstacle(Vector2D toPosition)
            {
                using var obstaclesOnTheWay = character.PhysicsBody.PhysicsSpace.TestLine(
                          characterCenter,
                          toPosition,
                          CollisionGroup.GetDefault(),
                          sendDebugEvent: false);
                foreach (var test in obstaclesOnTheWay.AsList())
                {
                    var testPhysicsBody = test.PhysicsBody;
                    if (testPhysicsBody.AssociatedProtoTile is not null)
                    {
                        // obstacle tile on the way
                        return(true);
                    }

                    var testWorldObject = testPhysicsBody.AssociatedWorldObject;
                    if (ReferenceEquals(testWorldObject, character) ||
                        ReferenceEquals(testWorldObject, forStructureRelocation))
                    {
                        // not an obstacle - it's the character or world object itself
                        continue;
                    }

                    switch (testWorldObject.ProtoWorldObject)
                    {
                    case IProtoObjectDeposit _:     // allow deposits
                    case ObjectWallDestroyed _:     // allow destroyed walls
                        continue;
                    }

                    // obstacle object on the way
                    return(true);
                }

                // no obstacles
                return(false);
            }
        }
        private static Vector2D FindClosestPosition(Vector2D position)
        {
            var physicsSpace   = ServerWorldService.GetPhysicsSpace();
            var collisionGroup = CollisionGroup.GetDefault();

            if (IsValidPosition(
                    // round position
                    position.ToVector2Ushort().ToVector2D()))
            {
                // can teleport right there
                return(position);
            }

            // check nearby tiles
            var tile = ServerWorldService.GetTile(position.ToVector2Ushort());

            foreach (var neighborTile in tile.EightNeighborTiles)
            {
                if (!neighborTile.IsValidTile)
                {
                    continue;
                }

                var neighborTilePosition = neighborTile.Position.ToVector2D();
                if (IsValidPosition(neighborTilePosition))
                {
                    return(neighborTilePosition);
                }
            }

            throw new Exception("No empty position available nearby.");

            // Local function for checking if the position is valid.
            bool IsValidPosition(Vector2D pos)
            {
                using var objectsNearby = physicsSpace.TestRectangle(
                          // include some padding, otherwise the check will include border-objects
                          position: pos + (0.01, 0.01),
                          size: (0.98, 0.98),
                          collisionGroup: collisionGroup,
                          sendDebugEvent: false);
                if (objectsNearby.Count > 0)
                {
                    return(false);
                }

                var posTile = Server.World.GetTile(pos.ToVector2Ushort());

                return(ServerCharacterSpawnHelper.IsValidSpawnTile(posTile,
                                                                   checkNeighborTiles: false));
            }
        }
Exemple #6
0
        private static void ServerTeleport(ICharacter player, Vector2D toPosition)
        {
            if (player.PhysicsBody.HasAnyShapeCollidingWithGroup(CollisionGroup.GetDefault()))
            {
                // perform position validity check
                toPosition = FindClosestValidPosition(toPosition);
            }
            else
            {
                // This is a character without the physical collider (probably a spectator).
                // It could be teleported anywhere.
            }

            ServerWorldService.SetPosition(player, toPosition);
        }
        /// <summary>
        /// Check if the character's interaction area collides with the world object click area.
        /// The character also should not be dead.
        /// </summary>
        public bool SharedIsInsideCharacterInteractionArea(
            ICharacter character,
            TWorldObject worldObject,
            bool writeToLog,
            CollisionGroup requiredCollisionGroup = null)
        {
            if (worldObject.IsDestroyed)
            {
                return(false);
            }

            try
            {
                this.VerifyGameObject(worldObject);
            }
            catch (Exception ex)
            {
                if (writeToLog)
                {
                    Logger.Exception(ex);
                }
                else
                {
                    Logger.Warning(ex.Message + " during " + nameof(SharedIsInsideCharacterInteractionArea));
                }

                return(false);
            }

            bool isInsideInteractionArea;

            if (worldObject.PhysicsBody.HasShapes)
            {
                // check that the world object is inside the interaction area of the character
                using (var objectsInCharacterInteractionArea
                           = InteractionCheckerSystem.SharedGetTempObjectsInCharacterInteractionArea(
                                 character,
                                 writeToLog,
                                 requiredCollisionGroup))
                {
                    isInsideInteractionArea =
                        objectsInCharacterInteractionArea?.Any(t => t.PhysicsBody.AssociatedWorldObject == worldObject)
                        ?? false;
                }
            }
            else
            {
                // the world object doesn't have physics shapes
                // check this object tile by tile
                // ensure at least one tile of this object is inside the character interaction area
                // ensure there is direct line of sight between player character and this tile

                var characterInteractionAreaShape = character.PhysicsBody.Shapes.FirstOrDefault(
                    s => s.CollisionGroup
                    == CollisionGroups.CharacterInteractionArea);
                isInsideInteractionArea = false;
                foreach (var tileOffset in ((IProtoStaticWorldObject)worldObject.ProtoWorldObject).Layout.TileOffsets)
                {
                    var penetration = character.PhysicsBody.PhysicsSpace.TestShapeCollidesWithShape(
                        sourceShape: characterInteractionAreaShape,
                        targetShape: new RectangleShape(
                            position: (worldObject.TilePosition + tileOffset).ToVector2D()
                            + (0.01, 0.01),
                            size: (0.98, 0.98),
                            collisionGroup: CollisionGroups.ClickArea),
                        sourceShapeOffset: character.PhysicsBody.Position);

                    if (!penetration.HasValue)
                    {
                        // this tile is not inside the character interaction area
                        continue;
                    }

                    // the tile is inside the character interaction area
                    // check that there is a direct line between the character and the tile
                    isInsideInteractionArea = true;
                    break;
                }
            }

            if (!isInsideInteractionArea)
            {
                // the world object is outside the character interaction area
                if (writeToLog)
                {
                    Logger.Warning(
                        $"Character cannot interact with {worldObject} - outside the interaction area.",
                        character);

                    if (IsClient)
                    {
                        this.ClientOnCannotInteract(worldObject, CoreStrings.Notification_TooFar, isOutOfRange: true);
                    }
                }

                return(false);
            }

            // check that there are no other objects on the way between them (defined by default layer)
            var physicsSpace      = character.PhysicsBody.PhysicsSpace;
            var characterCenter   = character.Position + character.PhysicsBody.CenterOffset;
            var worldObjectCenter = worldObject.TilePosition.ToVector2D() + worldObject.PhysicsBody.CenterOffset;
            var worldObjectPointClosestToCharacter = worldObject.PhysicsBody.ClampPointInside(
                characterCenter,
                CollisionGroups.Default,
                out var isSuccess);

            if (!isSuccess)
            {
                // the physics body seems to not have the default collider, let's check for the click area instead
                worldObjectPointClosestToCharacter = worldObject.PhysicsBody.ClampPointInside(
                    characterCenter,
                    CollisionGroups.ClickArea,
                    out _);
            }

            // local method for testing if there is an obstacle from current to the specified position
            bool TestHasObstacle(Vector2D toPosition)
            {
                using (var obstaclesOnTheWay = physicsSpace.TestLine(
                           characterCenter,
                           toPosition,
                           CollisionGroup.GetDefault(),
                           sendDebugEvent: writeToLog))
                {
                    foreach (var test in obstaclesOnTheWay)
                    {
                        var testPhysicsBody = test.PhysicsBody;
                        if (testPhysicsBody.AssociatedProtoTile != null)
                        {
                            // obstacle tile on the way
                            return(true);
                        }

                        var testWorldObject = testPhysicsBody.AssociatedWorldObject;
                        if (testWorldObject == character ||
                            testWorldObject == worldObject)
                        {
                            // not an obstacle - it's the character or world object itself
                            continue;
                        }

                        if (!this.CommonIsAllowedObjectToInteractThrought(testWorldObject))
                        {
                            // obstacle object on the way
                            return(true);
                        }
                    }

                    // no obstacles
                    return(false);
                }
            }

            if (character.ProtoCharacter is PlayerCharacterSpectator)
            {
                // don't test for obstacles for spectator character
                return(true);
            }

            // let's test by casting rays from character center to:
            // 0) world object center
            // 1) world object point closest to the character
            // 2) combined - take X from center, take Y from closest
            // 3) combined - take X from closest, take Y from center
            if (TestHasObstacle(worldObjectCenter) &&
                TestHasObstacle(worldObjectPointClosestToCharacter) &&
                TestHasObstacle((worldObjectCenter.X,
                                 worldObjectPointClosestToCharacter.Y)) &&
                TestHasObstacle((worldObjectPointClosestToCharacter.X, worldObjectCenter.Y)))
            {
                // has obstacle
                if (writeToLog)
                {
                    Logger.Warning(
                        $"Character cannot interact with {worldObject} - there are other objects on the way.",
                        character);

                    if (IsClient)
                    {
                        this.ClientOnCannotInteract(worldObject,
                                                    CoreStrings.Notification_ObstaclesOnTheWay,
                                                    isOutOfRange: true);
                    }
                }

                return(false);
            }

            if (character.GetPublicState <ICharacterPublicState>().IsDead)
            {
                // character is dead
                if (writeToLog)
                {
                    Logger.Warning(
                        $"Character cannot interact with {worldObject} - character is dead.",
                        character);
                }

                return(false);
            }

            return(true);
        }
Exemple #8
0
        public void SharedValidatePlacement(
            ICharacter character,
            Vector2Ushort targetPosition,
            bool logErrors,
            out bool canPlace,
            out bool isTooFar)
        {
            if (NewbieProtectionSystem.SharedIsNewbie(character))
            {
                if (logErrors)
                {
                    Logger.Warning("Newbie cannot plant bombs");
                    NewbieProtectionSystem.SharedNotifyNewbieCannotPerformAction(character, this);
                }

                canPlace = false;
                isTooFar = false;
                return;
            }

            // check if there is a direct line of sight
            // check that there are no other objects on the way between them (defined by default layer)
            var physicsSpace      = character.PhysicsBody.PhysicsSpace;
            var characterCenter   = character.Position + character.PhysicsBody.CenterOffset;
            var worldObjectCenter = targetPosition.ToVector2D() + (0.5, 0.5);

            // local method for testing if there is an obstacle from current to the specified position
            bool TestHasObstacle(Vector2D toPosition)
            {
                using var obstaclesOnTheWay = physicsSpace.TestLine(
                          characterCenter,
                          toPosition,
                          CollisionGroup.GetDefault(),
                          sendDebugEvent: false);
                foreach (var test in obstaclesOnTheWay.AsList())
                {
                    var testPhysicsBody = test.PhysicsBody;
                    if (testPhysicsBody.AssociatedProtoTile != null)
                    {
                        // obstacle tile on the way
                        return(true);
                    }

                    var testWorldObject = testPhysicsBody.AssociatedWorldObject;
                    if (testWorldObject == character)
                    {
                        // not an obstacle - it's the character or world object itself
                        continue;
                    }

                    switch (testWorldObject.ProtoWorldObject)
                    {
                    case IProtoObjectDeposit _:     // allow deposits
                    case ObjectWallDestroyed _:     // allow destroyed walls
                        continue;
                    }

                    // obstacle object on the way
                    return(true);
                }

                // no obstacles
                return(false);
            }

            // let's test by casting rays from character center to the center of the planted bomb
            if (TestHasObstacle(worldObjectCenter))
            {
                // has obstacle
                if (logErrors)
                {
                    if (IsClient)
                    {
                        this.ClientShowCannotPlaceObstaclesOnTheWayNotification();
                    }
                    else
                    {
                        Logger.Warning($"{character} cannot place {this} - obstacles on the way");
                        this.CallClient(character, _ => _.ClientRemote_CannotPlaceObstacles());
                    }
                }

                canPlace = false;
                isTooFar = false;
                return;
            }

            if (!this.ObjectExplosiveProto.CheckTileRequirements(targetPosition,
                                                                 character,
                                                                 logErrors))
            {
                // explosive static object placement requirements failed
                canPlace = false;
                isTooFar = false;
                return;
            }

            // validate distance to the character
            if (this.SharedIsTooFarToPlace(character, targetPosition, logErrors))
            {
                canPlace = true;
                isTooFar = true;
                return;
            }

            canPlace = true;
            isTooFar = false;
        }
        private static bool SharedHasObstaclesOnTheWay(
            ICharacter character,
            Vector2D characterCenter,
            IPhysicsSpace physicsSpace,
            [CanBeNull] IWorldObject worldObject,
            Vector2D worldObjectCenter,
            Vector2D worldObjectPointClosestToCharacter,
            bool sendDebugEvents)
        {
            // let's test by casting rays from character center to:
            // 0) world object center
            // 1) world object point closest to the character
            // 2) combined - take X from center, take Y from closest
            // 3) combined - take X from closest, take Y from center
            if (TestHasObstacle(worldObjectCenter) &&
                TestHasObstacle(worldObjectPointClosestToCharacter) &&
                TestHasObstacle((worldObjectCenter.X,
                                 worldObjectPointClosestToCharacter.Y)) &&
                TestHasObstacle((worldObjectPointClosestToCharacter.X, worldObjectCenter.Y)))
            {
                // has obstacle
                return(true);
            }

            return(false);

            // local method for testing if there is an obstacle from current to the specified position
            bool TestHasObstacle(Vector2D toPosition)
            {
                using var obstaclesOnTheWay = physicsSpace.TestLine(
                          characterCenter,
                          toPosition,
                          CollisionGroup.GetDefault(),
                          sendDebugEvent: sendDebugEvents);
                foreach (var test in obstaclesOnTheWay)
                {
                    var testPhysicsBody = test.PhysicsBody;
                    if (!(testPhysicsBody.AssociatedProtoTile is null))
                    {
                        // obstacle tile on the way
                        return(true);
                    }

                    var testWorldObject = testPhysicsBody.AssociatedWorldObject;
                    if (ReferenceEquals(testWorldObject, worldObject))
                    {
                        // not an obstacle - it's the world object itself
                        continue;
                    }

                    if (testWorldObject is ICharacter)
                    {
                        // characters are not assumed as an obstacle
                        continue;
                    }

                    // no need for this check anymore as we're checking for general "is ICharacter" above
                    //if (ReferenceEquals(testWorldObject, character))
                    //{
                    //    // not an obstacle - it's the player's character itself
                    //    continue;
                    //}

                    if (!testWorldObject.ProtoWorldObject
                        .SharedIsAllowedObjectToInteractThrough(testWorldObject))
                    {
                        // obstacle object on the way
                        return(true);
                    }
                }

                // no obstacles
                return(false);
            }
        }
Exemple #10
0
        public bool SharedValidatePlacement(ICharacter character, Vector2Ushort targetPosition, bool logErrors)
        {
            // check if there is a direct line of sight
            // check that there are no other objects on the way between them (defined by default layer)
            var physicsSpace      = character.PhysicsBody.PhysicsSpace;
            var characterCenter   = character.Position + character.PhysicsBody.CenterOffset;
            var worldObjectCenter = targetPosition.ToVector2D() + (0.5, 0.5);

            // local method for testing if there is an obstacle from current to the specified position
            bool TestHasObstacle(Vector2D toPosition)
            {
                using (var obstaclesOnTheWay = physicsSpace.TestLine(
                           characterCenter,
                           toPosition,
                           CollisionGroup.GetDefault(),
                           sendDebugEvent: false))
                {
                    foreach (var test in obstaclesOnTheWay)
                    {
                        var testPhysicsBody = test.PhysicsBody;
                        if (testPhysicsBody.AssociatedProtoTile != null)
                        {
                            // obstacle tile on the way
                            return(true);
                        }

                        var testWorldObject = testPhysicsBody.AssociatedWorldObject;
                        if (testWorldObject == character)
                        {
                            // not an obstacle - it's the character or world object itself
                            continue;
                        }

                        // obstacle object on the way
                        return(true);
                    }

                    // no obstacles
                    return(false);
                }
            }

            // let's test by casting rays from character center to the center of the planted bomb
            if (TestHasObstacle(worldObjectCenter))
            {
                // has obstacle
                if (logErrors)
                {
                    if (IsClient)
                    {
                        this.ClientShowCannotPlaceObstaclesOnTheWayNotification();
                    }
                    else
                    {
                        Logger.Warning($"{character} cannot place {this} - obstacles on the way");
                        this.CallClient(character, _ => _.ClientRemote_CannotPlaceObstacles());
                    }
                }

                return(false);
            }

            // validate distance to the character
            if (targetPosition.TileDistanceTo(character.TilePosition)
                > this.DeployDistanceMax)
            {
                // distance exceeded - too far
                if (logErrors)
                {
                    if (IsClient)
                    {
                        this.ClientShowCannotPlaceTooFarNotification();
                    }
                    else
                    {
                        Logger.Warning($"{character} cannot place {this} - too far");
                        this.CallClient(character, _ => _.ClientRemote_CannotPlaceTooFar());
                    }
                }

                return(false);
            }

            if (!this.ObjectExplosiveProto.CheckTileRequirements(targetPosition,
                                                                 character,
                                                                 logErrors))
            {
                // explosive static object placement requirements failed
                return(false);
            }

            return(true);
        }