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