Greytide Virus station event (#33547)
* proof of concept * full implementation * I commited a crime * t * min players increase
This commit is contained in:
@@ -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();
|
||||
}
|
||||
96
Content.Server/StationEvents/Events/GreytideVirusRule.cs
Normal file
96
Content.Server/StationEvents/Events/GreytideVirusRule.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -396,6 +396,25 @@ public abstract partial class SharedDoorSystem : EntitySystem
|
||||
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)
|
||||
{
|
||||
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
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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: 25
|
||||
reoccurrenceDelay: 20
|
||||
- type: GreytideVirusRule
|
||||
accessGroups:
|
||||
- Cargo
|
||||
- Command
|
||||
- Engineering
|
||||
- Research
|
||||
- Security
|
||||
- Service
|
||||
blacklist:
|
||||
- External # don't space everything
|
||||
|
||||
Reference in New Issue
Block a user