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);
|
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
|
#endregion
|
||||||
|
|
||||||
#region Closing
|
#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: ClericalError
|
||||||
- id: CockroachMigration
|
- id: CockroachMigration
|
||||||
- id: GasLeak
|
- id: GasLeak
|
||||||
|
- id: GreytideVirus
|
||||||
- id: IonStorm # its calm like 90% of the time smh
|
- id: IonStorm # its calm like 90% of the time smh
|
||||||
- id: KudzuGrowth
|
- id: KudzuGrowth
|
||||||
- id: MassHallucinations
|
- id: MassHallucinations
|
||||||
@@ -540,3 +541,24 @@
|
|||||||
maxOccurrences: 1 # this event has diminishing returns on interesting-ness, so we cap it
|
maxOccurrences: 1 # this event has diminishing returns on interesting-ness, so we cap it
|
||||||
weight: 5
|
weight: 5
|
||||||
- type: MobReplacementRule
|
- 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