Construction Bugfixes (#1329)

* Added: CanReach property to AfterAttack events which signals if the attack was within reach.
Fixed: You cannot construct/deconstruct things out of reach.
Fixed: Construction entities now preserve transform rotation.
Fixed: No more exceptions about missing grids when constructing world entities.
Fixed: Used the proper intermediate sprite for intermediate entities.
Fixed: Issue with missing sprite layers on ghost.

* The alligator is greedy, he always eats the bigger number...

* Adds a check so that you cannot use tools on entities that are obstructed from view.
This commit is contained in:
Acruid
2020-07-10 16:52:07 -07:00
committed by GitHub
parent 89c111664a
commit b6b31b7294
4 changed files with 47 additions and 19 deletions

View File

@@ -158,11 +158,13 @@ namespace Content.Client.GameObjects.EntitySystems
comp.Prototype = prototype; comp.Prototype = prototype;
comp.GhostID = _nextId++; comp.GhostID = _nextId++;
ghost.Transform.LocalRotation = dir.ToAngle(); ghost.Transform.LocalRotation = dir.ToAngle();
var sprite = ghost.GetComponent<SpriteComponent>();
sprite.LayerSetSprite(0, prototype.Icon);
sprite.LayerSetVisible(0, true);
_ghosts.Add(comp.GhostID, comp); _ghosts.Add(comp.GhostID, comp);
var sprite = ghost.GetComponent<SpriteComponent>();
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) private void TryStartConstruction(int ghostId)

View File

@@ -286,7 +286,8 @@ namespace Content.Server.GameObjects.EntitySystems.Click
if (item != null) if (item != null)
{ {
// After attack: Check if we clicked on an empty location, if so the only interaction we can do is AfterInteract // 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; return;
@@ -330,9 +331,9 @@ namespace Content.Server.GameObjects.EntitySystems.Click
/// <summary> /// <summary>
/// We didn't click on any entity, try doing an AfterInteract on the click location /// We didn't click on any entity, try doing an AfterInteract on the click location
/// </summary> /// </summary>
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); RaiseLocalEvent(message);
if (message.Handled) if (message.Handled)
{ {
@@ -340,7 +341,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click
} }
var afterInteracts = weapon.GetAllComponents<IAfterInteract>().ToList(); var afterInteracts = weapon.GetAllComponents<IAfterInteract>().ToList();
var afterInteractEventArgs = new AfterInteractEventArgs {User = user, ClickLocation = clickLocation}; var afterInteractEventArgs = new AfterInteractEventArgs {User = user, ClickLocation = clickLocation, CanReach = canReach};
foreach (var afterInteract in afterInteracts) 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); RaiseLocalEvent(afterAtkMsg);
if (afterAtkMsg.Handled) if (afterAtkMsg.Handled)
{ {
@@ -391,7 +392,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click
var afterAttacks = weapon.GetAllComponents<IAfterInteract>().ToList(); var afterAttacks = weapon.GetAllComponents<IAfterInteract>().ToList();
var afterAttackEventArgs = new AfterInteractEventArgs var afterAttackEventArgs = new AfterInteractEventArgs
{ {
User = user, ClickLocation = clickLocation, Target = attacked User = user, ClickLocation = clickLocation, Target = attacked, CanReach = true
}; };
foreach (var afterAttack in afterAttacks) 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); RaiseLocalEvent(afterAtkMsg);
if (afterAtkMsg.Handled) if (afterAtkMsg.Handled)
return; return;
@@ -695,7 +696,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click
var afterAttacks = weapon.GetAllComponents<IAfterInteract>().ToList(); var afterAttacks = weapon.GetAllComponents<IAfterInteract>().ToList();
var afterAttackEventArgs = new AfterInteractEventArgs 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 //See if we have a ranged attack interaction

View File

@@ -4,6 +4,7 @@ using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.Construction; using Content.Server.GameObjects.Components.Construction;
using Content.Server.GameObjects.Components.Interactable; using Content.Server.GameObjects.Components.Interactable;
using Content.Server.GameObjects.Components.Stack; using Content.Server.GameObjects.Components.Stack;
using Content.Server.GameObjects.EntitySystems.Click;
using Content.Server.Interfaces.GameObjects.Components.Interaction; using Content.Server.Interfaces.GameObjects.Components.Interaction;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Server.Utility; using Content.Server.Utility;
@@ -78,6 +79,10 @@ namespace Content.Server.GameObjects.EntitySystems
if(msg.Handled) if(msg.Handled)
return; return;
// You can only construct/deconstruct things within reach
if(!msg.CanReach)
return;
var targetEnt = msg.Attacked; var targetEnt = msg.Attacked;
var handEnt = msg.ItemInHand; var handEnt = msg.ItemInHand;
@@ -85,6 +90,10 @@ namespace Content.Server.GameObjects.EntitySystems
if(targetEnt is null || handEnt is null) if(targetEnt is null || handEnt is null)
return; return;
var interaction = Get<InteractionSystem>();
if(!interaction.InRangeUnobstructed(handEnt.Transform.MapPosition, targetEnt.Transform.MapPosition, ignoredEnt: targetEnt, ignoreInsideBlocker: true))
return;
// Cannot deconstruct an entity with no prototype. // Cannot deconstruct an entity with no prototype.
var targetPrototype = targetEnt.MetaData.EntityPrototype; var targetPrototype = targetEnt.MetaData.EntityPrototype;
if (targetPrototype is null) if (targetPrototype is null)
@@ -156,7 +165,7 @@ namespace Content.Server.GameObjects.EntitySystems
else // replace ent with intermediate else // replace ent with intermediate
{ {
// Spawn frame // Spawn frame
var frame = EntityManager.SpawnEntity("structureconstructionframe", targetEntPos); var frame = SpawnCopyTransform("structureconstructionframe", targetEnt.Transform);
var construction = frame.GetComponent<ConstructionComponent>(); var construction = frame.GetComponent<ConstructionComponent>();
SetupComponent(construction, prototype); SetupComponent(construction, prototype);
construction.Stage = prototype.Stages.Count - 2; 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) private static void SetupDeconIntermediateSprite(ConstructionComponent constructionComponent, ConstructionPrototype prototype)
{ {
if(!constructionComponent.Owner.TryGetComponent<SpriteComponent>(out var spriteComp)) if(!constructionComponent.Owner.TryGetComponent<SpriteComponent>(out var spriteComp))
return; 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) if (prototype.Stages[i].Icon != null)
{ {
@@ -331,12 +348,12 @@ namespace Content.Server.GameObjects.EntitySystems
if (prototype.Stages.Count == 2) if (prototype.Stages.Count == 2)
{ {
// Exactly 2 stages, so don't make an intermediate frame. // 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<ItemComponent>()); hands.PutInHandOrDrop(ent.GetComponent<ItemComponent>());
} }
else else
{ {
var frame = EntityManager.SpawnEntity("structureconstructionframe", placingEnt.Transform.GridPosition); var frame = SpawnCopyTransform("structureconstructionframe", placingEnt.Transform);
var construction = frame.GetComponent<ConstructionComponent>(); var construction = frame.GetComponent<ConstructionComponent>();
SetupComponent(construction, prototype); SetupComponent(construction, prototype);
@@ -379,7 +396,7 @@ namespace Content.Server.GameObjects.EntitySystems
if (constructionComponent.Stage == constructPrototype.Stages.Count - 1) if (constructionComponent.Stage == constructPrototype.Stages.Count - 1)
{ {
// Oh boy we get to finish construction! // 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; ent.Transform.LocalRotation = transformComponent.LocalRotation;
ReplaceInContainerOrGround(constructEntity, ent); ReplaceInContainerOrGround(constructEntity, ent);

View File

@@ -1,4 +1,4 @@
using System; using System;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
@@ -23,6 +23,7 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
public IEntity User { get; set; } public IEntity User { get; set; }
public GridCoordinates ClickLocation { get; set; } public GridCoordinates ClickLocation { get; set; }
public IEntity Target { get; set; } public IEntity Target { get; set; }
public bool CanReach { get; set; }
} }
/// <summary> /// <summary>
@@ -56,12 +57,19 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
/// </summary> /// </summary>
public GridCoordinates ClickLocation { get; } public GridCoordinates ClickLocation { get; }
public AfterInteractMessage(IEntity user, IEntity itemInHand, IEntity attacked, GridCoordinates clickLocation) /// <summary>
/// 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.
/// </summary>
public bool CanReach { get; }
public AfterInteractMessage(IEntity user, IEntity itemInHand, IEntity attacked, GridCoordinates clickLocation, bool canReach)
{ {
User = user; User = user;
Attacked = attacked; Attacked = attacked;
ClickLocation = clickLocation; ClickLocation = clickLocation;
ItemInHand = itemInHand; ItemInHand = itemInHand;
CanReach = canReach;
} }
} }