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; }
public void SharedValidatePlacement( ICharacter character, Vector2Ushort targetPosition, bool logErrors, out bool canPlace, out bool isTooFar, out object errorCodeOrMessage) { if (NewbieProtectionSystem.SharedIsNewbie(character)) { if (logErrors) { NewbieProtectionSystem.SharedNotifyNewbieCannotPerformAction(character, this); } canPlace = false; isTooFar = false; errorCodeOrMessage = null; return; } // check whether somebody nearby is already placing a bomb there var tempCharactersNearby = Api.Shared.GetTempList <ICharacter>(); if (IsServer) { Server.World.GetScopedByPlayers(character, tempCharactersNearby); } else { Client.Characters.GetKnownPlayerCharacters(tempCharactersNearby); } foreach (var otherCharacter in tempCharactersNearby.AsList()) { if (otherCharacter != character && otherCharacter.IsInitialized && PlayerCharacter.GetPublicState(otherCharacter).CurrentPublicActionState is ItemExplosiveActionPublicState explosiveActionState && explosiveActionState.TargetPosition == targetPosition) { // someone is already planting a bomb here canPlace = false; isTooFar = false; errorCodeOrMessage = null; 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 obstaclesInTheWay = physicsSpace.TestLine( characterCenter, toPosition, CollisionGroup.Default, sendDebugEvent: false); foreach (var test in obstaclesInTheWay.AsList()) { var testPhysicsBody = test.PhysicsBody; if (testPhysicsBody.AssociatedProtoTile is not null) { // obstacle tile on the way return(true); } var testWorldObject = testPhysicsBody.AssociatedWorldObject; if (testWorldObject is null) { // some barrier on the way return(true); } 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); } if (!this.ObjectExplosiveProto.CheckTileRequirements(targetPosition, character, out errorCodeOrMessage, logErrors)) { // explosive static object placement requirements failed canPlace = false; isTooFar = false; return; } // let's check whether there are any obstacles by casting rays // from character's center to the center of the planted bomb if (TestHasObstacle(worldObjectCenter)) { // has obstacle if (logErrors) { if (IsClient) { this.ClientShowCannotPlaceObstaclesInTheWayNotification(); } else { Logger.Warning($"{character} cannot place {this} - obstacles in the way"); this.CallClient(character, _ => _.ClientRemote_CannotPlaceObstacles()); } } canPlace = false; isTooFar = false; errorCodeOrMessage = CoreStrings.Notification_ObstaclesOnTheWay; return; } // validate distance to the character if (this.SharedIsTooFarToPlace(character, targetPosition, logErrors)) { canPlace = true; isTooFar = true; return; } canPlace = true; isTooFar = false; }