using Robust.Shared.Random; using Content.Server.Electrocution; using Content.Server.Power.EntitySystems; using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.Wires; namespace Content.Server.Wires; /// [ImplicitDataDefinitionForInheritors] public abstract partial class BaseWireAction : IWireAction { private ISharedAdminLogManager _adminLogger = default!; /// /// The loc-string of the text that gets returned by . Also used for admin logging. /// [DataField("name")] public abstract string Name { get; set; } /// /// Default color that gets returned by . /// [DataField("color")] public abstract Color Color { get; set; } /// /// If true, the default behavior of will return an off-light when the /// wire owner is not powered. /// [DataField("lightRequiresPower")] public virtual bool LightRequiresPower { get; set; } = true; /// /// Nyanotrasen - The chance that the user is shocked when tampering with the wire: cutting, pulsing, or mending it. /// [DataField("shockChance")] public float ShockChance = 0.55f; /// /// Nyanotrasen - How much damage the user takes when tampering. /// [DataField("shockDamage")] public int ShockDamage = 15; /// /// Nyanotrasen - How long the user is stunned after a failed tamper attempt. /// [DataField("shockStunTime")] public TimeSpan ShockStunTime = TimeSpan.FromSeconds(3f); public virtual StatusLightData? GetStatusLightData(Wire wire) { if (LightRequiresPower && !IsPowered(wire.Owner)) return new StatusLightData(Color, StatusLightState.Off, Loc.GetString(Name)); var state = GetLightState(wire); return state == null ? null : new StatusLightData(Color, state.Value, Loc.GetString(Name)); } public virtual StatusLightState? GetLightState(Wire wire) => null; public IEntityManager EntityManager = default!; public IRobustRandom Random = default!; public WiresSystem WiresSystem = default!; public ElectrocutionSystem ElectrocutionSystem = default!; // not virtual so implementors are aware that they need a nullable here public abstract object? StatusKey { get; } // ugly, but IoC doesn't work during deserialization public virtual void Initialize() { EntityManager = IoCManager.Resolve(); _adminLogger = IoCManager.Resolve(); Random = IoCManager.Resolve(); WiresSystem = EntityManager.EntitySysManager.GetEntitySystem(); ElectrocutionSystem = EntityManager.EntitySysManager.GetEntitySystem(); } public virtual bool AddWire(Wire wire, int count) => count == 1; public virtual bool Cut(EntityUid user, Wire wire) => !TryShockUser(user, wire, "cutting") && Log(user, wire, "cut"); // Nyanotrasen - Tactical hacking public virtual bool Mend(EntityUid user, Wire wire) => !TryShockUser(user, wire, "mending") && Log(user, wire, "mended"); // Nyanotrasen - Tactical hacking public virtual void Pulse(EntityUid user, Wire wire) // Nyanotrasen - Tactical hacking { if (!TryShockUser(user, wire, "pulsing")) Log(user, wire, "pulsed"); } /// /// Nyanotrasen - Returns true if the user has been shocked. /// private bool TryShockUser(EntityUid user, Wire wire, string verb) { if (!IsPowered(wire.Owner)) return false; if (!Random.Prob(ShockChance)) return false; var shocked = ElectrocutionSystem.TryDoElectrocution(user, wire.Owner, ShockDamage, ShockStunTime, false); if (shocked) { var player = EntityManager.ToPrettyString(user); var owner = EntityManager.ToPrettyString(wire.Owner); var name = Loc.GetString(Name); var color = wire.Color.Name(); var action = GetType().Name; _adminLogger.Add(LogType.WireHacking, LogImpact.Medium, $"{player} shocked by {owner} when {verb} {color} {name} wire ({action})"); } return shocked; } private bool Log(EntityUid user, Wire wire, string verb) { var player = EntityManager.ToPrettyString(user); var owner = EntityManager.ToPrettyString(wire.Owner); var name = Loc.GetString(Name); var color = wire.Color.Name(); var action = GetType().Name; // logs something like "... mended red POWR wire (PowerWireAction) in ...." _adminLogger.Add(LogType.WireHacking, LogImpact.Medium, $"{player} {verb} {color} {name} wire ({action}) in {owner}"); return true; } public virtual void Update(Wire wire) { } /// /// Utility function to check if this given entity is powered. /// /// true if powered, false otherwise protected bool IsPowered(EntityUid uid) { return WiresSystem.IsPowered(uid, EntityManager); } }