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:
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user