Files
tbd-station-14/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs
metalgearsloth 8ab0e59db6 Refactor disarms (#36546)
* Refactor disarms

- Move client stuff to shared
- Cleanup a bunch of stuff
- Ref events
- Fix the swing sound mispredict (I noticed it on target dummies).

* Revert this change

* minor review

* Rebiew

---------

Co-authored-by: Milon <milonpl.git@proton.me>
2025-04-19 11:38:22 +10:00

123 lines
4.8 KiB
C#

using Content.Server.Chat.Systems;
using Content.Server.Movement.Systems;
using Content.Shared.Damage.Events;
using Content.Shared.Damage.Systems;
using Content.Shared.Effects;
using Content.Shared.Speech.Components;
using Content.Shared.Weapons.Melee;
using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.Map;
using Robust.Shared.Player;
using System.Linq;
using System.Numerics;
namespace Content.Server.Weapons.Melee;
public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
{
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly DamageExamineSystem _damageExamine = default!;
[Dependency] private readonly LagCompensationSystem _lag = default!;
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<MeleeSpeechComponent, MeleeHitEvent>(OnSpeechHit);
SubscribeLocalEvent<MeleeWeaponComponent, DamageExamineEvent>(OnMeleeExamineDamage);
}
private void OnMeleeExamineDamage(EntityUid uid, MeleeWeaponComponent component, ref DamageExamineEvent args)
{
if (component.Hidden)
return;
var damageSpec = GetDamage(uid, args.User, component);
if (damageSpec.Empty)
return;
_damageExamine.AddDamageExamine(args.Message, Damageable.ApplyUniversalAllModifiers(damageSpec), Loc.GetString("damage-melee"));
}
protected override bool ArcRaySuccessful(EntityUid targetUid,
Vector2 position,
Angle angle,
Angle arcWidth,
float range,
MapId mapId,
EntityUid ignore,
ICommonSession? session)
{
// Originally the client didn't predict damage effects so you'd intuit some level of how far
// in the future you'd need to predict, but then there was a lot of complaining like "why would you add artifical delay" as if ping is a choice.
// Now damage effects are predicted but for wide attacks it differs significantly from client and server so your game could be lying to you on hits.
// This isn't fair in the slightest because it makes ping a huge advantage and this would be a hidden system.
// Now the client tells us what they hit and we validate if it's plausible.
// Even if the client is sending entities they shouldn't be able to hit:
// A) Wide-damage is split anyway
// B) We run the same validation we do for click attacks.
// Could also check the arc though future effort + if they're aimbotting it's not really going to make a difference.
// (This runs lagcomp internally and is what clickattacks use)
if (!Interaction.InRangeUnobstructed(ignore, targetUid, range + 0.1f, overlapCheck: false))
return false;
// TODO: Check arc though due to the aforementioned aimbot + damage split comments it's less important.
return true;
}
protected override bool InRange(EntityUid user, EntityUid target, float range, ICommonSession? session)
{
EntityCoordinates targetCoordinates;
Angle targetLocalAngle;
if (session is { } pSession)
{
(targetCoordinates, targetLocalAngle) = _lag.GetCoordinatesAngle(target, pSession);
return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range, overlapCheck: false);
}
return Interaction.InRangeUnobstructed(user, target, range);
}
protected override void DoDamageEffect(List<EntityUid> targets, EntityUid? user, TransformComponent targetXform)
{
var filter = Filter.Pvs(targetXform.Coordinates, entityMan: EntityManager).RemoveWhereAttachedEntity(o => o == user);
_color.RaiseEffect(Color.Red, targets, filter);
}
public override void DoLunge(EntityUid user, EntityUid weapon, Angle angle, Vector2 localPos, string? animation, bool predicted = true)
{
Filter filter;
if (predicted)
{
filter = Filter.PvsExcept(user, entityManager: EntityManager);
}
else
{
filter = Filter.Pvs(user, entityManager: EntityManager);
}
RaiseNetworkEvent(new MeleeLungeEvent(GetNetEntity(user), GetNetEntity(weapon), angle, localPos, animation), filter);
}
private void OnSpeechHit(EntityUid owner, MeleeSpeechComponent comp, MeleeHitEvent args)
{
if (!args.IsHit ||
!args.HitEntities.Any())
{
return;
}
if (comp.Battlecry != null)//If the battlecry is set to empty, doesn't speak
{
_chat.TrySendInGameICMessage(args.User, comp.Battlecry, InGameICChatType.Speak, true, true, checkRadioPrefix: false); //Speech that isn't sent to chat or adminlogs
}
}
}