Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Station AI ability to electricute doors #32012

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
bc88447
Boom! Emergency access!
ScarKy0 Sep 5, 2024
60bd34f
Emergency access sound
ScarKy0 Sep 5, 2024
130a3ea
locale
ScarKy0 Sep 5, 2024
8d33a04
Updated sounds
ScarKy0 Sep 5, 2024
136f19b
bleh
ScarKy0 Sep 5, 2024
265fd07
Door electrify base
ScarKy0 Sep 6, 2024
a08fffc
feat: popups on attempt to activate AI action when wires cut
Sep 8, 2024
a5967bb
refactor: use SharedApcPowerReceiverComponent to check if AI can inte…
Sep 8, 2024
9050743
refactor: added icon and sound for door overcharge
Sep 8, 2024
846cf95
meta.json should use tabs not spaces
Sep 8, 2024
ac19543
refactor: extracted sounds for airlock overcharge to static field in …
Sep 8, 2024
fc889c4
refactor: cleanup, ScarKy0 mentions for resources
Sep 9, 2024
43261a5
refactor: removed unused textures
Sep 9, 2024
d899a5a
feat: now notification is displayed when AI attempting to interact wi…
Sep 10, 2024
36ff0ee
StationAiWhitelistComponent is properly gating BUI OnMessageAttempt,…
Sep 11, 2024
28f27fb
Merge remote-tracking branch 'origin/master' into feature/ai-door-ele…
Sep 12, 2024
9b2f203
Merge remote-tracking branch 'origin/master' into feature/ai-door-ele…
Sep 12, 2024
9d8d5d4
refactor: use PlayLocal to play electrify sound only for AI player
Sep 12, 2024
67e6875
refactor: SetBoltsDown now uses TrySetBoltDown, checks for power.
Sep 12, 2024
5dc4dfc
bolts now check for power using SharedPowerReceiverSystem
Sep 12, 2024
7483434
electrify localization and louder electrify sounds
Sep 12, 2024
0b81947
extracted ShowDeviceNotRespondingPopup, reverted airlocks not opening…
Sep 12, 2024
96c46fc
refactor: cleanup
Sep 16, 2024
1a7eac5
New sprites and fixes
ScarKy0 Sep 19, 2024
fdc01d3
Copyright
ScarKy0 Sep 19, 2024
64094f9
even more sprite changes
ScarKy0 Sep 20, 2024
c1f9f60
refactore: cleanup, rename overcharge => electrify
Sep 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 56 additions & 11 deletions Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,75 @@
using Content.Shared.Doors.Components;
using Content.Shared.Electrocution;
using Content.Shared.Silicons.StationAi;
using Robust.Shared.Utility;

namespace Content.Client.Silicons.StationAi;

public sealed partial class StationAiSystem
{
private readonly ResPath _aiActionsRsi = new ResPath("/Textures/Interface/Actions/actions_ai.rsi");

private void InitializeAirlock()
{
SubscribeLocalEvent<DoorBoltComponent, GetStationAiRadialEvent>(OnDoorBoltGetRadial);
SubscribeLocalEvent<AirlockComponent, GetStationAiRadialEvent>(OnEmergencyAccessGetRadial);
SubscribeLocalEvent<ElectrifiedComponent, GetStationAiRadialEvent>(OnDoorElectrifiedGetRadial);
}

private void OnDoorBoltGetRadial(Entity<DoorBoltComponent> ent, ref GetStationAiRadialEvent args)
{
args.Actions.Add(new StationAiRadial()
{
Sprite = ent.Comp.BoltsDown ?
new SpriteSpecifier.Rsi(
new ResPath("/Textures/Structures/Doors/Airlocks/Standard/basic.rsi"), "open") :
new SpriteSpecifier.Rsi(
new ResPath("/Textures/Structures/Doors/Airlocks/Standard/basic.rsi"), "closed"),
Tooltip = ent.Comp.BoltsDown ? Loc.GetString("bolt-open") : Loc.GetString("bolt-close"),
Event = new StationAiBoltEvent()
args.Actions.Add(
new StationAiRadial
{
Sprite = ent.Comp.BoltsDown
? new SpriteSpecifier.Rsi(_aiActionsRsi, "unbolt_door")
: new SpriteSpecifier.Rsi(_aiActionsRsi, "bolt_door"),
Tooltip = ent.Comp.BoltsDown
? Loc.GetString("bolt-open")
: Loc.GetString("bolt-close"),
Event = new StationAiBoltEvent
{
Bolted = !ent.Comp.BoltsDown,
}
}
);
}

private void OnEmergencyAccessGetRadial(Entity<AirlockComponent> ent, ref GetStationAiRadialEvent args)
{
args.Actions.Add(
new StationAiRadial
{
Sprite = ent.Comp.EmergencyAccess
? new SpriteSpecifier.Rsi(_aiActionsRsi, "emergency_off")
: new SpriteSpecifier.Rsi(_aiActionsRsi, "emergency_on"),
Tooltip = ent.Comp.EmergencyAccess
? Loc.GetString("emergency-access-off")
: Loc.GetString("emergency-access-on"),
Event = new StationAiEmergencyAccessEvent
{
EmergencyAccess = !ent.Comp.EmergencyAccess,
}
}
);
}

private void OnDoorElectrifiedGetRadial(Entity<ElectrifiedComponent> ent, ref GetStationAiRadialEvent args)
{
args.Actions.Add(
new StationAiRadial
{
Bolted = !ent.Comp.BoltsDown,
Sprite = ent.Comp.Enabled
? new SpriteSpecifier.Rsi(_aiActionsRsi, "door_overcharge_off")
: new SpriteSpecifier.Rsi(_aiActionsRsi, "door_overcharge_on"),
Tooltip = ent.Comp.Enabled
? Loc.GetString("bolt-open")
Fildrance marked this conversation as resolved.
Show resolved Hide resolved
: Loc.GetString("bolt-close"),
Event = new StationAiElectrifiedEvent
{
Electrified = !ent.Comp.Enabled,
}
}
});
);
}
}
10 changes: 5 additions & 5 deletions Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,22 +90,22 @@ private void AddTricksVerbs(GetVerbsEvent<Verb> args)
args.Verbs.Add(bolt);
}

if (TryComp<AirlockComponent>(args.Target, out var airlock))
if (TryComp<AirlockComponent>(args.Target, out var airlockComp))
{
Verb emergencyAccess = new()
{
Text = airlock.EmergencyAccess ? "Emergency Access Off" : "Emergency Access On",
Text = airlockComp.EmergencyAccess ? "Emergency Access Off" : "Emergency Access On",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/emergency_access.png")),
Act = () =>
{
_airlockSystem.ToggleEmergencyAccess(args.Target, airlock);
_airlockSystem.SetEmergencyAccess((args.Target, airlockComp), !airlockComp.EmergencyAccess);
},
Impact = LogImpact.Medium,
Message = Loc.GetString(airlock.EmergencyAccess
Message = Loc.GetString(airlockComp.EmergencyAccess
? "admin-trick-emergency-access-off-description"
: "admin-trick-emergency-access-on-description"),
Priority = (int) (airlock.EmergencyAccess ? TricksVerbPriorities.EmergencyAccessOff : TricksVerbPriorities.EmergencyAccessOn),
Priority = (int) (airlockComp.EmergencyAccess ? TricksVerbPriorities.EmergencyAccessOff : TricksVerbPriorities.EmergencyAccessOn),
};
args.Verbs.Add(emergencyAccess);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Content.Server.Electrocution;
using Content.Shared.Electrocution;
using Content.Shared.Construction;

namespace Content.Server.Construction.Completions;
Expand Down
1 change: 0 additions & 1 deletion Content.Server/Doors/WireActions/DoorBoltWireAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Content.Server.Wires;
using Content.Shared.Doors;
using Content.Shared.Doors.Components;
using Content.Shared.Doors.Systems;
using Content.Shared.Wires;

namespace Content.Server.Doors;
Expand Down
6 changes: 6 additions & 0 deletions Content.Server/Electrocution/ElectrocutionSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -488,4 +488,10 @@ private void PlayElectrocutionSound(EntityUid targetUid, EntityUid sourceUid, El
}
_audio.PlayPvs(electrified.ShockNoises, targetUid, AudioParams.Default.WithVolume(electrified.ShockVolume));
}

public void SetElectrifiedWireCut(Entity<ElectrifiedComponent> ent, bool value)
{
ent.Comp.IsWireCut = value;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should guard these values behind equals checks to avoid dirtying if the value hasn't changed because dirtying is expensive.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Dirty(ent);
}
}
2 changes: 2 additions & 0 deletions Content.Server/Power/PowerWireAction.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Content.Server.Electrocution;
using Content.Shared.Electrocution;
using Content.Server.Power.Components;
using Content.Server.Wires;
using Content.Shared.Power;
Expand Down Expand Up @@ -104,6 +105,7 @@ private void SetElectrified(EntityUid used, bool setting, ElectrifiedComponent?
&& !EntityManager.TryGetComponent(used, out electrified))
return;

_electrocutionSystem.SetElectrifiedWireCut((used, electrified), setting);
electrified.Enabled = setting;
}

Expand Down
2 changes: 1 addition & 1 deletion Content.Server/Remotes/DoorRemoteSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ private void OnBeforeInteract(Entity<DoorRemoteComponent> entity, ref BeforeRang
case OperatingMode.ToggleEmergencyAccess:
if (airlockComp != null)
{
_airlock.ToggleEmergencyAccess(args.Target.Value, airlockComp);
_airlock.SetEmergencyAccess((args.Target.Value, airlockComp), !airlockComp.EmergencyAccess);
_adminLogger.Add(LogType.Action, LogImpact.Medium,
$"{ToPrettyString(args.User):player} used {ToPrettyString(args.Used)} on {ToPrettyString(args.Target.Value)} to set emergency access {(airlockComp.EmergencyAccess ? "on" : "off")}");
}
Expand Down
13 changes: 13 additions & 0 deletions Content.Shared/Doors/Components/AirlockComponent.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Content.Shared.DeviceLinking;
using Content.Shared.Doors.Systems;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;

Expand All @@ -23,6 +24,18 @@ public sealed partial class AirlockComponent : Component
[ViewVariables(VVAccess.ReadWrite)]
[DataField, AutoNetworkedField]
public bool EmergencyAccess = false;

/// <summary>
/// Sound to play when the airlock emergency access is turned on.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public SoundSpecifier EmergencyOnSound = new SoundPathSpecifier("/Audio/Machines/airlock_emergencyon.ogg");

/// <summary>
/// Sound to play when the airlock emergency access is turned off.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public SoundSpecifier EmergencyOffSound = new SoundPathSpecifier("/Audio/Machines/airlock_emergencyoff.ogg");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VVRW is redundant with datafield.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


/// <summary>
/// Pry modifier for a powered airlock.
Expand Down
22 changes: 18 additions & 4 deletions Content.Shared/Doors/Systems/SharedAirlockSystem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Content.Shared.Doors.Components;
using Robust.Shared.Audio.Systems;
using Content.Shared.Popups;
using Content.Shared.Prying.Components;
using Content.Shared.Wires;
Expand All @@ -11,6 +12,7 @@ public abstract class SharedAirlockSystem : EntitySystem
[Dependency] protected readonly SharedDoorSystem DoorSystem = default!;
[Dependency] protected readonly SharedPopupSystem Popup = default!;
[Dependency] private readonly SharedWiresSystem _wiresSystem = default!;
[Dependency] protected readonly SharedAudioSystem Audio = default!;

public override void Initialize()
{
Expand Down Expand Up @@ -123,11 +125,23 @@ public void UpdateEmergencyLightStatus(EntityUid uid, AirlockComponent component
Appearance.SetData(uid, DoorVisuals.EmergencyLights, component.EmergencyAccess);
}

public void ToggleEmergencyAccess(EntityUid uid, AirlockComponent component)
public void SetEmergencyAccess(Entity<AirlockComponent> ent, bool value, EntityUid? user = null, bool predicted = false)
{
component.EmergencyAccess = !component.EmergencyAccess;
Dirty(uid, component); // This only runs on the server apparently so we need this.
UpdateEmergencyLightStatus(uid, component);
if(!ent.Comp.Powered)
return;

if (ent.Comp.EmergencyAccess == value)
return;

ent.Comp.EmergencyAccess = value;
Dirty(ent, ent.Comp); // This only runs on the server apparently so we need this.
UpdateEmergencyLightStatus(ent, ent.Comp);

var sound = ent.Comp.EmergencyAccess ? ent.Comp.EmergencyOnSound : ent.Comp.EmergencyOffSound;
if (predicted)
Audio.PlayPredicted(sound, ent, user: user);
else
Audio.PlayPvs(sound, ent);
}
Comment on lines +139 to 145
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do any of the callers even need this? This method is new...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AdminVerbSystem uses non-predicted version. is that wrong?


public void SetAutoCloseDelayModifier(AirlockComponent component, float value)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,121 +1,125 @@
using Robust.Shared.GameStates;
using Robust.Shared.Audio;

namespace Content.Server.Electrocution;
namespace Content.Shared.Electrocution;

/// <summary>
/// Component for things that shock users on touch.
/// </summary>
[RegisterComponent]
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class ElectrifiedComponent : Component
{
[DataField("enabled")]
[DataField, AutoNetworkedField]
public bool Enabled = true;

/// <summary>
/// Should player get damage on collide
/// </summary>
[DataField("onBump")]
[DataField, AutoNetworkedField]
public bool OnBump = true;

/// <summary>
/// Should player get damage on attack
/// </summary>
[DataField("onAttacked")]
[DataField, AutoNetworkedField]
public bool OnAttacked = true;

/// <summary>
/// When true - disables power if a window is present in the same tile
/// </summary>
[DataField("noWindowInTile")]
[DataField, AutoNetworkedField]
public bool NoWindowInTile = false;

/// <summary>
/// Should player get damage on interact with empty hand
/// </summary>
[DataField("onHandInteract")]
[DataField, AutoNetworkedField]
public bool OnHandInteract = true;

/// <summary>
/// Should player get damage on interact while holding an object in their hand
/// </summary>
[DataField("onInteractUsing")]
[DataField, AutoNetworkedField]
public bool OnInteractUsing = true;

/// <summary>
/// Indicates if the entity requires power to function
/// </summary>
[DataField("requirePower")]
[DataField, AutoNetworkedField]
public bool RequirePower = true;

/// <summary>
/// Indicates if the entity uses APC power
/// </summary>
[DataField("usesApcPower")]
[DataField, AutoNetworkedField]
public bool UsesApcPower = false;

/// <summary>
/// Identifier for the high voltage node.
/// </summary>
[DataField("highVoltageNode")]
[DataField, AutoNetworkedField]
public string? HighVoltageNode;

/// <summary>
/// Identifier for the medium voltage node.
/// </summary>
[DataField("mediumVoltageNode")]
[DataField, AutoNetworkedField]
public string? MediumVoltageNode;

/// <summary>
/// Identifier for the low voltage node.
/// </summary>
[DataField("lowVoltageNode")]
[DataField, AutoNetworkedField]
public string? LowVoltageNode;

/// <summary>
/// Damage multiplier for HV electrocution
/// </summary>
[DataField]
[DataField, AutoNetworkedField]
public float HighVoltageDamageMultiplier = 3f;

/// <summary>
/// Shock time multiplier for HV electrocution
/// </summary>
[DataField]
[DataField, AutoNetworkedField]
public float HighVoltageTimeMultiplier = 1.5f;

/// <summary>
/// Damage multiplier for MV electrocution
/// </summary>
[DataField]
[DataField, AutoNetworkedField]
public float MediumVoltageDamageMultiplier = 2f;

/// <summary>
/// Shock time multiplier for MV electrocution
/// </summary>
[DataField]
[DataField, AutoNetworkedField]
public float MediumVoltageTimeMultiplier = 1.25f;

[DataField("shockDamage")]
[DataField, AutoNetworkedField]
public float ShockDamage = 7.5f;

/// <summary>
/// Shock time, in seconds.
/// </summary>
[DataField("shockTime")]
[DataField, AutoNetworkedField]
public float ShockTime = 8f;

[DataField("siemensCoefficient")]
[DataField, AutoNetworkedField]
public float SiemensCoefficient = 1f;

[DataField("shockNoises")]
[DataField, AutoNetworkedField]
public SoundSpecifier ShockNoises = new SoundCollectionSpecifier("sparks");

[DataField("playSoundOnShock")]
[DataField, AutoNetworkedField]
public bool PlaySoundOnShock = true;

[DataField("shockVolume")]
[DataField, AutoNetworkedField]
public float ShockVolume = 20;

[DataField]
[DataField, AutoNetworkedField]
public float Probability = 1f;

[DataField, AutoNetworkedField]
public bool IsWireCut = false;
}
Loading
Loading