stealth clothing (#19397)

Co-authored-by: deltanedas <@deltanedas:kde.org>
This commit is contained in:
deltanedas
2023-08-30 03:34:58 +01:00
committed by GitHub
parent 15c0c25fbf
commit 7870d02254
5 changed files with 203 additions and 3 deletions

View File

@@ -20,6 +20,11 @@ public sealed class GetItemActionsEvent : EntityEventArgs
{ {
public SortedSet<ActionType> Actions = new(); public SortedSet<ActionType> Actions = new();
/// <summary>
/// User equipping the item.
/// </summary>
public EntityUid User;
/// <summary> /// <summary>
/// Slot flags for the inventory slot that this item got equipped to. Null if not in a slot (i.e., if equipped to hands). /// Slot flags for the inventory slot that this item got equipped to. Null if not in a slot (i.e., if equipped to hands).
/// </summary> /// </summary>
@@ -30,8 +35,9 @@ public sealed class GetItemActionsEvent : EntityEventArgs
/// </summary> /// </summary>
public bool InHands => SlotFlags == null; public bool InHands => SlotFlags == null;
public GetItemActionsEvent(SlotFlags? slotFlags = null) public GetItemActionsEvent(EntityUid user, SlotFlags? slotFlags = null)
{ {
User = user;
SlotFlags = slotFlags; SlotFlags = slotFlags;
} }
} }

View File

@@ -410,7 +410,7 @@ public abstract class SharedActionsSystem : EntitySystem
#region EquipHandlers #region EquipHandlers
private void OnDidEquip(EntityUid uid, ActionsComponent component, DidEquipEvent args) private void OnDidEquip(EntityUid uid, ActionsComponent component, DidEquipEvent args)
{ {
var ev = new GetItemActionsEvent(args.SlotFlags); var ev = new GetItemActionsEvent(args.Equipee, args.SlotFlags);
RaiseLocalEvent(args.Equipment, ev); RaiseLocalEvent(args.Equipment, ev);
if (ev.Actions.Count == 0) if (ev.Actions.Count == 0)
@@ -421,7 +421,7 @@ public abstract class SharedActionsSystem : EntitySystem
private void OnHandEquipped(EntityUid uid, ActionsComponent component, DidEquipHandEvent args) private void OnHandEquipped(EntityUid uid, ActionsComponent component, DidEquipHandEvent args)
{ {
var ev = new GetItemActionsEvent(); var ev = new GetItemActionsEvent(args.User);
RaiseLocalEvent(args.Equipped, ev); RaiseLocalEvent(args.Equipped, ev);
if (ev.Actions.Count == 0) if (ev.Actions.Count == 0)

View File

@@ -0,0 +1,43 @@
using Content.Shared.Actions;
using Content.Shared.Actions.ActionTypes;
using Content.Shared.Clothing.EntitySystems;
using Robust.Shared.GameStates;
namespace Content.Shared.Clothing.Components;
/// <summary>
/// Adds StealthComponent to the user when enabled, either by an action or the system's SetEnabled method.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), Access(typeof(StealthClothingSystem))]
public sealed partial class StealthClothingComponent : Component
{
/// <summary>
/// Whether stealth effect is enabled.
/// </summary>
[DataField("enabled"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
public bool Enabled;
/// <summary>
/// Number added to MinVisibility when stealthed, to make the user not fully invisible.
/// </summary>
[DataField("visibility"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
public float Visibility;
/// <summary>
/// The action for enabling and disabling stealth.
/// </summary>
[DataField("toggleAction")]
public InstantAction ToggleAction = new()
{
Event = new ToggleStealthEvent()
};
}
/// <summary>
/// When stealth is enabled, disables it.
/// When it is disabled, raises <see cref="AttemptStealthEvent"/> before enabling.
/// Put any checks in a handler for that event to cancel it.
/// </summary>
public sealed partial class ToggleStealthEvent : InstantActionEvent
{
}

View File

@@ -0,0 +1,137 @@
using Content.Shared.Actions;
using Content.Shared.Clothing.Components;
using Content.Shared.Inventory.Events;
using Content.Shared.Stealth;
using Content.Shared.Stealth.Components;
using Robust.Shared.GameStates;
namespace Content.Shared.Clothing.EntitySystems;
/// <summary>
/// Handles the toggle action and disables stealth when clothing is unequipped.
/// </summary>
public sealed class StealthClothingSystem : EntitySystem
{
[Dependency] private readonly SharedStealthSystem _stealth = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<StealthClothingComponent, GetItemActionsEvent>(OnGetItemActions);
SubscribeLocalEvent<StealthClothingComponent, ToggleStealthEvent>(OnToggleStealth);
SubscribeLocalEvent<StealthClothingComponent, AfterAutoHandleStateEvent>(OnHandleState);
SubscribeLocalEvent<StealthClothingComponent, GotUnequippedEvent>(OnUnequipped);
}
/// <summary>
/// Sets the clothing's stealth effect for the user.
/// </summary>
/// <returns>True if it was changed, false otherwise</returns>
public bool SetEnabled(EntityUid uid, EntityUid user, bool enabled, StealthClothingComponent? comp = null)
{
if (!Resolve(uid, ref comp) || comp.Enabled == enabled)
return false;
// TODO remove this when clothing unequip on delete is less sus
// prevent debug assert when ending round and its disabled
if (MetaData(user).EntityLifeStage >= EntityLifeStage.Terminating)
return false;
comp.Enabled = enabled;
Dirty(uid, comp);
var stealth = EnsureComp<StealthComponent>(user);
// slightly visible, but doesn't change when moving so it's ok
var visibility = enabled ? stealth.MinVisibility + comp.Visibility : stealth.MaxVisibility;
_stealth.SetVisibility(user, visibility, stealth);
_stealth.SetEnabled(user, enabled, stealth);
return true;
}
/// <summary>
/// Raise <see cref="AddStealthActionEvent"/> then add the toggle action if it was not cancelled.
/// </summary>
private void OnGetItemActions(EntityUid uid, StealthClothingComponent comp, GetItemActionsEvent args)
{
var ev = new AddStealthActionEvent(args.User);
RaiseLocalEvent(uid, ev);
if (ev.Cancelled)
return;
args.Actions.Add(comp.ToggleAction);
}
/// <summary>
/// Raises <see cref="AttemptStealthEvent"/> if enabling.
/// </summary>
private void OnToggleStealth(EntityUid uid, StealthClothingComponent comp, ToggleStealthEvent args)
{
args.Handled = true;
var user = args.Performer;
if (comp.Enabled)
{
SetEnabled(uid, user, false, comp);
return;
}
var ev = new AttemptStealthEvent(user);
RaiseLocalEvent(uid, ev);
if (ev.Cancelled)
return;
SetEnabled(uid, user, true, comp);
}
/// <summary>
/// Calls <see cref="SetEnabled"/> when server sends new state.
/// </summary>
private void OnHandleState(EntityUid uid, StealthClothingComponent comp, ref AfterAutoHandleStateEvent args)
{
// SetEnabled checks if it is the same, so change it to before state was received from the server
var enabled = comp.Enabled;
comp.Enabled = !enabled;
var user = Transform(uid).ParentUid;
SetEnabled(uid, user, enabled, comp);
}
/// <summary>
/// Force unstealths the user, doesnt remove StealthComponent since other things might use it
/// </summary>
private void OnUnequipped(EntityUid uid, StealthClothingComponent comp, GotUnequippedEvent args)
{
SetEnabled(uid, args.Equipee, false, comp);
}
}
/// <summary>
/// Raised on the stealth clothing when attempting to add an action.
/// </summary>
public class AddStealthActionEvent : CancellableEntityEventArgs
{
/// <summary>
/// User that equipped the stealth clothing.
/// </summary>
public EntityUid User;
public AddStealthActionEvent(EntityUid user)
{
User = user;
}
}
/// <summary>
/// Raised on the stealth clothing when the user is attemping to enable it.
/// </summary>
public class AttemptStealthEvent : CancellableEntityEventArgs
{
/// <summary>
/// User that is attempting to enable the stealth clothing.
/// </summary>
public EntityUid User;
public AttemptStealthEvent(EntityUid user)
{
User = user;
}
}

View File

@@ -101,6 +101,20 @@
sprite: Clothing/OuterClothing/Suits/spaceninja.rsi sprite: Clothing/OuterClothing/Suits/spaceninja.rsi
- type: Clothing - type: Clothing
sprite: Clothing/OuterClothing/Suits/spaceninja.rsi sprite: Clothing/OuterClothing/Suits/spaceninja.rsi
- type: StealthClothing
visibility: 0.3
toggleAction:
# have to plan (un)cloaking ahead of time
useDelay: 5
name: action-name-toggle-phase-cloak
description: action-desc-toggle-phase-cloak
priority: -9
event: !type:ToggleStealthEvent
- type: PressureProtection
highPressureMultiplier: 0.6
lowPressureMultiplier: 1000
- type: TemperatureProtection
coefficient: 0.01
- type: Armor - type: Armor
modifiers: modifiers:
coefficients: coefficients: