Hardbombs & Defusables (#15380)

Co-authored-by: Kara <lunarautomaton6@gmail.com>
Co-authored-by: Just-a-Unity-Dev <just-a-unity-dev@users.noreply.github.com>
Co-authored-by: LankLTE <twlowe06@gmail.com>
Co-authored-by: LankLTE <135308300+LankLTE@users.noreply.github.com>
This commit is contained in:
eclips_e
2023-09-12 09:42:38 +08:00
committed by GitHub
parent 75bf7bc6e8
commit 133ca98655
31 changed files with 1124 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
using Content.Server.Defusable.Systems;
using Content.Server.Explosion.Components;
using Robust.Shared.Audio;
namespace Content.Server.Defusable.Components;
/// <summary>
/// This is used for bombs that should be defused. The explosion configuration should be handled by <see cref="ExplosiveComponent"/>.
/// </summary>
[RegisterComponent, Access(typeof(DefusableSystem))]
public sealed partial class DefusableComponent : Component
{
/// <summary>
/// The bomb will play this sound on defusal.
/// </summary>
[ViewVariables(VVAccess.ReadOnly), DataField("defusalSound")]
public SoundSpecifier DefusalSound = new SoundPathSpecifier("/Audio/Misc/notice2.ogg");
/// <summary>
/// The bomb will play this sound on bolt.
/// </summary>
[ViewVariables(VVAccess.ReadOnly), DataField("boltSound")]
public SoundSpecifier BoltSound = new SoundPathSpecifier("/Audio/Machines/boltsdown.ogg");
/// <summary>
/// Is this bomb one use?
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("disposable")]
public bool Disposable = true;
/// <summary>
/// Is the bomb live? This is different from BombUsable because this tracks whether the bomb is ticking down or not.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("activated")]
public bool Activated;
/// <summary>
/// Is the bomb actually usable? This is different from Activated because this tracks whether the bomb can even start in the first place.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public bool Usable = true;
/// <summary>
/// Does the bomb show how much time remains?
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public bool DisplayTime = true;
/// <summary>
/// Is this bomb supposed to be stuck to the ground?
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public bool Bolted;
/// <summary>
/// How much time is added when the Activate wire is pulsed?
/// </summary>
[DataField("delayTime")]
public int DelayTime = 30;
#region Wires
// wires, this is so that they're one use
[ViewVariables(VVAccess.ReadWrite), Access(Other=AccessPermissions.ReadWrite)]
public bool DelayWireUsed;
[ViewVariables(VVAccess.ReadWrite), Access(Other=AccessPermissions.ReadWrite)]
public bool ProceedWireCut;
[ViewVariables(VVAccess.ReadWrite), Access(Other=AccessPermissions.ReadWrite)]
public bool ProceedWireUsed;
[ViewVariables(VVAccess.ReadWrite), Access(Other=AccessPermissions.ReadWrite)]
public bool ActivatedWireUsed;
#endregion
}

View File

@@ -0,0 +1,435 @@
using Content.Server.Defusable.Components;
using Content.Server.Explosion.Components;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Popups;
using Content.Server.Wires;
using Content.Shared.Administration.Logs;
using Content.Shared.Construction.Components;
using Content.Shared.Database;
using Content.Shared.Defusable;
using Content.Shared.Examine;
using Content.Shared.Popups;
using Content.Shared.Verbs;
using Content.Shared.Wires;
using Robust.Server.GameObjects;
namespace Content.Server.Defusable.Systems;
/// <inheritdoc/>
public sealed class DefusableSystem : SharedDefusableSystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly ExplosionSystem _explosion = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly TriggerSystem _trigger = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly WiresSystem _wiresSystem = default!;
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DefusableComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<DefusableComponent, GetVerbsEvent<AlternativeVerb>>(OnGetAltVerbs);
SubscribeLocalEvent<DefusableComponent, AnchorAttemptEvent>(OnAnchorAttempt);
SubscribeLocalEvent<DefusableComponent, UnanchorAttemptEvent>(OnUnanchorAttempt);
}
#region Subscribed Events
/// <summary>
/// Adds a verb allowing for the bomb to be started easily.
/// </summary>
private void OnGetAltVerbs(EntityUid uid, DefusableComponent comp, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanInteract || !args.CanAccess)
return;
args.Verbs.Add(new AlternativeVerb
{
Text = Loc.GetString("defusable-verb-begin"),
Disabled = comp is { Activated: true, Usable: true },
Priority = 10,
Act = () =>
{
TryStartCountdown(uid, args.User, comp);
}
});
}
private void OnExamine(EntityUid uid, DefusableComponent comp, ExaminedEvent args)
{
if (!args.IsInDetailsRange)
return;
if (!comp.Usable)
{
args.PushMarkup(Loc.GetString("defusable-examine-defused", ("name", uid)));
}
else if (comp.Activated && TryComp<ActiveTimerTriggerComponent>(uid, out var activeComp))
{
if (comp.DisplayTime)
{
args.PushMarkup(Loc.GetString("defusable-examine-live", ("name", uid),
("time", MathF.Floor(activeComp.TimeRemaining))));
}
else
{
args.PushMarkup(Loc.GetString("defusable-examine-live-display-off", ("name", uid)));
}
}
else
{
args.PushMarkup(Loc.GetString("defusable-examine-inactive", ("name", uid)));
}
args.PushMarkup(Loc.GetString("defusable-examine-bolts", ("down", comp.Bolted)));
}
private void OnAnchorAttempt(EntityUid uid, DefusableComponent component, AnchorAttemptEvent args)
{
if (CheckAnchorAttempt(uid, component, args))
args.Cancel();
}
private void OnUnanchorAttempt(EntityUid uid, DefusableComponent component, UnanchorAttemptEvent args)
{
if (CheckAnchorAttempt(uid, component, args))
args.Cancel();
}
private bool CheckAnchorAttempt(EntityUid uid, DefusableComponent component, BaseAnchoredAttemptEvent args)
{
// Don't allow the thing to be anchored if bolted to the ground
if (!component.Bolted)
return false;
var msg = Loc.GetString("defusable-popup-cant-anchor", ("name", uid));
_popup.PopupEntity(msg, uid, args.User);
return true;
}
#endregion
#region Public
public void TryStartCountdown(EntityUid uid, EntityUid user, DefusableComponent comp)
{
if (!comp.Usable)
{
_popup.PopupEntity(Loc.GetString("defusable-popup-fried", ("name", uid)), uid);
return;
}
var xform = Transform(uid);
if (!xform.Anchored)
_transform.AnchorEntity(uid, xform);
SetBolt(comp, true);
SetActivated(comp, true);
_popup.PopupEntity(Loc.GetString("defusable-popup-begun", ("name", uid)), uid);
if (TryComp<OnUseTimerTriggerComponent>(uid, out var timerTrigger))
{
_trigger.HandleTimerTrigger(
uid,
null,
timerTrigger.Delay,
timerTrigger.BeepInterval,
timerTrigger.InitialBeepDelay,
timerTrigger.BeepSound
);
}
RaiseLocalEvent(uid, new BombArmedEvent(uid));
_appearance.SetData(uid, DefusableVisuals.Active, comp.Activated);
_adminLogger.Add(LogType.Explosion, LogImpact.High,
$"{ToPrettyString(user):entity} begun a countdown on {ToPrettyString(uid):entity}");
if (TryComp<WiresPanelComponent>(uid, out var wiresPanelComponent))
_wiresSystem.TogglePanel(uid, wiresPanelComponent, false);
}
public void TryDetonateBomb(EntityUid uid, DefusableComponent comp)
{
if (!comp.Activated)
return;
_popup.PopupEntity(Loc.GetString("defusable-popup-boom", ("name", uid)), uid, PopupType.LargeCaution);
RaiseLocalEvent(uid, new BombDetonatedEvent(uid));
_explosion.TriggerExplosive(uid);
QueueDel(uid);
_appearance.SetData(uid, DefusableVisuals.Active, comp.Activated);
_adminLogger.Add(LogType.Explosion, LogImpact.High,
$"{ToPrettyString(uid):entity} has been detonated.");
}
public void TryDefuseBomb(EntityUid uid, DefusableComponent comp)
{
if (!comp.Activated)
return;
_popup.PopupEntity(Loc.GetString("defusable-popup-defuse", ("name", uid)), uid);
SetActivated(comp, false);
var xform = Transform(uid);
if (comp.Disposable)
{
SetUsable(comp, false);
RemComp<ExplodeOnTriggerComponent>(uid);
RemComp<OnUseTimerTriggerComponent>(uid);
}
RemComp<ActiveTimerTriggerComponent>(uid);
_audio.PlayPvs(comp.DefusalSound, uid);
RaiseLocalEvent(uid, new BombDefusedEvent(uid));
comp.ActivatedWireUsed = false;
comp.DelayWireUsed = false;
comp.ProceedWireCut = false;
comp.ProceedWireUsed = false;
comp.Bolted = false;
if (xform.Anchored)
_transform.Unanchor(uid, xform);
_appearance.SetData(uid, DefusableVisuals.Active, comp.Activated);
_adminLogger.Add(LogType.Explosion, LogImpact.High,
$"{ToPrettyString(uid):entity} has been defused!");
}
// jesus christ
public void SetUsable(DefusableComponent component, bool value)
{
component.Usable = value;
}
public void SetDisplayTime(DefusableComponent component, bool value)
{
component.DisplayTime = value;
}
/// <summary>
/// Sets the Activated value of a component to a value.
/// </summary>
/// <param name="component"></param>
/// <param name="value"></param>
/// <remarks>
/// Use <see cref="TryDefuseBomb"/> to defuse bomb. This is a setter.
/// </remarks>
public void SetActivated(DefusableComponent component, bool value)
{
component.Activated = value;
}
public void SetBolt(DefusableComponent component, bool value)
{
component.Bolted = value;
}
#endregion
#region Wires
public void DelayWirePulse(EntityUid user, Wire wire, DefusableComponent comp)
{
if (comp is not { Activated: true, DelayWireUsed: false })
return;
_trigger.TryDelay(wire.Owner, 30f);
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-chirp", ("name", wire.Owner)), wire.Owner);
comp.DelayWireUsed = true;
_adminLogger.Add(LogType.Explosion, LogImpact.High,
$"{ToPrettyString(user):user} pulsed the DeLAY wire of {ToPrettyString(wire.Owner):entity}.");
}
public bool ProceedWireCut(EntityUid user, Wire wire, DefusableComponent comp)
{
if (comp is not { Activated: true, ProceedWireCut: false })
return true;
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-proceed-pulse", ("name", wire.Owner)), wire.Owner);
SetDisplayTime(comp, false);
_adminLogger.Add(LogType.Explosion, LogImpact.High,
$"{ToPrettyString(user):user} cut the PRoCeeD wire of {ToPrettyString(wire.Owner):entity}.");
comp.ProceedWireCut = true;
return true;
}
public void ProceedWirePulse(EntityUid user, Wire wire, DefusableComponent comp)
{
if (comp is { Activated: true, ProceedWireUsed: false })
{
comp.ProceedWireUsed = true;
_trigger.TryDelay(wire.Owner, -15f);
}
_adminLogger.Add(LogType.Explosion, LogImpact.High,
$"{ToPrettyString(user):user} pulsed the PRoCeeD wire of {ToPrettyString(wire.Owner):entity}.");
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-proceed-pulse", ("name", wire.Owner)), wire.Owner);
}
public bool ActivateWireCut(EntityUid user, Wire wire, DefusableComponent comp)
{
// if you cut the wire it just defuses the bomb
if (comp.Activated)
{
TryDefuseBomb(wire.Owner, comp);
_adminLogger.Add(LogType.Explosion, LogImpact.High,
$"{ToPrettyString(user):user} has defused {ToPrettyString(wire.Owner):entity}!");
}
_adminLogger.Add(LogType.Explosion, LogImpact.High,
$"{ToPrettyString(user):user} cut the LIVE wire of {ToPrettyString(wire.Owner):entity}.");
return true;
}
public void ActivateWirePulse(EntityUid user, Wire wire, DefusableComponent comp)
{
// if the component isnt active, just start the countdown
// if it is and it isn't already used then delay it
if (comp.Activated)
{
if (!comp.ActivatedWireUsed)
{
_trigger.TryDelay(wire.Owner, 30f);
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-chirp", ("name", wire.Owner)), wire.Owner);
comp.ActivatedWireUsed = true;
_adminLogger.Add(LogType.Explosion, LogImpact.High,
$"{ToPrettyString(user):user} pulsed the LIVE wire of {ToPrettyString(wire.Owner):entity}.");
}
}
else
{
TryStartCountdown(wire.Owner, user, comp);
_adminLogger.Add(LogType.Explosion, LogImpact.High,
$"{ToPrettyString(user):user} pulsed the LIVE wire of {ToPrettyString(wire.Owner):entity} and begun the countdown.");
}
}
public bool BoomWireCut(EntityUid user, Wire wire, DefusableComponent comp)
{
if (comp.Activated)
{
EntityManager.System<DefusableSystem>().TryDetonateBomb(wire.Owner, comp);
_adminLogger.Add(LogType.Explosion, LogImpact.Extreme,
$"{ToPrettyString(user):user} cut the BOOM wire of {ToPrettyString(wire.Owner):entity} and caused it to detonate!");
}
else
{
EntityManager.System<DefusableSystem>().SetUsable(comp, false);
_adminLogger.Add(LogType.Explosion, LogImpact.High,
$"{ToPrettyString(user):user} cut the BOOM wire of {ToPrettyString(wire.Owner):entity}.");
}
return true;
}
public bool BoomWireMend(EntityUid user, Wire wire, DefusableComponent comp)
{
if (comp is { Activated: false, Usable: false })
{
SetUsable(comp, true);
_adminLogger.Add(LogType.Explosion, LogImpact.High,
$"{ToPrettyString(user):user} mended the BOOM wire of {ToPrettyString(wire.Owner):entity}.");
}
// you're already dead lol
return true;
}
public void BoomWirePulse(EntityUid user, Wire wire, DefusableComponent comp)
{
if (comp.Activated)
{
TryDetonateBomb(wire.Owner, comp);
}
_adminLogger.Add(LogType.Explosion, LogImpact.Extreme,
$"{ToPrettyString(user):user} pulsed the BOOM wire of {ToPrettyString(wire.Owner):entity} and caused it to detonate!");
}
public bool BoltWireMend(EntityUid user, Wire wire, DefusableComponent comp)
{
if (!comp.Activated)
return true;
SetBolt(comp, true);
_audio.PlayPvs(comp.BoltSound, wire.Owner);
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-bolt-pulse", ("name", wire.Owner)), wire.Owner);
_adminLogger.Add(LogType.Explosion, LogImpact.High,
$"{ToPrettyString(user):user} mended the BOLT wire of {ToPrettyString(wire.Owner):entity}!");
return true;
}
public bool BoltWireCut(EntityUid user, Wire wire, DefusableComponent comp)
{
if (!comp.Activated)
return true;
SetBolt(comp, false);
_audio.PlayPvs(comp.BoltSound, wire.Owner);
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-bolt-pulse", ("name", wire.Owner)), wire.Owner);
_adminLogger.Add(LogType.Explosion, LogImpact.High,
$"{ToPrettyString(user):user} cut the BOLT wire of {ToPrettyString(wire.Owner):entity}!");
return true;
}
public void BoltWirePulse(EntityUid user, Wire wire, DefusableComponent comp)
{
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-bolt-pulse", ("name", wire.Owner)), wire.Owner);
_adminLogger.Add(LogType.Explosion, LogImpact.High,
$"{ToPrettyString(user):user} pulsed the BOLT wire of {ToPrettyString(wire.Owner):entity}!");
}
#endregion
}
public sealed class BombDefusedEvent : EntityEventArgs
{
public EntityUid Entity;
public BombDefusedEvent(EntityUid entity)
{
Entity = entity;
}
}
public sealed class BombArmedEvent : EntityEventArgs
{
public EntityUid Entity;
public BombArmedEvent(EntityUid entity)
{
Entity = entity;
}
}
public sealed class BombDetonatedEvent : EntityEventArgs
{
public EntityUid Entity;
public BombDetonatedEvent(EntityUid entity)
{
Entity = entity;
}
}

View File

@@ -0,0 +1,42 @@
using Content.Server.Defusable.Components;
using Content.Server.Defusable.Systems;
using Content.Server.Doors.Systems;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Popups;
using Content.Server.Wires;
using Content.Shared.Defusable;
using Content.Shared.Doors;
using Content.Shared.Doors.Components;
using Content.Shared.Wires;
namespace Content.Server.Defusable.WireActions;
public sealed partial class ActivateWireAction : ComponentWireAction<DefusableComponent>
{
public override Color Color { get; set; } = Color.Lime;
public override string Name { get; set; } = "wire-name-bomb-live";
public override StatusLightState? GetLightState(Wire wire, DefusableComponent comp)
{
return comp.Activated ? StatusLightState.BlinkingFast : StatusLightState.Off;
}
public override object StatusKey { get; } = DefusableWireStatus.LiveIndicator;
public override bool Cut(EntityUid user, Wire wire, DefusableComponent comp)
{
return EntityManager.System<DefusableSystem>().ActivateWireCut(user, wire, comp);
}
public override bool Mend(EntityUid user, Wire wire, DefusableComponent comp)
{
// if its not disposable defusable system already handles* this
// *probably
return true;
}
public override void Pulse(EntityUid user, Wire wire, DefusableComponent comp)
{
EntityManager.System<DefusableSystem>().ActivateWirePulse(user, wire, comp);
}
}

View File

@@ -0,0 +1,38 @@
using Content.Server.Defusable.Components;
using Content.Server.Defusable.Systems;
using Content.Server.Popups;
using Content.Server.Wires;
using Content.Shared.Defusable;
using Content.Shared.Wires;
using Robust.Server.GameObjects;
namespace Content.Server.Defusable.WireActions;
public sealed partial class BoltWireAction : ComponentWireAction<DefusableComponent>
{
public override Color Color { get; set; } = Color.Red;
public override string Name { get; set; } = "wire-name-bomb-bolt";
public override bool LightRequiresPower { get; set; } = false;
public override StatusLightState? GetLightState(Wire wire, DefusableComponent comp)
{
return comp.Bolted ? StatusLightState.On : StatusLightState.Off;
}
public override object StatusKey { get; } = DefusableWireStatus.BoltIndicator;
public override bool Cut(EntityUid user, Wire wire, DefusableComponent comp)
{
return EntityManager.System<DefusableSystem>().BoltWireCut(user, wire, comp);
}
public override bool Mend(EntityUid user, Wire wire, DefusableComponent comp)
{
return EntityManager.System<DefusableSystem>().BoltWireMend(user, wire, comp);
}
public override void Pulse(EntityUid user, Wire wire, DefusableComponent comp)
{
EntityManager.System<DefusableSystem>().BoltWirePulse(user, wire, comp);
}
}

View File

@@ -0,0 +1,39 @@
using Content.Server.Defusable.Components;
using Content.Server.Defusable.Systems;
using Content.Server.Doors.Systems;
using Content.Server.Wires;
using Content.Shared.Defusable;
using Content.Shared.Doors;
using Content.Shared.Doors.Components;
using Content.Shared.Wires;
namespace Content.Server.Defusable.WireActions;
public sealed partial class BoomWireAction : ComponentWireAction<DefusableComponent>
{
public override Color Color { get; set; } = Color.Red;
public override string Name { get; set; } = "wire-name-bomb-boom";
public override bool LightRequiresPower { get; set; } = false;
public override StatusLightState? GetLightState(Wire wire, DefusableComponent comp)
{
return comp.Activated ? StatusLightState.On : StatusLightState.Off;
}
public override object StatusKey { get; } = DefusableWireStatus.BoomIndicator;
public override bool Cut(EntityUid user, Wire wire, DefusableComponent comp)
{
return EntityManager.System<DefusableSystem>().BoomWireCut(user, wire, comp);
}
public override bool Mend(EntityUid user, Wire wire, DefusableComponent comp)
{
return EntityManager.System<DefusableSystem>().BoomWireMend(user, wire, comp);
}
public override void Pulse(EntityUid user, Wire wire, DefusableComponent comp)
{
EntityManager.System<DefusableSystem>().BoomWirePulse(user, wire, comp);
}
}

View File

@@ -0,0 +1,44 @@
using Content.Server.Defusable.Components;
using Content.Server.Defusable.Systems;
using Content.Server.Doors.Systems;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Popups;
using Content.Server.Wires;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.Defusable;
using Content.Shared.Doors;
using Content.Shared.Doors.Components;
using Content.Shared.Wires;
using Robust.Server.GameObjects;
namespace Content.Server.Defusable.WireActions;
public sealed partial class DelayWireAction : ComponentWireAction<DefusableComponent>
{
public override Color Color { get; set; } = Color.Yellow;
public override string Name { get; set; } = "wire-name-bomb-delay";
public override bool LightRequiresPower { get; set; } = false;
public override StatusLightState? GetLightState(Wire wire, DefusableComponent comp)
{
return comp.DelayWireUsed ? StatusLightState.On : StatusLightState.Off;
}
public override object StatusKey { get; } = DefusableWireStatus.DelayIndicator;
public override bool Cut(EntityUid user, Wire wire, DefusableComponent comp)
{
return true;
}
public override bool Mend(EntityUid user, Wire wire, DefusableComponent comp)
{
return true;
}
public override void Pulse(EntityUid user, Wire wire, DefusableComponent comp)
{
EntityManager.System<DefusableSystem>().DelayWirePulse(user, wire, comp);
}
}

View File

@@ -0,0 +1,41 @@
using Content.Server.Defusable.Components;
using Content.Server.Defusable.Systems;
using Content.Server.Doors.Systems;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Popups;
using Content.Server.Wires;
using Content.Shared.Defusable;
using Content.Shared.Doors;
using Content.Shared.Doors.Components;
using Content.Shared.Wires;
namespace Content.Server.Defusable.WireActions;
public sealed partial class ProceedWireAction : ComponentWireAction<DefusableComponent>
{
public override Color Color { get; set; } = Color.Blue;
public override string Name { get; set; } = "wire-name-bomb-proceed";
public override bool LightRequiresPower { get; set; } = false;
public override StatusLightState? GetLightState(Wire wire, DefusableComponent comp)
{
return comp.Activated ? StatusLightState.Off : StatusLightState.BlinkingFast;
}
public override object StatusKey { get; } = DefusableWireStatus.ProceedIndicator;
public override bool Cut(EntityUid user, Wire wire, DefusableComponent comp)
{
return EntityManager.System<DefusableSystem>().ProceedWireCut(user, wire, comp);
}
public override bool Mend(EntityUid user, Wire wire, DefusableComponent comp)
{
return true;
}
public override void Pulse(EntityUid user, Wire wire, DefusableComponent comp)
{
EntityManager.System<DefusableSystem>().ProceedWirePulse(user, wire, comp);
}
}