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

Greytide Virus station event #33547

Merged
merged 7 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
38 changes: 38 additions & 0 deletions Content.Server/StationEvents/Components/GreytideVirusComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Content.Server.StationEvents.Events;
using Content.Shared.Destructible.Thresholds;
using Content.Shared.Access;
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();
}
96 changes: 96 additions & 0 deletions Content.Server/StationEvents/Events/GreytideVirusRule.cs
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));
Copy link
Contributor

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

Copy link
Member Author

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?

Copy link
Contributor

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.

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);
}
}
}
19 changes: 19 additions & 0 deletions Content.Shared/Doors/Systems/SharedDoorSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

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

i suppose you could realize emagdoor method via this new one

Copy link
Member Author

Choose a reason for hiding this comment

The 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
Expand Down
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.
22 changes: 22 additions & 0 deletions Resources/Prototypes/GameRules/events.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- id: ClericalError
- id: CockroachMigration
- id: GasLeak
- id: GreytideVirus
- id: IonStorm # its calm like 90% of the time smh
- id: KudzuGrowth
- id: MassHallucinations
Expand Down Expand Up @@ -540,3 +541,24 @@
maxOccurrences: 1 # this event has diminishing returns on interesting-ness, so we cap it
weight: 5
- type: MobReplacementRule

- type: entity
id: GreyTideVirus
parent: BaseStationEventShortDelay
components:
- type: StationEvent
startAudio:
path: /Audio/Announcements/attention.ogg
weight: 5
minimumPlayers: 10
reoccurrenceDelay: 20
- type: GreytideVirusRule
accessGroups:
- Cargo
- Command
- Engineering
- Research
- Security
- Service
blacklist:
- External # don't space everything
Loading