-
Notifications
You must be signed in to change notification settings - Fork 3.8k
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
Greytide Virus station event #33547
Greytide Virus station event #33547
Changes from all commits
1eb931e
22b618a
e9b0e85
4e3a87a
ddf0ef5
1ee9f63
ad327c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
using Content.Server.StationEvents.Events; | ||
using Content.Shared.Access; | ||
using Content.Shared.Destructible.Thresholds; | ||
using Robust.Shared.Prototypes; | ||
|
||
namespace Content.Server.StationEvents.Components; | ||
|
||
/// <summary> | ||
/// Greytide Virus event specific configuration | ||
/// </summary> | ||
[RegisterComponent, Access(typeof(GreytideVirusRule))] | ||
public sealed partial class GreytideVirusRuleComponent : Component | ||
{ | ||
/// <summary> | ||
/// Range from which the severity is randomly picked from. | ||
/// </summary> | ||
[DataField] | ||
public MinMax SeverityRange = new(1, 3); | ||
|
||
/// <summary> | ||
/// Severity corresponding to the number of access groups affected. | ||
/// Will pick randomly from the SeverityRange if not specified. | ||
/// </summary> | ||
[DataField] | ||
public int? Severity; | ||
|
||
/// <summary> | ||
/// Access groups to pick from. | ||
/// </summary> | ||
[DataField] | ||
public List<ProtoId<AccessGroupPrototype>> AccessGroups = new(); | ||
|
||
/// <summary> | ||
/// Entities with this access level will be ignored. | ||
/// </summary> | ||
[DataField] | ||
public List<ProtoId<AccessLevelPrototype>> Blacklist = new(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
using Content.Server.StationEvents.Components; | ||
using Content.Shared.Access; | ||
using Content.Shared.Access.Systems; | ||
using Content.Shared.Access.Components; | ||
using Content.Shared.Doors.Components; | ||
using Content.Shared.Doors.Systems; | ||
using Content.Shared.Lock; | ||
using Content.Shared.GameTicking.Components; | ||
using Robust.Shared.Prototypes; | ||
using Robust.Shared.Random; | ||
|
||
namespace Content.Server.StationEvents.Events; | ||
|
||
|
||
/// <summary> | ||
/// Greytide Virus event | ||
/// This will open and bolt airlocks and unlock lockers from randomly selected access groups. | ||
/// </summary> | ||
public sealed class GreytideVirusRule : StationEventSystem<GreytideVirusRuleComponent> | ||
{ | ||
[Dependency] private readonly AccessReaderSystem _access = default!; | ||
[Dependency] private readonly SharedDoorSystem _door = default!; | ||
[Dependency] private readonly LockSystem _lock = default!; | ||
[Dependency] private readonly IPrototypeManager _prototype = default!; | ||
[Dependency] private readonly IRobustRandom _random = default!; | ||
|
||
protected override void Added(EntityUid uid, GreytideVirusRuleComponent virusComp, GameRuleComponent gameRule, GameRuleAddedEvent args) | ||
{ | ||
if (!TryComp<StationEventComponent>(uid, out var stationEvent)) | ||
return; | ||
|
||
// pick severity randomly from range if not specified otherwise | ||
virusComp.Severity ??= virusComp.SeverityRange.Next(_random); | ||
virusComp.Severity = Math.Min(virusComp.Severity.Value, virusComp.AccessGroups.Count); | ||
|
||
stationEvent.StartAnnouncement = Loc.GetString("station-event-greytide-virus-start-announcement", ("severity", virusComp.Severity.Value)); | ||
base.Added(uid, virusComp, gameRule, args); | ||
} | ||
protected override void Started(EntityUid uid, GreytideVirusRuleComponent virusComp, GameRuleComponent gameRule, GameRuleStartedEvent args) | ||
{ | ||
base.Started(uid, virusComp, gameRule, args); | ||
|
||
if (virusComp.Severity == null) | ||
return; | ||
|
||
// pick random access groups | ||
var chosen = _random.GetItems(virusComp.AccessGroups, virusComp.Severity.Value, allowDuplicates: false); | ||
|
||
// combine all the selected access groups | ||
var accessIds = new HashSet<ProtoId<AccessLevelPrototype>>(); | ||
foreach (var group in chosen) | ||
{ | ||
if (_prototype.TryIndex(group, out var proto)) | ||
accessIds.UnionWith(proto.Tags); | ||
} | ||
|
||
var firelockQuery = GetEntityQuery<FirelockComponent>(); | ||
var accessQuery = GetEntityQuery<AccessReaderComponent>(); | ||
|
||
var lockQuery = AllEntityQuery<LockComponent>(); | ||
while (lockQuery.MoveNext(out var lockUid, out var lockComp)) | ||
{ | ||
if (!accessQuery.TryComp(lockUid, out var accessComp)) | ||
continue; | ||
|
||
// check access | ||
// the AreAccessTagsAllowed function is a little weird because it technically has support for certain tags to be locked out of opening something | ||
// which might have unintened side effects (see the comments in the function itself) | ||
// but no one uses that yet, so it is fine for now | ||
if (!_access.AreAccessTagsAllowed(accessIds, accessComp) || _access.AreAccessTagsAllowed(virusComp.Blacklist, accessComp)) | ||
continue; | ||
|
||
// open lockers | ||
_lock.Unlock(lockUid, null, lockComp); | ||
} | ||
|
||
var airlockQuery = AllEntityQuery<AirlockComponent, DoorComponent>(); | ||
while (airlockQuery.MoveNext(out var airlockUid, out var airlockComp, out var doorComp)) | ||
{ | ||
// don't space everything | ||
if (firelockQuery.HasComp(airlockUid)) | ||
continue; | ||
|
||
// use the access reader from the door electronics if they exist | ||
if (!_access.GetMainAccessReader(airlockUid, out var accessComp)) | ||
continue; | ||
|
||
// check access | ||
if (!_access.AreAccessTagsAllowed(accessIds, accessComp) || _access.AreAccessTagsAllowed(virusComp.Blacklist, accessComp)) | ||
continue; | ||
|
||
// open and bolt airlocks | ||
_door.TryOpenAndBolt(airlockUid, doorComp, airlockComp); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -396,6 +396,25 @@ public void OnPartialOpen(EntityUid uid, DoorComponent? door = null) | |
Dirty(uid, door); | ||
|
||
} | ||
|
||
/// <summary> | ||
/// Opens and then bolts a door. | ||
/// Different from emagging this does not remove the access reader, so it can be repaired by simply unbolting the door. | ||
/// </summary> | ||
public bool TryOpenAndBolt(EntityUid uid, DoorComponent? door = null, AirlockComponent? airlock = null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i suppose you could realize emagdoor method via this new one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No you can't. In the emag version the check for being unbolted and powered happens in a separate event, so that it does not consume a charge if it fails. |
||
{ | ||
if (!Resolve(uid, ref door, ref airlock)) | ||
return false; | ||
|
||
if (IsBolted(uid) || !airlock.Powered || door.State != DoorState.Closed) | ||
{ | ||
return false; | ||
} | ||
|
||
SetState(uid, DoorState.Emagging, door); | ||
|
||
return true; | ||
} | ||
#endregion | ||
|
||
#region Closing | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
station-event-greytide-virus-start-announcement = Gr3y.T1d3 virus detected in the station's secure locking encryption subroutines. Severity level of { $severity }. Recommend station AI involvement. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should be getting rid of such hardcoded strings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move it into the component maybe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine as it's just the code-side of text that's customisable via localisation.