using System.Numerics; using Content.Client.Gameplay; using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.RCD.Components; using Content.Shared.RCD.Systems; using Robust.Client.Placement; using Robust.Client.Player; using Robust.Client.State; using Robust.Shared.Map; using Robust.Shared.Map.Components; namespace Content.Client.RCD; public sealed class AlignRCDConstruction : PlacementMode { [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; private readonly SharedMapSystem _mapSystem; private readonly RCDSystem _rcdSystem; private readonly SharedTransformSystem _transformSystem; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IStateManager _stateManager = default!; private const float SearchBoxSize = 2f; private const float PlaceColorBaseAlpha = 0.5f; private EntityCoordinates _unalignedMouseCoords = default; /// /// This placement mode is not on the engine because it is content specific (i.e., for the RCD) /// public AlignRCDConstruction(PlacementManager pMan) : base(pMan) { IoCManager.InjectDependencies(this); _mapSystem = _entityManager.System(); _rcdSystem = _entityManager.System(); _transformSystem = _entityManager.System(); ValidPlaceColor = ValidPlaceColor.WithAlpha(PlaceColorBaseAlpha); } public override void AlignPlacementMode(ScreenCoordinates mouseScreen) { _unalignedMouseCoords = ScreenToCursorGrid(mouseScreen); MouseCoords = _unalignedMouseCoords.AlignWithClosestGridTile(SearchBoxSize, _entityManager, _mapManager); var gridId = _transformSystem.GetGrid(MouseCoords); if (!_entityManager.TryGetComponent(gridId, out var mapGrid)) return; CurrentTile = _mapSystem.GetTileRef(gridId.Value, mapGrid, MouseCoords); float tileSize = mapGrid.TileSize; GridDistancing = tileSize; if (pManager.CurrentPermission!.IsTile) { MouseCoords = new EntityCoordinates(MouseCoords.EntityId, new Vector2(CurrentTile.X + tileSize / 2, CurrentTile.Y + tileSize / 2)); } else { MouseCoords = new EntityCoordinates(MouseCoords.EntityId, new Vector2(CurrentTile.X + tileSize / 2 + pManager.PlacementOffset.X, CurrentTile.Y + tileSize / 2 + pManager.PlacementOffset.Y)); } } public override bool IsValidPosition(EntityCoordinates position) { var player = _playerManager.LocalSession?.AttachedEntity; // If the destination is out of interaction range, set the placer alpha to zero if (!_entityManager.TryGetComponent(player, out var xform)) return false; if (!_transformSystem.InRange(xform.Coordinates, position, SharedInteractionSystem.InteractionRange)) { InvalidPlaceColor = InvalidPlaceColor.WithAlpha(0); return false; } // Otherwise restore the alpha value else { InvalidPlaceColor = InvalidPlaceColor.WithAlpha(PlaceColorBaseAlpha); } // Determine if player is carrying an RCD in their active hand if (!_entityManager.TryGetComponent(player, out var hands)) return false; var heldEntity = hands.ActiveHand?.HeldEntity; if (!_entityManager.TryGetComponent(heldEntity, out var rcd)) return false; var gridUid = _transformSystem.GetGrid(position); if (!_entityManager.TryGetComponent(gridUid, out var mapGrid)) return false; var tile = _mapSystem.GetTileRef(gridUid.Value, mapGrid, position); var posVector = _mapSystem.TileIndicesFor(gridUid.Value, mapGrid, position); // Determine if the user is hovering over a target var currentState = _stateManager.CurrentState; if (currentState is not GameplayStateBase screen) return false; var target = screen.GetClickedEntity(_transformSystem.ToMapCoordinates(_unalignedMouseCoords)); // Determine if the RCD operation is valid or not if (!_rcdSystem.IsRCDOperationStillValid(heldEntity.Value, rcd, gridUid.Value, mapGrid, tile, posVector, target, player.Value, false)) return false; return true; } }