using Content.Shared.Ninja.Components; using Content.Shared.Weapons.Melee.Events; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Popups; using System.Diagnostics.CodeAnalysis; using Content.Shared.Damage.Systems; // DeltaV using Content.Shared.Stealth.Components; // DeltaV using Robust.Shared.Prototypes; // DeltaV namespace Content.Shared.Ninja.Systems; /// /// Provides shared ninja API, handles being attacked revealing ninja and stops guns from shooting. /// public abstract class SharedSpaceNinjaSystem : EntitySystem { [Dependency] protected readonly SharedNinjaSuitSystem Suit = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; // DeltaV public EntityQuery NinjaQuery; public override void Initialize() { base.Initialize(); NinjaQuery = GetEntityQuery(); // SubscribeLocalEvent(OnNinjaAttacked); // DeltaV - Handled by the DamageChangedEvent SubscribeLocalEvent(OnNinjaAttack); SubscribeLocalEvent(OnShotAttempted); SubscribeLocalEvent(OnNinjaAttacked); // DeltaV - Reveal the ninja on damage } public bool IsNinja([NotNullWhen(true)] EntityUid? uid) { return NinjaQuery.HasComp(uid); } /// /// Set the ninja's worn suit entity /// public void AssignSuit(Entity ent, EntityUid? suit) { if (ent.Comp.Suit == suit) return; ent.Comp.Suit = suit; Dirty(ent, ent.Comp); } /// /// Set the ninja's worn gloves entity /// public void AssignGloves(Entity ent, EntityUid? gloves) { if (ent.Comp.Gloves == gloves) return; ent.Comp.Gloves = gloves; Dirty(ent, ent.Comp); } /// /// Bind a katana entity to a ninja, letting it be recalled and dash. /// Does nothing if the player is not a ninja or already has a katana bound. /// public void BindKatana(Entity ent, EntityUid katana) { if (!NinjaQuery.Resolve(ent, ref ent.Comp, false) || ent.Comp.Katana != null) return; ent.Comp.Katana = katana; Dirty(ent, ent.Comp); } /// /// Gets the user's battery and tries to use some charge from it, returning true if successful. /// Serverside only. /// public virtual bool TryUseCharge(EntityUid user, float charge) { return false; } // DeltaV - Handled by the DamageChangedEvent /// /// Handle revealing ninja if cloaked when attacked. /// //private void OnNinjaAttacked(Entity ent, ref AttackedEvent args) //{ // TryRevealNinja(ent, disable: true); //} // END DeltaV /// /// DeltaV - Handle revealing ninja if cloaked when attacked by a hitscan attack. /// private void OnNinjaAttacked(Entity ent, ref DamageChangedEvent args) { // If there's no damage delta, just return if (args.DamageDelta is not { } damage) return; // Don't reveal on (most) healing if (!args.DamageIncreased) return; // If the damage doesn't have a source, we need to check the type, in case it // was a grenade or explosion. We want to ignore airloss and toxin damage types. if (!args.Origin.HasValue) { // If there are any negative values, its probably natual or chem healing, so don't reveal. It might be an OD from medicine. if (!damage.AnyPositive()) return; // Check the damage types for damage types that should reveal (brute, burns) // Basically, we want to ignore most indirect forms of damage (airloss, toxins) var damageGroups = damage.GetDamagePerGroup(_prototypeManager); if (!damageGroups.ContainsKey("Brute") && !damageGroups.ContainsKey("Burn")) // This feels a bit dirty, oh well. return; } // Only reveal on damage at least the minumum. This prevents tiny ticks of damage (e.g. from malign rifts pulses) if (damage.GetTotal() < ent.Comp.MinimumRevealDamage) return; // Yea, now reveal that son of a bitch >:3 TryRevealNinja(ent, disable: true); } /// /// Handle revealing ninja if cloaked when attacking. /// Only reveals, there is no cooldown. /// private void OnNinjaAttack(Entity ent, ref MeleeAttackEvent args) { TryRevealNinja(ent, disable: false); } private void TryRevealNinja(Entity ent, bool disable) { // DeltaV - Reveal ninja on damage if (ent.Comp.Suit is not {} uid) return; if (!TryComp(ent.Comp.Suit, out var suit)) return; if (!HasComp(ent)) // Only attempt to reveal if stealthed return; // END DeltaV Suit.RevealNinja((uid, suit), ent, disable: disable); } /// /// Require ninja to fight with HONOR, no guns! /// private void OnShotAttempted(Entity ent, ref ShotAttemptedEvent args) { Popup.PopupClient(Loc.GetString("gun-disabled"), ent, ent); args.Cancel(); } }