Stamina damage (#9230)
This commit is contained in:
10
Content.Server/Damage/Components/ActiveStaminaComponent.cs
Normal file
10
Content.Server/Damage/Components/ActiveStaminaComponent.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Content.Server.Damage.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks whether an entity has ANY stamina damage for update purposes only.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class ActiveStaminaComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
47
Content.Server/Damage/Components/StaminaComponent.cs
Normal file
47
Content.Server/Damage/Components/StaminaComponent.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Content.Server.Damage.Systems;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Server.Damage.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Add to an entity to paralyze it whenever it reaches critical amounts of Stamina DamageType.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class StaminaComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Have we reached peak stamina damage and been paralyzed?
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("critical")]
|
||||
public bool Critical;
|
||||
|
||||
/// <summary>
|
||||
/// How much stamina reduces per second.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("decay")]
|
||||
public float Decay = 3f;
|
||||
|
||||
/// <summary>
|
||||
/// How much time after receiving damage until stamina starts decreasing.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("cooldown")]
|
||||
public float DecayCooldown = 5f;
|
||||
|
||||
/// <summary>
|
||||
/// How much stamina damage this entity has taken.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("staminaDamage")]
|
||||
public float StaminaDamage;
|
||||
|
||||
/// <summary>
|
||||
/// How much stamina damage is required to entire stam crit.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("excess")]
|
||||
public float CritThreshold = 100f;
|
||||
|
||||
/// <summary>
|
||||
/// Next time we're allowed to decrease stamina damage. Refreshes whenever the stam damage is changed.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("decayAccumulator")]
|
||||
public float StaminaDecayAccumulator;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Server.Damage.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Applies stamina damage when colliding with an entity.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class StaminaDamageOnCollideComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("damage")]
|
||||
public float Damage = 55f;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Content.Server.Damage.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class StaminaDamageOnHitComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("damage")]
|
||||
public float Damage = 30f;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Content.Server.Damage.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Attempting to apply stamina damage on a melee hit to an entity.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public struct StaminaDamageOnHitAttemptEvent
|
||||
{
|
||||
public bool Cancelled;
|
||||
}
|
||||
221
Content.Server/Damage/Systems/StaminaSystem.cs
Normal file
221
Content.Server/Damage/Systems/StaminaSystem.cs
Normal file
@@ -0,0 +1,221 @@
|
||||
using Content.Server.Damage.Components;
|
||||
using Content.Server.Damage.Events;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Weapon.Melee;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Rounding;
|
||||
using Content.Shared.Stunnable;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Damage.Systems;
|
||||
|
||||
public sealed class StaminaSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly AlertsSystem _alerts = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
|
||||
|
||||
private const float UpdateCooldown = 2f;
|
||||
private float _accumulator;
|
||||
|
||||
private const string CollideFixture = "projectile";
|
||||
|
||||
/// <summary>
|
||||
/// How much of a buffer is there between the stun duration and when stuns can be re-applied.
|
||||
/// </summary>
|
||||
private const float StamCritBufferTime = 3f;
|
||||
|
||||
private readonly List<EntityUid> _dirtyEntities = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<StaminaDamageOnCollideComponent, StartCollideEvent>(OnCollide);
|
||||
SubscribeLocalEvent<StaminaDamageOnHitComponent, MeleeHitEvent>(OnHit);
|
||||
SubscribeLocalEvent<StaminaComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<StaminaComponent, ComponentShutdown>(OnShutdown);
|
||||
}
|
||||
|
||||
private void OnShutdown(EntityUid uid, StaminaComponent component, ComponentShutdown args)
|
||||
{
|
||||
SetStaminaAlert(uid);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, StaminaComponent component, ComponentStartup args)
|
||||
{
|
||||
SetStaminaAlert(uid, component);
|
||||
}
|
||||
|
||||
private void OnHit(EntityUid uid, StaminaDamageOnHitComponent component, MeleeHitEvent args)
|
||||
{
|
||||
if (component.Damage <= 0f) return;
|
||||
|
||||
var ev = new StaminaDamageOnHitAttemptEvent();
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
|
||||
if (ev.Cancelled) return;
|
||||
|
||||
var stamQuery = GetEntityQuery<StaminaComponent>();
|
||||
var toHit = new ValueList<StaminaComponent>();
|
||||
|
||||
// Split stamina damage between all eligible targets.
|
||||
foreach (var ent in args.HitEntities)
|
||||
{
|
||||
if (!stamQuery.TryGetComponent(ent, out var stam)) continue;
|
||||
toHit.Add(stam);
|
||||
}
|
||||
|
||||
foreach (var comp in toHit)
|
||||
{
|
||||
var oldDamage = comp.StaminaDamage;
|
||||
TakeStaminaDamage(comp.Owner, component.Damage / toHit.Count, comp);
|
||||
if (comp.StaminaDamage.Equals(oldDamage))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("stamina-resist"), comp.Owner, Filter.Entities(args.User));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCollide(EntityUid uid, StaminaDamageOnCollideComponent component, StartCollideEvent args)
|
||||
{
|
||||
if (!args.OurFixture.ID.Equals(CollideFixture)) return;
|
||||
|
||||
TakeStaminaDamage(args.OtherFixture.Body.Owner, component.Damage);
|
||||
}
|
||||
|
||||
private void SetStaminaAlert(EntityUid uid, StaminaComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false) || component.Deleted)
|
||||
{
|
||||
_alerts.ClearAlert(uid, AlertType.Stamina);
|
||||
return;
|
||||
}
|
||||
|
||||
var severity = ContentHelpers.RoundToLevels(MathF.Max(0f, component.CritThreshold - component.StaminaDamage), component.CritThreshold, 7);
|
||||
_alerts.ShowAlert(uid, AlertType.Stamina, (short) severity);
|
||||
}
|
||||
|
||||
public void TakeStaminaDamage(EntityUid uid, float value, StaminaComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false) || component.Critical) return;
|
||||
|
||||
var oldDamage = component.StaminaDamage;
|
||||
component.StaminaDamage = MathF.Max(0f, component.StaminaDamage + value);
|
||||
|
||||
// Reset the decay cooldown upon taking damage.
|
||||
if (oldDamage < component.StaminaDamage)
|
||||
{
|
||||
component.StaminaDecayAccumulator = component.DecayCooldown;
|
||||
}
|
||||
|
||||
var slowdownThreshold = component.CritThreshold / 2f;
|
||||
|
||||
// If we go above n% then apply slowdown
|
||||
if (oldDamage < slowdownThreshold &&
|
||||
component.StaminaDamage > slowdownThreshold)
|
||||
{
|
||||
_stunSystem.TrySlowdown(uid, TimeSpan.FromSeconds(3), true, 0.8f, 0.8f);
|
||||
}
|
||||
|
||||
SetStaminaAlert(uid, component);
|
||||
|
||||
// Can't do it here as resetting prediction gets cooked.
|
||||
_dirtyEntities.Add(uid);
|
||||
|
||||
if (!component.Critical)
|
||||
{
|
||||
if (component.StaminaDamage >= component.CritThreshold)
|
||||
{
|
||||
EnterStamCrit(uid, component);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (component.StaminaDamage < component.CritThreshold)
|
||||
{
|
||||
ExitStamCrit(uid, component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
if (!_timing.IsFirstTimePredicted) return;
|
||||
|
||||
_accumulator -= frameTime;
|
||||
|
||||
if (_accumulator > 0f) return;
|
||||
|
||||
var stamQuery = GetEntityQuery<StaminaComponent>();
|
||||
|
||||
foreach (var uid in _dirtyEntities)
|
||||
{
|
||||
// Don't need to RemComp as they will get handled below.
|
||||
if (!stamQuery.TryGetComponent(uid, out var comp) || comp.StaminaDamage <= 0f) continue;
|
||||
EnsureComp<ActiveStaminaComponent>(uid);
|
||||
}
|
||||
|
||||
_dirtyEntities.Clear();
|
||||
_accumulator += UpdateCooldown;
|
||||
|
||||
foreach (var active in EntityQuery<ActiveStaminaComponent>())
|
||||
{
|
||||
// Just in case we have active but not stamina we'll check and account for it.
|
||||
if (!stamQuery.TryGetComponent(active.Owner, out var comp) ||
|
||||
comp.StaminaDamage <= 0f)
|
||||
{
|
||||
RemComp<ActiveStaminaComponent>(active.Owner);
|
||||
continue;
|
||||
}
|
||||
|
||||
comp.StaminaDecayAccumulator -= UpdateCooldown;
|
||||
|
||||
if (comp.StaminaDecayAccumulator > 0f) continue;
|
||||
|
||||
// We were in crit so come out of it and continue.
|
||||
if (comp.Critical)
|
||||
{
|
||||
ExitStamCrit(active.Owner, comp);
|
||||
continue;
|
||||
}
|
||||
|
||||
comp.StaminaDecayAccumulator = 0f;
|
||||
TakeStaminaDamage(comp.Owner, -comp.Decay * UpdateCooldown, comp);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnterStamCrit(EntityUid uid, StaminaComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component) ||
|
||||
component.Critical) return;
|
||||
|
||||
// To make the difference between a stun and a stamcrit clear
|
||||
// TODO: Mask?
|
||||
|
||||
component.Critical = true;
|
||||
component.StaminaDamage = component.CritThreshold;
|
||||
component.StaminaDecayAccumulator = 0f;
|
||||
|
||||
var stunTime = TimeSpan.FromSeconds(6);
|
||||
_stunSystem.TryParalyze(uid, stunTime, true);
|
||||
|
||||
// Give them buffer before being able to be re-stunned
|
||||
component.StaminaDecayAccumulator = (float) stunTime.TotalSeconds + StamCritBufferTime;
|
||||
}
|
||||
|
||||
private void ExitStamCrit(EntityUid uid, StaminaComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component) ||
|
||||
!component.Critical) return;
|
||||
|
||||
component.Critical = false;
|
||||
component.StaminaDamage = 0f;
|
||||
SetStaminaAlert(uid, component);
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,6 @@ namespace Content.Server.Flash
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<FlashComponent, MeleeHitEvent>(OnFlashMeleeHit);
|
||||
SubscribeLocalEvent<FlashComponent, UseInHandEvent>(OnFlashUseInHand);
|
||||
SubscribeLocalEvent<FlashComponent, ExaminedEvent>(OnFlashExamined);
|
||||
|
||||
SubscribeLocalEvent<InventoryComponent, FlashAttemptEvent>(OnInventoryFlashAttempt);
|
||||
@@ -78,16 +77,6 @@ namespace Content.Server.Flash
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFlashUseInHand(EntityUid uid, FlashComponent comp, UseInHandEvent args)
|
||||
{
|
||||
if (!UseFlash(comp, args.User))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FlashArea(uid, args.User, comp.Range, comp.AoeFlashDuration, comp.SlowTo, true);
|
||||
}
|
||||
|
||||
private bool UseFlash(FlashComponent comp, EntityUid user)
|
||||
{
|
||||
if (comp.HasUses)
|
||||
@@ -121,39 +110,40 @@ namespace Content.Server.Flash
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Flash(EntityUid target, EntityUid? user, EntityUid? used, float flashDuration, float slowTo, bool displayPopup = true)
|
||||
public void Flash(EntityUid target, EntityUid? user, EntityUid? used, float flashDuration, float slowTo, bool displayPopup = true, FlashableComponent? flashable = null)
|
||||
{
|
||||
if (!Resolve(target, ref flashable, false)) return;
|
||||
|
||||
var attempt = new FlashAttemptEvent(target, user, used);
|
||||
RaiseLocalEvent(target, attempt, true);
|
||||
|
||||
if (attempt.Cancelled)
|
||||
return;
|
||||
|
||||
if (EntityManager.TryGetComponent<FlashableComponent>(target, out var flashable))
|
||||
flashable.LastFlash = _gameTiming.CurTime;
|
||||
flashable.Duration = flashDuration / 1000f; // TODO: Make this sane...
|
||||
Dirty(flashable);
|
||||
|
||||
_stunSystem.TrySlowdown(target, TimeSpan.FromSeconds(flashDuration/1000f), true,
|
||||
slowTo, slowTo);
|
||||
|
||||
if (displayPopup && user != null && target != user && EntityManager.EntityExists(user.Value))
|
||||
{
|
||||
flashable.LastFlash = _gameTiming.CurTime;
|
||||
flashable.Duration = flashDuration / 1000f; // TODO: Make this sane...
|
||||
Dirty(flashable);
|
||||
|
||||
_stunSystem.TrySlowdown(target, TimeSpan.FromSeconds(flashDuration/1000f), true,
|
||||
slowTo, slowTo);
|
||||
|
||||
if (displayPopup && user != null && target != user && EntityManager.EntityExists(user.Value))
|
||||
{
|
||||
user.Value.PopupMessage(target, Loc.GetString("flash-component-user-blinds-you",
|
||||
("user", user.Value)));
|
||||
}
|
||||
user.Value.PopupMessage(target, Loc.GetString("flash-component-user-blinds-you",
|
||||
("user", user.Value)));
|
||||
}
|
||||
}
|
||||
|
||||
public void FlashArea(EntityUid source, EntityUid? user, float range, float duration, float slowTo = 0f, bool displayPopup = false, SoundSpecifier? sound = null)
|
||||
public void FlashArea(EntityUid source, EntityUid? user, float range, float duration, float slowTo = 0.8f, bool displayPopup = false, SoundSpecifier? sound = null)
|
||||
{
|
||||
var transform = EntityManager.GetComponent<TransformComponent>(source);
|
||||
var mapPosition = transform.MapPosition;
|
||||
var flashableEntities = new List<EntityUid>();
|
||||
var flashableQuery = GetEntityQuery<FlashableComponent>();
|
||||
|
||||
foreach (var entity in _entityLookup.GetEntitiesInRange(transform.Coordinates, range))
|
||||
{
|
||||
if (!EntityManager.HasComponent<FlashableComponent>(entity))
|
||||
if (!flashableQuery.HasComponent(entity))
|
||||
continue;
|
||||
|
||||
flashableEntities.Add(entity);
|
||||
@@ -162,14 +152,15 @@ namespace Content.Server.Flash
|
||||
foreach (var entity in flashableEntities)
|
||||
{
|
||||
// Check for unobstructed entities while ignoring the mobs with flashable components.
|
||||
if (!_interactionSystem.InRangeUnobstructed(entity, transform.MapPosition, range, CollisionGroup.Opaque, (e) => flashableEntities.Contains(e)))
|
||||
if (!_interactionSystem.InRangeUnobstructed(entity, mapPosition, range, CollisionGroup.Opaque, (e) => flashableEntities.Contains(e)))
|
||||
continue;
|
||||
|
||||
Flash(entity, user, source, duration, slowTo, displayPopup);
|
||||
// They shouldn't have flash removed in between right?
|
||||
Flash(entity, user, source, duration, slowTo, displayPopup, flashableQuery.GetComponent(entity));
|
||||
}
|
||||
if (sound != null)
|
||||
{
|
||||
SoundSystem.Play(sound.GetSound(), Filter.Pvs(transform), transform.Coordinates);
|
||||
SoundSystem.Play(sound.GetSound(), Filter.Pvs(transform), source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Server.Stunnable.Systems;
|
||||
using Content.Shared.Sound;
|
||||
using Content.Shared.Timing;
|
||||
|
||||
@@ -8,21 +9,6 @@ namespace Content.Server.Stunnable.Components
|
||||
{
|
||||
public bool Activated = false;
|
||||
|
||||
/// <summary>
|
||||
/// What the <see cref="UseDelayComponent"/> is when the stun baton is active.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("activeCooldown")]
|
||||
public TimeSpan ActiveDelay = TimeSpan.FromSeconds(4);
|
||||
|
||||
/// <summary>
|
||||
/// Store what the <see cref="UseDelayComponent"/> was before being activated.
|
||||
/// </summary>
|
||||
public TimeSpan? OldDelay;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("paralyzeTime")]
|
||||
public float ParalyzeTime { get; set; } = 5f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("energyPerUse")]
|
||||
public float EnergyPerUse { get; set; } = 350;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Damage.Components;
|
||||
using Content.Server.Damage.Events;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.Events;
|
||||
using Content.Server.Speech.EntitySystems;
|
||||
using Content.Server.Stunnable.Components;
|
||||
using Content.Server.Weapon.Melee;
|
||||
using Content.Server.Weapon.Melee.Components;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction.Events;
|
||||
@@ -12,55 +13,48 @@ using Content.Shared.Item;
|
||||
using Content.Shared.Jittering;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.Timing;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Stunnable
|
||||
namespace Content.Server.Stunnable.Systems
|
||||
{
|
||||
public sealed class StunbatonSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly MeleeWeaponSystem _melee = default!;
|
||||
[Dependency] private readonly StunSystem _stunSystem = default!;
|
||||
[Dependency] private readonly StutteringSystem _stutteringSystem = default!;
|
||||
[Dependency] private readonly SharedJitteringSystem _jitterSystem = default!;
|
||||
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<StunbatonComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
SubscribeLocalEvent<StunbatonComponent, UseInHandEvent>(OnUseInHand);
|
||||
SubscribeLocalEvent<StunbatonComponent, ThrowDoHitEvent>(OnThrowCollide);
|
||||
SubscribeLocalEvent<StunbatonComponent, ExaminedEvent>(OnExamined);
|
||||
SubscribeLocalEvent<StunbatonComponent, StaminaDamageOnHitAttemptEvent>(OnStaminaHitAttempt);
|
||||
SubscribeLocalEvent<StunbatonComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
}
|
||||
|
||||
private void OnMeleeHit(EntityUid uid, StunbatonComponent comp, MeleeHitEvent args)
|
||||
private void OnMeleeHit(EntityUid uid, StunbatonComponent component, MeleeHitEvent args)
|
||||
{
|
||||
if (!comp.Activated || !args.HitEntities.Any() || args.Handled || _useDelay.ActiveDelay(uid))
|
||||
return;
|
||||
if (!component.Activated) return;
|
||||
|
||||
if (!TryComp<BatteryComponent>(uid, out var battery) || !battery.TryUseCharge(comp.EnergyPerUse))
|
||||
return;
|
||||
// Don't apply damage if it's activated; just do stamina damage.
|
||||
args.BonusDamage -= args.BaseDamage;
|
||||
}
|
||||
|
||||
foreach (var entity in args.HitEntities)
|
||||
private void OnStaminaHitAttempt(EntityUid uid, StunbatonComponent component, ref StaminaDamageOnHitAttemptEvent args)
|
||||
{
|
||||
if (!component.Activated ||
|
||||
!TryComp<BatteryComponent>(uid, out var battery) || !battery.TryUseCharge(component.EnergyPerUse))
|
||||
{
|
||||
StunEntity(entity, comp);
|
||||
SendPowerPulse(entity, args.User, uid);
|
||||
args.Cancelled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_melee.SetAttackCooldown(uid, _timing.CurTime + comp.ActiveDelay);
|
||||
_useDelay.BeginDelay(uid);
|
||||
// No combat should occur if we successfully stunned.
|
||||
args.Handled = true;
|
||||
if (battery.CurrentCharge < component.EnergyPerUse)
|
||||
{
|
||||
SoundSystem.Play(component.SparksSound.GetSound(), Filter.Pvs(component.Owner, entityManager: EntityManager), uid, AudioHelpers.WithVariation(0.25f));
|
||||
TurnOff(component);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUseInHand(EntityUid uid, StunbatonComponent comp, UseInHandEvent args)
|
||||
@@ -75,21 +69,6 @@ namespace Content.Server.Stunnable
|
||||
}
|
||||
}
|
||||
|
||||
private void OnThrowCollide(EntityUid uid, StunbatonComponent comp, ThrowDoHitEvent args)
|
||||
{
|
||||
if (!comp.Activated)
|
||||
return;
|
||||
|
||||
if (!TryComp<BatteryComponent>(uid, out var battery))
|
||||
return;
|
||||
|
||||
if (_robustRandom.Prob(comp.OnThrowStunChance) && battery.TryUseCharge(comp.EnergyPerUse))
|
||||
{
|
||||
SendPowerPulse(args.Target, args.User, uid);
|
||||
StunEntity(args.Target, comp);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExamined(EntityUid uid, StunbatonComponent comp, ExaminedEvent args)
|
||||
{
|
||||
var msg = comp.Activated
|
||||
@@ -101,24 +80,6 @@ namespace Content.Server.Stunnable
|
||||
("charge", (int)((battery.CurrentCharge/battery.MaxCharge) * 100))));
|
||||
}
|
||||
|
||||
private void StunEntity(EntityUid entity, StunbatonComponent comp)
|
||||
{
|
||||
if (!EntityManager.TryGetComponent(entity, out StatusEffectsComponent? status) || !comp.Activated) return;
|
||||
|
||||
SoundSystem.Play(comp.StunSound.GetSound(), Filter.Pvs(comp.Owner), comp.Owner, AudioHelpers.WithVariation(0.25f));
|
||||
_stunSystem.TryParalyze(entity, TimeSpan.FromSeconds(comp.ParalyzeTime), true, status);
|
||||
|
||||
var slowdownTime = TimeSpan.FromSeconds(comp.ParalyzeTime);
|
||||
_jitterSystem.DoJitter(entity, slowdownTime, true, status:status);
|
||||
_stutteringSystem.DoStutter(entity, slowdownTime, true, status);
|
||||
|
||||
if (!TryComp<BatteryComponent>(comp.Owner, out var battery) || !(battery.CurrentCharge < comp.EnergyPerUse))
|
||||
return;
|
||||
|
||||
SoundSystem.Play(comp.SparksSound.GetSound(), Filter.Pvs(comp.Owner), comp.Owner, AudioHelpers.WithVariation(0.25f));
|
||||
TurnOff(comp);
|
||||
}
|
||||
|
||||
private void TurnOff(StunbatonComponent comp)
|
||||
{
|
||||
if (!comp.Activated)
|
||||
@@ -135,11 +96,6 @@ namespace Content.Server.Stunnable
|
||||
SoundSystem.Play(comp.SparksSound.GetSound(), Filter.Pvs(comp.Owner), comp.Owner, AudioHelpers.WithVariation(0.25f));
|
||||
|
||||
comp.Activated = false;
|
||||
if (TryComp<UseDelayComponent>(comp.Owner, out var useDelay) && comp.OldDelay != null)
|
||||
{
|
||||
useDelay.Delay = comp.OldDelay.Value;
|
||||
comp.OldDelay = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void TurnOn(StunbatonComponent comp, EntityUid user)
|
||||
@@ -147,13 +103,6 @@ namespace Content.Server.Stunnable
|
||||
if (comp.Activated)
|
||||
return;
|
||||
|
||||
if (EntityManager.TryGetComponent<SpriteComponent?>(comp.Owner, out var sprite) &&
|
||||
EntityManager.TryGetComponent<SharedItemComponent?>(comp.Owner, out var item))
|
||||
{
|
||||
item.EquippedPrefix = "on";
|
||||
sprite.LayerSetState(0, "stunbaton_on");
|
||||
}
|
||||
|
||||
var playerFilter = Filter.Pvs(comp.Owner, entityManager: EntityManager);
|
||||
if (!TryComp<BatteryComponent>(comp.Owner, out var battery) || battery.CurrentCharge < comp.EnergyPerUse)
|
||||
{
|
||||
@@ -162,14 +111,15 @@ namespace Content.Server.Stunnable
|
||||
return;
|
||||
}
|
||||
|
||||
SoundSystem.Play(comp.SparksSound.GetSound(), playerFilter, comp.Owner, AudioHelpers.WithVariation(0.25f));
|
||||
|
||||
comp.Activated = true;
|
||||
if (TryComp<UseDelayComponent>(comp.Owner, out var useDelay))
|
||||
if (EntityManager.TryGetComponent<SpriteComponent?>(comp.Owner, out var sprite) &&
|
||||
EntityManager.TryGetComponent<SharedItemComponent?>(comp.Owner, out var item))
|
||||
{
|
||||
comp.OldDelay = useDelay.Delay;
|
||||
useDelay.Delay = comp.ActiveDelay;
|
||||
item.EquippedPrefix = "on";
|
||||
sprite.LayerSetState(0, "stunbaton_on");
|
||||
}
|
||||
|
||||
SoundSystem.Play(comp.SparksSound.GetSound(), playerFilter, comp.Owner, AudioHelpers.WithVariation(0.25f));
|
||||
comp.Activated = true;
|
||||
}
|
||||
|
||||
private void SendPowerPulse(EntityUid target, EntityUid? user, EntityUid used)
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Damage.Systems;
|
||||
using Content.Server.Projectiles.Components;
|
||||
using Content.Server.Weapon.Melee;
|
||||
using Content.Server.Weapon.Ranged.Components;
|
||||
@@ -24,6 +25,7 @@ namespace Content.Server.Weapon.Ranged.Systems;
|
||||
public sealed partial class GunSystem : SharedGunSystem
|
||||
{
|
||||
[Dependency] private readonly EffectSystem _effects = default!;
|
||||
[Dependency] private readonly StaminaSystem _stamina = default!;
|
||||
|
||||
public const float DamagePitchVariation = MeleeWeaponSystem.DamagePitchVariation;
|
||||
|
||||
@@ -116,6 +118,9 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
var distance = result.Distance;
|
||||
FireEffects(fromCoordinates, distance, entityDirection.ToAngle(), hitscan, result.HitEntity);
|
||||
|
||||
if (hitscan.StaminaDamage > 0f)
|
||||
_stamina.TakeStaminaDamage(result.HitEntity, hitscan.StaminaDamage);
|
||||
|
||||
var dmg = hitscan.Damage;
|
||||
|
||||
if (dmg != null)
|
||||
|
||||
Reference in New Issue
Block a user