diff --git a/Content.Client/GameObjects/EntitySystems/ConstructionSystem.cs b/Content.Client/GameObjects/EntitySystems/ConstructionSystem.cs index 327511cd82..d776eaf167 100644 --- a/Content.Client/GameObjects/EntitySystems/ConstructionSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/ConstructionSystem.cs @@ -158,11 +158,13 @@ namespace Content.Client.GameObjects.EntitySystems comp.Prototype = prototype; comp.GhostID = _nextId++; ghost.Transform.LocalRotation = dir.ToAngle(); - var sprite = ghost.GetComponent(); - sprite.LayerSetSprite(0, prototype.Icon); - sprite.LayerSetVisible(0, true); - _ghosts.Add(comp.GhostID, comp); + var sprite = ghost.GetComponent(); + sprite.Color = new Color(48, 255, 48, 128); + sprite.AddBlankLayer(0); // There is no way to actually check if this already exists, so we blindly insert a new one + sprite.LayerSetSprite(0, prototype.Icon); + sprite.LayerSetShader(0, "unshaded"); + sprite.LayerSetVisible(0, true); } private void TryStartConstruction(int ghostId) diff --git a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs index 1e2d95ae3c..ca4e650b70 100644 --- a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs @@ -286,7 +286,8 @@ namespace Content.Server.GameObjects.EntitySystems.Click if (item != null) { // After attack: Check if we clicked on an empty location, if so the only interaction we can do is AfterInteract - InteractAfter(player, item, coordinates); + var distSqrt = (playerTransform.WorldPosition - coordinates.ToMapPos(_mapManager)).LengthSquared; + InteractAfter(player, item, coordinates, distSqrt <= InteractionRangeSquared); } return; @@ -330,9 +331,9 @@ namespace Content.Server.GameObjects.EntitySystems.Click /// /// We didn't click on any entity, try doing an AfterInteract on the click location /// - private void InteractAfter(IEntity user, IEntity weapon, GridCoordinates clickLocation) + private void InteractAfter(IEntity user, IEntity weapon, GridCoordinates clickLocation, bool canReach) { - var message = new AfterInteractMessage(user, weapon, null, clickLocation); + var message = new AfterInteractMessage(user, weapon, null, clickLocation, canReach); RaiseLocalEvent(message); if (message.Handled) { @@ -340,7 +341,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click } var afterInteracts = weapon.GetAllComponents().ToList(); - var afterInteractEventArgs = new AfterInteractEventArgs {User = user, ClickLocation = clickLocation}; + var afterInteractEventArgs = new AfterInteractEventArgs {User = user, ClickLocation = clickLocation, CanReach = canReach}; foreach (var afterInteract in afterInteracts) { @@ -380,7 +381,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click } } - var afterAtkMsg = new AfterInteractMessage(user, weapon, attacked, clickLocation); + var afterAtkMsg = new AfterInteractMessage(user, weapon, attacked, clickLocation, true); RaiseLocalEvent(afterAtkMsg); if (afterAtkMsg.Handled) { @@ -391,7 +392,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click var afterAttacks = weapon.GetAllComponents().ToList(); var afterAttackEventArgs = new AfterInteractEventArgs { - User = user, ClickLocation = clickLocation, Target = attacked + User = user, ClickLocation = clickLocation, Target = attacked, CanReach = true }; foreach (var afterAttack in afterAttacks) @@ -687,7 +688,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click } } - var afterAtkMsg = new AfterInteractMessage(user, weapon, attacked, clickLocation); + var afterAtkMsg = new AfterInteractMessage(user, weapon, attacked, clickLocation, false); RaiseLocalEvent(afterAtkMsg); if (afterAtkMsg.Handled) return; @@ -695,7 +696,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click var afterAttacks = weapon.GetAllComponents().ToList(); var afterAttackEventArgs = new AfterInteractEventArgs { - User = user, ClickLocation = clickLocation, Target = attacked + User = user, ClickLocation = clickLocation, Target = attacked, CanReach = false }; //See if we have a ranged attack interaction diff --git a/Content.Server/GameObjects/EntitySystems/ConstructionSystem.cs b/Content.Server/GameObjects/EntitySystems/ConstructionSystem.cs index e4a4393230..e403782fbf 100644 --- a/Content.Server/GameObjects/EntitySystems/ConstructionSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/ConstructionSystem.cs @@ -4,6 +4,7 @@ using Content.Server.GameObjects.Components; using Content.Server.GameObjects.Components.Construction; using Content.Server.GameObjects.Components.Interactable; using Content.Server.GameObjects.Components.Stack; +using Content.Server.GameObjects.EntitySystems.Click; using Content.Server.Interfaces.GameObjects.Components.Interaction; using Content.Server.Interfaces; using Content.Server.Utility; @@ -78,6 +79,10 @@ namespace Content.Server.GameObjects.EntitySystems if(msg.Handled) return; + // You can only construct/deconstruct things within reach + if(!msg.CanReach) + return; + var targetEnt = msg.Attacked; var handEnt = msg.ItemInHand; @@ -85,6 +90,10 @@ namespace Content.Server.GameObjects.EntitySystems if(targetEnt is null || handEnt is null) return; + var interaction = Get(); + if(!interaction.InRangeUnobstructed(handEnt.Transform.MapPosition, targetEnt.Transform.MapPosition, ignoredEnt: targetEnt, ignoreInsideBlocker: true)) + return; + // Cannot deconstruct an entity with no prototype. var targetPrototype = targetEnt.MetaData.EntityPrototype; if (targetPrototype is null) @@ -156,7 +165,7 @@ namespace Content.Server.GameObjects.EntitySystems else // replace ent with intermediate { // Spawn frame - var frame = EntityManager.SpawnEntity("structureconstructionframe", targetEntPos); + var frame = SpawnCopyTransform("structureconstructionframe", targetEnt.Transform); var construction = frame.GetComponent(); SetupComponent(construction, prototype); construction.Stage = prototype.Stages.Count - 2; @@ -185,12 +194,20 @@ namespace Content.Server.GameObjects.EntitySystems } } + private IEntity SpawnCopyTransform(string prototypeId, ITransformComponent toReplace) + { + var frame = EntityManager.SpawnEntity(prototypeId, toReplace.MapPosition); + frame.Transform.WorldRotation = toReplace.WorldRotation; + frame.Transform.ParentUid = toReplace.ParentUid; + return frame; + } + private static void SetupDeconIntermediateSprite(ConstructionComponent constructionComponent, ConstructionPrototype prototype) { if(!constructionComponent.Owner.TryGetComponent(out var spriteComp)) return; - for (var i = prototype.Stages.Count - 1; i < 0; i--) + for (var i = prototype.Stages.Count - 1; i >= 0; i--) { if (prototype.Stages[i].Icon != null) { @@ -331,12 +348,12 @@ namespace Content.Server.GameObjects.EntitySystems if (prototype.Stages.Count == 2) { // Exactly 2 stages, so don't make an intermediate frame. - var ent = EntityManager.SpawnEntity(prototype.Result, placingEnt.Transform.GridPosition); + var ent = SpawnCopyTransform(prototype.Result, placingEnt.Transform); hands.PutInHandOrDrop(ent.GetComponent()); } else { - var frame = EntityManager.SpawnEntity("structureconstructionframe", placingEnt.Transform.GridPosition); + var frame = SpawnCopyTransform("structureconstructionframe", placingEnt.Transform); var construction = frame.GetComponent(); SetupComponent(construction, prototype); @@ -379,7 +396,7 @@ namespace Content.Server.GameObjects.EntitySystems if (constructionComponent.Stage == constructPrototype.Stages.Count - 1) { // Oh boy we get to finish construction! - var ent = EntityManager.SpawnEntity(constructPrototype.Result, transformComponent.GridPosition); + var ent = SpawnCopyTransform(constructPrototype.Result, transformComponent); ent.Transform.LocalRotation = transformComponent.LocalRotation; ReplaceInContainerOrGround(constructEntity, ent); diff --git a/Content.Server/Interfaces/GameObjects/Components/Interaction/IAfterInteract.cs b/Content.Server/Interfaces/GameObjects/Components/Interaction/IAfterInteract.cs index ed0a167850..04eeef96fb 100644 --- a/Content.Server/Interfaces/GameObjects/Components/Interaction/IAfterInteract.cs +++ b/Content.Server/Interfaces/GameObjects/Components/Interaction/IAfterInteract.cs @@ -1,4 +1,4 @@ -using System; +using System; using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; @@ -23,6 +23,7 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction public IEntity User { get; set; } public GridCoordinates ClickLocation { get; set; } public IEntity Target { get; set; } + public bool CanReach { get; set; } } /// @@ -56,12 +57,19 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction /// public GridCoordinates ClickLocation { get; } - public AfterInteractMessage(IEntity user, IEntity itemInHand, IEntity attacked, GridCoordinates clickLocation) + /// + /// Is the click location close enough to reach by the player? This does not check for obstructions, just that the target is within + /// reach radius around the user. + /// + public bool CanReach { get; } + + public AfterInteractMessage(IEntity user, IEntity itemInHand, IEntity attacked, GridCoordinates clickLocation, bool canReach) { User = user; Attacked = attacked; ClickLocation = clickLocation; ItemInHand = itemInHand; + CanReach = canReach; } }