private static bool SharedIsWithinInteractionDistance(
            ICharacter character,
            Vector2Ushort tilePosition,
            out bool obstaclesOnTheWay)
        {
            var interactionAreaShape = character.PhysicsBody.Shapes.FirstOrDefault(
                s => s.CollisionGroup == CollisionGroups.CharacterInteractionArea);

            if (interactionAreaShape is null)
            {
                // no interaction area shape (probably a spectator character)
                obstaclesOnTheWay = false;
                return(false);
            }

            var penetration = character.PhysicsBody.PhysicsSpace.TestShapeCollidesWithShape(
                sourceShape: interactionAreaShape,
                targetShape: new CircleShape(
                    center: tilePosition.ToVector2D() + (0.5, 0.5),
                    radius: ClickAreaRadius,
                    collisionGroup: CollisionGroups.ClickArea),
                sourceShapeOffset: character.PhysicsBody.Position);

            if (!penetration.HasValue)
            {
                // outside of interaction area
                obstaclesOnTheWay = false;
                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 = (Vector2D)tilePosition + new Vector2D(0.5, 0.5);
            var worldObjectPointClosestToCharacter = new BoundsInt(tilePosition, Vector2Int.One)
                                                     .ClampInside(characterCenter);

            obstaclesOnTheWay = ObstacleTestHelper.SharedHasObstaclesOnTheWay(characterCenter,
                                                                              physicsSpace,
                                                                              worldObjectCenter,
                                                                              worldObjectPointClosestToCharacter,
                                                                              sendDebugEvents: false);

            return(!obstaclesOnTheWay);
        }
Пример #2
0
        private bool ServerCheckIsDoorShouldBeOpened(
            IStaticWorldObject worldObject,
            TPrivateState privateState)
        {
            if (privateState.IsBlockedByShield)
            {
                return(false);
            }

            var mode = privateState.AccessMode;

            if (mode == WorldObjectAccessMode.Closed)
            {
                return(false);
            }

            Server.World.GetInViewScopeByPlayers(worldObject, StaticTempCharactersNearby);
            if (StaticTempCharactersNearby.Count == 0)
            {
                // no characters nearby
                return(false);
            }

            var objectOpeningBounds = this.SharedGetDoorOpeningBounds(worldObject);

            foreach (var character in StaticTempCharactersNearby)
            {
                if (!character.ServerIsOnline ||
                    CharacterIdleSystem.ServerIsIdlePlayer(character) ||
                    character.ProtoCharacter is PlayerCharacterSpectator)
                {
                    continue;
                }

                if (!objectOpeningBounds.Contains(character.Position))
                {
                    // too far from this door
                    continue;
                }

                if (!WorldObjectAccessModeSystem.ServerHasAccess(worldObject,
                                                                 character,
                                                                 mode,
                                                                 writeToLog: false))
                {
                    continue;
                }

                // we don't do this check because it requires character to be the door owner
                //if (!this.SharedCanInteract(character, gameObject, writeToLog: false))
                //{
                //    return false;
                //}

                // we do this check instead:
                // ensure the character is alive and there is a direct line of sight between the character and the door
                var characterPublicState = character.GetPublicState <ICharacterPublicState>();
                if (characterPublicState.IsDead)
                {
                    // dead
                    continue;
                }

                if (!this.IsHeavyVehicleCanPass &&
                    characterPublicState is PlayerCharacterPublicState playerCharacterPublicState &&
                    playerCharacterPublicState.CurrentVehicle?.ProtoGameObject is IProtoVehicle protoVehicle &&
                    protoVehicle.IsHeavyVehicle)
                {
                    // in a heavy vehicle and cannot pass
                    continue;
                }

                var characterPhysicsBody = character.PhysicsBody;
                var characterCenter      = character.Position + characterPhysicsBody.CenterOffset;
                if (!ObstacleTestHelper.SharedHasObstaclesOnTheWay(characterCenter,
                                                                   characterPhysicsBody.PhysicsSpace,
                                                                   worldObject,
                                                                   sendDebugEvents: true))
                {
                    return(true);
                }
            }

            // the door should be closed
            return(false);
        }
Пример #3
0
        public static bool SharedCheckCanInteract(
            ICharacter character,
            IDynamicWorldObject vehicle,
            bool writeToLog)
        {
            if (vehicle is null ||
                vehicle.IsDestroyed)
            {
                return(false);
            }

            // it's possible to repair any vehicle within a certain distance to the character
            var canInteract = character.Position.DistanceSquaredTo(vehicle.Position)
                              <= MaxDistanceForRepairAction * MaxDistanceForRepairAction;

            if (!canInteract)
            {
                if (writeToLog)
                {
                    Logger.Warning(
                        $"Character cannot interact with {vehicle} for repair - too far",
                        character);

                    if (IsClient)
                    {
                        CannotInteractMessageDisplay.ClientOnCannotInteract(vehicle,
                                                                            CoreStrings.Notification_TooFar,
                                                                            isOutOfRange: true);
                    }
                }

                return(false);
            }

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

            if (ObstacleTestHelper.SharedHasObstaclesInTheWay(characterCenter,
                                                              physicsSpace,
                                                              vehicle,
                                                              sendDebugEvents: writeToLog))
            {
                if (writeToLog)
                {
                    Logger.Warning(
                        $"Character cannot interact with {vehicle} for repair - obstacles in the way",
                        character);

                    if (IsClient)
                    {
                        CannotInteractMessageDisplay.ClientOnCannotInteract(vehicle,
                                                                            CoreStrings.Notification_ObstaclesOnTheWay,
                                                                            isOutOfRange: true);
                    }
                }

                return(false);
            }

            using var tempCharactersNearby = Api.Shared.GetTempList <ICharacter>();
            if (IsClient)
            {
                Client.Characters.GetKnownPlayerCharacters(tempCharactersNearby);
            }
            else
            {
                Server.World.GetScopedByPlayers(vehicle, tempCharactersNearby);
            }

            foreach (var otherPlayerCharacter in tempCharactersNearby.AsList())
            {
                if (ReferenceEquals(character, otherPlayerCharacter))
                {
                    continue;
                }

                if (PlayerCharacter.GetPublicState(otherPlayerCharacter).CurrentPublicActionState
                    is VehicleRepairActionState.PublicState repairActionState &&
                    ReferenceEquals(repairActionState.TargetWorldObject, vehicle))
                {
                    // already repairing by another player
                    if (!writeToLog)
                    {
                        return(false);
                    }

                    Logger.Important($"Cannot start repairing {vehicle} - already repairing by another player",
                                     character);
                    if (IsClient)
                    {
                        NotificationSystem.ClientShowNotification(
                            CoreStrings.Notification_ErrorCannotInteract,
                            CoreStrings.Notification_ErrorObjectUsedByAnotherPlayer,
                            NotificationColor.Bad,
                            icon: ((IProtoVehicle)vehicle.ProtoGameObject).Icon);
                    }

                    return(false);
                }
            }

            return(true);
        }
Пример #4
0
        /// <summary>
        /// Check if the character's interaction area collides with the world object click area.
        /// The character also should not be dead.
        /// </summary>
        public virtual 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);
            }

            if (character.GetPublicState <ICharacterPublicState>().IsDead ||
                IsServer && !character.ServerIsOnline)
            {
                if (writeToLog)
                {
                    Logger.Warning(
                        $"Character cannot interact with {worldObject} - character is dead or offline.",
                        character);
                }

                return(false);
            }

            bool isInsideInteractionArea;

            if (worldObject.PhysicsBody.HasShapes &&
                worldObject.PhysicsBody.HasAnyShapeCollidingWithGroup(CollisionGroups.ClickArea))
            {
                // check that the world object is inside the interaction area of the character
                using var objectsInCharacterInteractionArea
                          = InteractionCheckerSystem.SharedGetTempObjectsInCharacterInteractionArea(
                                character,
                                writeToLog,
                                requiredCollisionGroup);

                isInsideInteractionArea = false;
                if (objectsInCharacterInteractionArea is not null)
                {
                    foreach (var t in objectsInCharacterInteractionArea.AsList())
                    {
                        if (!ReferenceEquals(worldObject, t.PhysicsBody.AssociatedWorldObject))
                        {
                            continue;
                        }

                        isInsideInteractionArea = true;
                        break;
                    }
                }
            }
            else if (worldObject.ProtoWorldObject is IProtoStaticWorldObject protoStaticWorldObject)
            {
                // the world object doesn't have click area collision 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 protoStaticWorldObject.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;
                }
            }
            else
            {
                isInsideInteractionArea = false;
            }

            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)
                    {
                        ClientOnCannotInteract(worldObject, CoreStrings.Notification_TooFar, isOutOfRange: true);
                    }
                }

                return(false);
            }

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

            // 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;

            if (!ObstacleTestHelper.SharedHasObstaclesOnTheWay(characterCenter,
                                                               physicsSpace,
                                                               worldObject,
                                                               sendDebugEvents: writeToLog))
            {
                return(true);
            }

            if (writeToLog)
            {
                Logger.Warning(
                    $"Character cannot interact with {worldObject} - there are other objects on the way.",
                    character);

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

            return(false);
        }