using Content.Shared.Hands.Components; using Content.Shared.Input; using Content.Shared.Interaction; using Content.Shared.RCD; using Content.Shared.RCD.Components; using Content.Shared.RCD.Systems; using Robust.Client.Placement; using Robust.Client.Player; using Robust.Shared.Enums; using Robust.Shared.Input; using Robust.Shared.Input.Binding; namespace Content.Client.RCD; public sealed class RCDConstructionGhostSystem : EntitySystem { [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly RCDSystem _rcdSystem = default!; [Dependency] private readonly IPlacementManager _placementManager = default!; private string _placementMode = typeof(AlignRCDConstruction).Name; private Direction _placementDirection = default; private bool _useMirrorPrototype = false; public event EventHandler? FlipConstructionPrototype; public override void Initialize() { base.Initialize(); // bind key CommandBinds.Builder .Bind(ContentKeyFunctions.EditorFlipObject, new PointerInputCmdHandler(HandleFlip, outsidePrediction: true)) .Register(); } public override void Shutdown() { CommandBinds.Unregister(); base.Shutdown(); } private bool HandleFlip(in PointerInputCmdHandler.PointerInputCmdArgs args) { if (args.State == BoundKeyState.Down) { if (!_placementManager.IsActive || _placementManager.Eraser) return false; var placerEntity = _placementManager.CurrentPermission?.MobUid; if (!TryComp(placerEntity, out var rcd) || string.IsNullOrEmpty(rcd.CachedPrototype.MirrorPrototype)) return false; _useMirrorPrototype = !rcd.UseMirrorPrototype; var useProto = _useMirrorPrototype ? rcd.CachedPrototype.MirrorPrototype : rcd.CachedPrototype.Prototype; CreatePlacer(placerEntity.Value, rcd, useProto); // tell the server RaiseNetworkEvent(new RCDConstructionGhostFlipEvent(GetNetEntity(placerEntity.Value), _useMirrorPrototype)); } return true; } public override void Update(float frameTime) { base.Update(frameTime); // Get current placer data var placerEntity = _placementManager.CurrentPermission?.MobUid; var placerProto = _placementManager.CurrentPermission?.EntityType; var placerIsRCD = HasComp(placerEntity); // Exit if erasing or the current placer is not an RCD (build mode is active) if (_placementManager.Eraser || (placerEntity != null && !placerIsRCD)) return; // Determine if player is carrying an RCD in their active hand var player = _playerManager.LocalSession?.AttachedEntity; if (!TryComp(player, out var hands)) return; var heldEntity = hands.ActiveHand?.HeldEntity; if (!TryComp(heldEntity, out var rcd)) { // If the player was holding an RCD, but is no longer, cancel placement if (placerIsRCD) _placementManager.Clear(); return; } // Update the direction the RCD prototype based on the placer direction if (_placementDirection != _placementManager.Direction) { _placementDirection = _placementManager.Direction; RaiseNetworkEvent(new RCDConstructionGhostRotationEvent(GetNetEntity(heldEntity.Value), _placementDirection)); } // If the placer has not changed build it. _rcdSystem.UpdateCachedPrototype(heldEntity.Value, rcd); var useProto = (_useMirrorPrototype && !string.IsNullOrEmpty(rcd.CachedPrototype.MirrorPrototype)) ? rcd.CachedPrototype.MirrorPrototype : rcd.CachedPrototype.Prototype; bool needNewPlacer = heldEntity != placerEntity; bool needNewPrototypeUpdate = useProto != placerProto; if (needNewPlacer || needNewPrototypeUpdate) CreatePlacer(heldEntity.Value, rcd, useProto); } private void CreatePlacer(EntityUid uid, RCDComponent component, string? prototype) { // Create a new placer var newObjInfo = new PlacementInformation { MobUid = uid, PlacementOption = _placementMode, EntityType = prototype, Range = (int) Math.Ceiling(SharedInteractionSystem.InteractionRange), IsTile = (component.CachedPrototype.Mode == RcdMode.ConstructTile), UseEditorContext = false, }; _placementManager.Clear(); _placementManager.BeginPlacing(newObjInfo); } }