using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.Graphics;
+using Robust.Client.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
{
private readonly IEntityManager _entManager;
private readonly IGameTiming _timing;
+ private readonly IPlayerManager _player;
private readonly SharedTransformSystem _transform;
private readonly MetaDataSystem _meta;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
- public DoAfterOverlay(IEntityManager entManager, IPrototypeManager protoManager, IGameTiming timing)
+ public DoAfterOverlay(IEntityManager entManager, IPrototypeManager protoManager, IGameTiming timing, IPlayerManager player)
{
_entManager = entManager;
_timing = timing;
+ _player = player;
_transform = _entManager.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
_meta = _entManager.EntitySysManager.GetEntitySystem<MetaDataSystem>();
- var sprite = new SpriteSpecifier.Rsi(new ("/Textures/Interface/Misc/progress_bar.rsi"), "icon");
+ var sprite = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/progress_bar.rsi"), "icon");
_barTexture = _entManager.EntitySysManager.GetEntitySystem<SpriteSystem>().Frame0(sprite);
_shader = protoManager.Index<ShaderPrototype>("unshaded").Instance();
var curTime = _timing.CurTime;
var bounds = args.WorldAABB.Enlarged(5f);
+ var localEnt = _player.LocalSession?.AttachedEntity;
var metaQuery = _entManager.GetEntityQuery<MetaDataComponent>();
var enumerator = _entManager.AllEntityQueryEnumerator<ActiveDoAfterComponent, DoAfterComponent, SpriteComponent, TransformComponent>();
foreach (var doAfter in comp.DoAfters.Values)
{
+ // Hide some DoAfters from other players for stealthy actions (ie: thieving gloves)
+ var alpha = 1f;
+ if (doAfter.Args.Hidden)
+ {
+ if (uid != localEnt)
+ continue;
+
+ // Hints to the local player that this do-after is not visible to other players.
+ alpha = 0.5f;
+ }
+
// Use the sprite itself if we know its bounds. This means short or tall sprites don't get overlapped
// by the bar.
float yOffset = sprite.Bounds.Height / 2f + 0.05f;
{
var elapsed = doAfter.CancelledTime.Value - doAfter.StartTime;
elapsedRatio = (float) Math.Min(1, elapsed.TotalSeconds / doAfter.Args.Delay.TotalSeconds);
- var cancelElapsed = (time - doAfter.CancelledTime.Value).TotalSeconds;
+ var cancelElapsed = (time - doAfter.CancelledTime.Value).TotalSeconds;
var flash = Math.Floor(cancelElapsed / FlashTime) % 2 == 0;
- color = new Color(1f, 0f, 0f, flash ? 1f : 0f);
+ color = new Color(1f, 0f, 0f, flash ? alpha : 0f);
}
else
{
var elapsed = time - doAfter.StartTime;
elapsedRatio = (float) Math.Min(1, elapsed.TotalSeconds / doAfter.Args.Delay.TotalSeconds);
- color = GetProgressColor(elapsedRatio);
+ color = GetProgressColor(elapsedRatio, alpha);
}
var xProgress = (EndX - StartX) * elapsedRatio + StartX;
handle.SetTransform(Matrix3.Identity);
}
- public static Color GetProgressColor(float progress)
+ public static Color GetProgressColor(float progress, float alpha = 1f)
{
if (progress >= 1.0f)
{
- return new Color(0f, 1f, 0f);
+ return new Color(0f, 1f, 0f, alpha);
}
// lerp
var hue = (5f / 18f) * progress;
- return Color.FromHsv((hue, 1f, 0.75f, 1f));
+ return Color.FromHsv((hue, 1f, 0.75f, alpha));
}
}
public override void Initialize()
{
base.Initialize();
- _overlay.AddOverlay(new DoAfterOverlay(EntityManager, _prototype, GameTiming));
+ _overlay.AddOverlay(new DoAfterOverlay(EntityManager, _prototype, GameTiming, _player));
}
public override void Shutdown()
var doAfterArgs = new DoAfterArgs(EntityManager, user, ev.Time, new AwaitedDoAfterEvent(), null, target: target, used: held)
{
ExtraCheck = Check,
+ Hidden = ev.Stealth,
AttemptFrequency = AttemptFrequency.EveryTick,
BreakOnDamage = true,
BreakOnTargetMove = true,
var doAfterArgs = new DoAfterArgs(EntityManager, user, ev.Time, new AwaitedDoAfterEvent(), null, target: target, used: held)
{
ExtraCheck = Check,
+ Hidden = ev.Stealth,
AttemptFrequency = AttemptFrequency.EveryTick,
BreakOnDamage = true,
BreakOnTargetMove = true,
if (result != DoAfterStatus.Finished) return;
_handsSystem.TryDrop(user, checkActionBlocker: false, handsComp: userHands);
- _handsSystem.TryPickup(target, held, handName, checkActionBlocker: false, animateUser: true, handsComp: hands);
+ _handsSystem.TryPickup(target, held, handName, checkActionBlocker: false, animateUser: !ev.Stealth, animate: !ev.Stealth, handsComp: hands);
_adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):user} has placed the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s hands");
// hand update will trigger strippable update
}
var doAfterArgs = new DoAfterArgs(EntityManager, user, ev.Time, new AwaitedDoAfterEvent(), null, target: target, used: item)
{
ExtraCheck = Check,
+ Hidden = ev.Stealth,
AttemptFrequency = AttemptFrequency.EveryTick,
BreakOnDamage = true,
BreakOnTargetMove = true,
// Raise a dropped event, so that things like gas tank internals properly deactivate when stripping
RaiseLocalEvent(item, new DroppedEvent(user), true);
- _handsSystem.PickupOrDrop(user, item);
+ _handsSystem.PickupOrDrop(user, item, animateUser: !ev.Stealth, animate: !ev.Stealth);
_adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):user} has stripped the item {ToPrettyString(item):item} from {ToPrettyString(target):target}");
}
var doAfterArgs = new DoAfterArgs(EntityManager, user, ev.Time, new AwaitedDoAfterEvent(), null, target: target, used: item)
{
ExtraCheck = Check,
+ Hidden = ev.Stealth,
AttemptFrequency = AttemptFrequency.EveryTick,
BreakOnDamage = true,
BreakOnTargetMove = true,
DuplicateCondition = DuplicateConditions.SameTool
};
- if (Check() && _handsSystem.TryGetHand(target, handName, out var handSlot, hands) && handSlot.HeldEntity != null)
+ if (!ev.Stealth && Check() && _handsSystem.TryGetHand(target, handName, out var handSlot, hands) && handSlot.HeldEntity != null)
{
_popup.PopupEntity(
Loc.GetString("strippable-component-alert-owner",
return;
_handsSystem.TryDrop(target, item, checkActionBlocker: false, handsComp: hands);
- _handsSystem.PickupOrDrop(user, item, handsComp: userHands);
+ _handsSystem.PickupOrDrop(user, item, animateUser: !ev.Stealth, animate: !ev.Stealth, handsComp: userHands);
// hand update will trigger strippable update
_adminLogger.Add(LogType.Stripping, LogImpact.Medium,
$"{ToPrettyString(user):user} has stripped the item {ToPrettyString(item):item} from {ToPrettyString(target):target}");
public NetEntity? NetUsed;
+ /// <summary>
+ /// Whether the progress bar for this DoAfter should be hidden from other players.
+ /// </summary>
+ [DataField]
+ public bool Hidden;
+
#region Event options
/// <summary>
/// The event that will get raised when the DoAfter has finished. If null, this will simply raise a <see cref="SimpleDoAfterEvent"/>
Delay = other.Delay;
Target = other.Target;
Used = other.Used;
+ Hidden = other.Hidden;
EventTarget = other.EventTarget;
Broadcast = other.Broadcast;
NeedHand = other.NeedHand;