mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-26 18:17:12 +03:00
* Automatic changelog update * Make NPC names proper nouns & fix some genders (#20534) * Proper names & genders * Uppercase proper names * Make Smile female * fix bingus wrinkly head (#20531) * rouny meat and steak (#20526) * lost friendship flavor * add rouny steak * rouny special meat * rouny meat textures --------- Co-authored-by: deltanedas <@deltanedas:kde.org> * Automatic changelog update * Wearable Wet Floor Sign and 'Janitorial Suicide Vest' (#20311) * Explosive wet floor sign & janitorial suicide vest * fix attributions * Remove name & desc from explosive wet floor sign * Make wet floor sign chameleonable * Automatic changelog update * Minor slippery stuff (#20535) * Update submodule to 162.2.0 (#20570) * Predicted armor (#20560) * clean up some lines in smile the slime prototype (#20552) * Revert "Use full file path for temp replays (#19002)" (#20545) * Add EyesGlasses into ClothesMate (#20523) * Automatic changelog update * Fix Punpun crew monitor sensor (#20484) * Automatic changelog update * EasyPry airlocks for arrivals. Now also prying refactor I guess (#19394) Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> * Automatic changelog update * Make arcades hackable again (#20555) * Automatic changelog update * Health alert tweaks (#20557) * Automatic changelog update * Add active and ended game rule components, generic TryRoundStartAttempt and minPlayers field (#20564) * Improve active game rule querying, add generic try round start attempt method, move minPlayers to GameRuleComponent * Nukeops todo and cleanup * Remove Active field * Add EndedGameRuleComponent * bartender suit (#20521) * give me a drink bartender * guh * aARG * Necropolis and mine walls (#20578) * Automatic changelog update * archaic accent tweaks (#20567) * Automatic changelog update * Add confirmation to kick and respawn in the admin player actions panel (#20542) * Remove windows-latest CI runs, remove unused build-test-release.yml (#20540) * Change .editorconfig to keep existing attribute arrangement (#20538) * Automatic changelog update * Catch replay start and end errors on round restarts (#20565) * Update trivial components to use auto comp states (#20539) * Fix role unbans not applying in real time (#20547) * Disable AHelp buttons when no player is selected, update button styling (#20568) * Automatic changelog update * Added a toggle fullscreen button (default F11) (#20272) * Added a toggle fullscreen button (default F11) * Removed un-needed comments * Review Requested Changes * Fixed Acidental Spacing Change * bwoink, removed extraneous code * nothing, litterally * Automatic changelog update * Fix not networking markings (#20588) * Automatic changelog update * Update RobustToolbox to v162.2.1 (#20590) * Automatic changelog update * Glorfcode (force say on damage/stun/crit) (#20562) * Automatic changelog update * Fix AHelp progressively showing more AHelp panels (#20591) * Automatic changelog update * caninsert entitystorage tweaks (#20589) * Automatic changelog update * Fix mapping actions crashing on load (#20592) * Fix loading a map with a ThirstComponent crashing the game (#20594) * Fix electrocution displaying real name of disguised players (#20603) * Organ fixes (#20488) * Automatic changelog update * Kettle medical rework (#20435) * Initial try at medical rework for kettle * Re-add decal decorations to Kettle's medical * Add more lockers to kettle's med * Actually upload kettle's map, not its proto * Automatic changelog update * Added blocked visuals to volumetric pump (#20610) * Automatic changelog update * Move TimedDespawn to engine (#20515) * Update submodule to 163.0.0 (#20616) * Remove v0.1 version number from local main menu screen (#20617) * Remove cloneData parameter from AutoNetworkedField (#20596) * Update submodule to 164.0.0 (#20618) * fix cognizine ghost role (#20632) Co-authored-by: deltanedas <@deltanedas:kde.org> * Automatic changelog update * Move ID layer one pixel to correct alignment (#20630) * Update nukie hardsuit descriptions (#20529) * Make holofans destructable (#20445) * Automatic changelog update * bowl is open (#20453) Co-authored-by: deltanedas <@deltanedas:kde.org> * Add TestPair.WaitCommand() (#20615) * Rename ThreatPrototype and mark fields as required (#20611) * Adjust hard bomb shape (#20608) * dragon refactor, objectives and use GenericAntag (#20201) Co-authored-by: deltanedas <@deltanedas:kde.org> * Automatic changelog update * Added generic empty liquids tank (#20563) * Move view variables verb to the top of the list with no category and localize it (#20546) * saltern update (#20325) Co-authored-by: deltanedas <@deltanedas:kde.org> * Fix followers leaking (#20643) * Add Winter Boots (#20622) * add * fix prototype * Add QM mantle (#20621) * add * fix prototype * Automatic changelog update * Slime mobs breathe nitrogen and resprite their organs (#20577) * Slimes breathe nitrogen and resprite their organs * ups * mmm * Automatic changelog update * Fix the new lizard horn's consistency + issue (#20620) * fix * add * Revert "add" This reverts commit a054a3204a8f185a94ceb80b1bd3bc9f30423711. * Add RandomHumanoidAppearance component to for space ninjas (#20605) * Automatic changelog update * Space cat breathes space (#20550) * Space cat breathes space Made Space Cat lungs ROBUST * Made Space Cat's lungs ROBUST 2.0 * Automatic changelog update * Wide anomaly locator (#20581) * Add files via upload * Add files via upload * Add files via upload * Add files via upload * add textures * fix encoding * fix 2 * Automatic changelog update * Fixed Telescopic Shield Lighting (#20650) * Fixed Telescopic Shield Lighting Bug fix #20199 * no need for these at all --------- Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> * Automatic changelog update * Add a special hardsuit for nukie medic (#20471) * Automatic changelog update * Carry over other mutations when doing species mutation (#20551) * Automatic changelog update * Add Spanish accent to poncho and sombrero (#20377) --------- Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com> Co-authored-by: Psychpsyo <60073468+Psychpsyo@users.noreply.github.com> Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com> Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Co-authored-by: ravage <142820619+ravage123321@users.noreply.github.com> Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com> Co-authored-by: LEVELcat <68501903+LEVELcat@users.noreply.github.com> Co-authored-by: Repo <47093363+Titian3@users.noreply.github.com> Co-authored-by: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> Co-authored-by: Doru991 <75124791+Doru991@users.noreply.github.com> Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com> Co-authored-by: Skarletto <122584947+Skarletto@users.noreply.github.com> Co-authored-by: NULL882 <104377798+NULL882@users.noreply.github.com> Co-authored-by: lunarcomets <140772713+lunarcomets@users.noreply.github.com> Co-authored-by: Miro Kavaliou <miraslauk@gmail.com> Co-authored-by: Kara <lunarautomaton6@gmail.com> Co-authored-by: Kacper Urbańczyk <kacperjaroslawurbanczyk@gmail.com> Co-authored-by: TsjipTsjip <19798667+TsjipTsjip@users.noreply.github.com> Co-authored-by: daerSeebaer <61566539+daerSeebaer@users.noreply.github.com> Co-authored-by: Fluffiest Floofers <thebluewulf@gmail.com> Co-authored-by: Flareguy <78941145+Flareguy@users.noreply.github.com> Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Co-authored-by: Kevin Zheng <kevinz5000@gmail.com> Co-authored-by: drteaspoon420 <87363733+drteaspoon420@users.noreply.github.com> Co-authored-by: Ubaser <134914314+UbaserB@users.noreply.github.com> Co-authored-by: Nim <128169402+Nimfar11@users.noreply.github.com> Co-authored-by: Kacper Urbańczyk <mikrel071204@gmail.com> Co-authored-by: Tox Cruize <141375638+TexCruize@users.noreply.github.com> Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com> Co-authored-by: Michael Cu <43478115+michaelcu@users.noreply.github.com>
350 lines
14 KiB
C#
350 lines
14 KiB
C#
using Content.Server.Atmos.Components;
|
|
using Content.Server.Atmos.EntitySystems;
|
|
using Content.Server.Atmos.Monitor.Systems;
|
|
using Content.Server.Popups;
|
|
using Content.Server.Power.Components;
|
|
using Content.Server.Power.EntitySystems;
|
|
using Content.Server.Remotes;
|
|
using Content.Server.Shuttles.Components;
|
|
using Content.Shared.Access.Components;
|
|
using Content.Shared.Access.Systems;
|
|
using Content.Shared.Atmos;
|
|
using Content.Shared.Atmos.Monitor;
|
|
using Content.Shared.Doors;
|
|
using Content.Shared.Doors.Components;
|
|
using Content.Shared.Doors.Systems;
|
|
using Content.Shared.Popups;
|
|
using Microsoft.Extensions.Options;
|
|
using Robust.Server.GameObjects;
|
|
using Robust.Shared.Map.Components;
|
|
using Robust.Shared.Player;
|
|
using Content.Shared.Prying.Components;
|
|
|
|
namespace Content.Server.Doors.Systems
|
|
{
|
|
public sealed class FirelockSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
|
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
|
|
[Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!;
|
|
[Dependency] private readonly AtmosphereSystem _atmosSystem = default!;
|
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
|
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
|
|
|
|
private static float _visualUpdateInterval = 0.5f;
|
|
private float _accumulatedFrameTime;
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
SubscribeLocalEvent<FirelockComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
|
|
SubscribeLocalEvent<FirelockComponent, GetPryTimeModifierEvent>(OnDoorGetPryTimeModifier);
|
|
SubscribeLocalEvent<FirelockComponent, DoorStateChangedEvent>(OnUpdateState);
|
|
|
|
SubscribeLocalEvent<FirelockComponent, BeforeDoorAutoCloseEvent>(OnBeforeDoorAutoclose);
|
|
SubscribeLocalEvent<FirelockComponent, AtmosAlarmEvent>(OnAtmosAlarm);
|
|
|
|
// Visuals
|
|
SubscribeLocalEvent<FirelockComponent, MapInitEvent>(UpdateVisuals);
|
|
SubscribeLocalEvent<FirelockComponent, ComponentStartup>(UpdateVisuals);
|
|
SubscribeLocalEvent<FirelockComponent, PowerChangedEvent>(PowerChanged);
|
|
}
|
|
|
|
private void PowerChanged(EntityUid uid, FirelockComponent component, ref PowerChangedEvent args)
|
|
{
|
|
// TODO this should REALLLLY not be door specific appearance thing.
|
|
_appearance.SetData(uid, DoorVisuals.Powered, args.Powered);
|
|
}
|
|
|
|
#region Visuals
|
|
private void UpdateVisuals(EntityUid uid, FirelockComponent component, EntityEventArgs args) => UpdateVisuals(uid, component);
|
|
|
|
public override void Update(float frameTime)
|
|
{
|
|
_accumulatedFrameTime += frameTime;
|
|
if (_accumulatedFrameTime < _visualUpdateInterval)
|
|
return;
|
|
|
|
_accumulatedFrameTime -= _visualUpdateInterval;
|
|
|
|
var airtightQuery = GetEntityQuery<AirtightComponent>();
|
|
var appearanceQuery = GetEntityQuery<AppearanceComponent>();
|
|
var xformQuery = GetEntityQuery<TransformComponent>();
|
|
|
|
foreach (var (firelock, door) in EntityQuery<FirelockComponent, DoorComponent>())
|
|
{
|
|
// only bother to check pressure on doors that are some variation of closed.
|
|
if (door.State != DoorState.Closed
|
|
&& door.State != DoorState.Welded
|
|
&& door.State != DoorState.Denying)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var uid = door.Owner;
|
|
if (airtightQuery.TryGetComponent(uid, out var airtight)
|
|
&& xformQuery.TryGetComponent(uid, out var xform)
|
|
&& appearanceQuery.TryGetComponent(uid, out var appearance))
|
|
{
|
|
var (fire, pressure) = CheckPressureAndFire(uid, firelock, xform, airtight, airtightQuery);
|
|
_appearance.SetData(uid, DoorVisuals.ClosedLights, fire || pressure, appearance);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateVisuals(EntityUid uid,
|
|
FirelockComponent? firelock = null,
|
|
DoorComponent? door = null,
|
|
AirtightComponent? airtight = null,
|
|
AppearanceComponent? appearance = null,
|
|
TransformComponent? xform = null)
|
|
{
|
|
if (!Resolve(uid, ref door, ref appearance, false))
|
|
return;
|
|
|
|
// only bother to check pressure on doors that are some variation of closed.
|
|
if (door.State != DoorState.Closed
|
|
&& door.State != DoorState.Welded
|
|
&& door.State != DoorState.Denying)
|
|
{
|
|
_appearance.SetData(uid, DoorVisuals.ClosedLights, false, appearance);
|
|
return;
|
|
}
|
|
|
|
var query = GetEntityQuery<AirtightComponent>();
|
|
if (!Resolve(uid, ref firelock, ref airtight, ref appearance, ref xform, false) || !query.Resolve(uid, ref airtight, false))
|
|
return;
|
|
|
|
var (fire, pressure) = CheckPressureAndFire(uid, firelock, xform, airtight, query);
|
|
_appearance.SetData(uid, DoorVisuals.ClosedLights, fire || pressure, appearance);
|
|
}
|
|
#endregion
|
|
|
|
public bool EmergencyPressureStop(EntityUid uid, FirelockComponent? firelock = null, DoorComponent? door = null)
|
|
{
|
|
if (!Resolve(uid, ref firelock, ref door))
|
|
return false;
|
|
|
|
if (door.State == DoorState.Open)
|
|
{
|
|
if (_doorSystem.TryClose(door.Owner, door))
|
|
{
|
|
return _doorSystem.OnPartialClose(door.Owner, door);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void OnBeforeDoorOpened(EntityUid uid, FirelockComponent component, BeforeDoorOpenedEvent args)
|
|
{
|
|
// Give the Door remote the ability to force a firelock open even if it is holding back dangerous gas
|
|
var overrideAccess = (args.User != null) && _accessReaderSystem.IsAllowed(args.User.Value, uid);
|
|
|
|
if (!this.IsPowered(uid, EntityManager) || (!overrideAccess && IsHoldingPressureOrFire(uid, component)))
|
|
args.Cancel();
|
|
}
|
|
|
|
private void OnDoorGetPryTimeModifier(EntityUid uid, FirelockComponent component, ref GetPryTimeModifierEvent args)
|
|
{
|
|
var state = CheckPressureAndFire(uid, component);
|
|
|
|
if (state.Fire)
|
|
{
|
|
_popupSystem.PopupEntity(Loc.GetString("firelock-component-is-holding-fire-message"),
|
|
uid, args.User, PopupType.MediumCaution);
|
|
}
|
|
else if (state.Pressure)
|
|
{
|
|
_popupSystem.PopupEntity(Loc.GetString("firelock-component-is-holding-pressure-message"),
|
|
uid, args.User, PopupType.MediumCaution);
|
|
}
|
|
|
|
if (state.Fire || state.Pressure)
|
|
args.PryTimeModifier *= component.LockedPryTimeModifier;
|
|
}
|
|
|
|
private void OnUpdateState(EntityUid uid, FirelockComponent component, DoorStateChangedEvent args)
|
|
{
|
|
var ev = new BeforeDoorAutoCloseEvent();
|
|
RaiseLocalEvent(uid, ev);
|
|
UpdateVisuals(uid, component, args);
|
|
if (ev.Cancelled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_doorSystem.SetNextStateChange(uid, component.AutocloseDelay);
|
|
}
|
|
|
|
private void OnBeforeDoorAutoclose(EntityUid uid, FirelockComponent component, BeforeDoorAutoCloseEvent args)
|
|
{
|
|
if (!this.IsPowered(uid, EntityManager))
|
|
args.Cancel();
|
|
|
|
// Make firelocks autoclose, but only if the last alarm type it
|
|
// remembers was a danger. This is to prevent people from
|
|
// flooding hallways with endless bad air/fire.
|
|
if (component.AlarmAutoClose &&
|
|
(_atmosAlarmable.TryGetHighestAlert(uid, out var alarm) && alarm != AtmosAlarmType.Danger || alarm == null))
|
|
args.Cancel();
|
|
}
|
|
|
|
private void OnAtmosAlarm(EntityUid uid, FirelockComponent component, AtmosAlarmEvent args)
|
|
{
|
|
if (!this.IsPowered(uid, EntityManager))
|
|
return;
|
|
|
|
if (!TryComp<DoorComponent>(uid, out var doorComponent))
|
|
return;
|
|
|
|
if (args.AlarmType == AtmosAlarmType.Normal || args.AlarmType == AtmosAlarmType.Warning)
|
|
{
|
|
if (doorComponent.State == DoorState.Closed)
|
|
_doorSystem.TryOpen(uid);
|
|
}
|
|
else if (args.AlarmType == AtmosAlarmType.Danger)
|
|
{
|
|
EmergencyPressureStop(uid, component, doorComponent);
|
|
}
|
|
}
|
|
|
|
public bool IsHoldingPressureOrFire(EntityUid uid, FirelockComponent firelock)
|
|
{
|
|
var result = CheckPressureAndFire(uid, firelock);
|
|
return result.Pressure || result.Fire;
|
|
}
|
|
|
|
public (bool Pressure, bool Fire) CheckPressureAndFire(EntityUid uid, FirelockComponent firelock)
|
|
{
|
|
var query = GetEntityQuery<AirtightComponent>();
|
|
if (query.TryGetComponent(uid, out AirtightComponent? airtight))
|
|
return CheckPressureAndFire(uid, firelock, Transform(uid), airtight, query);
|
|
return (false, false);
|
|
}
|
|
|
|
public (bool Pressure, bool Fire) CheckPressureAndFire(
|
|
EntityUid uid,
|
|
FirelockComponent firelock,
|
|
TransformComponent xform,
|
|
AirtightComponent airtight,
|
|
EntityQuery<AirtightComponent> airtightQuery)
|
|
{
|
|
if (!airtight.AirBlocked)
|
|
return (false, false);
|
|
|
|
if (TryComp(uid, out DockingComponent? dock) && dock.Docked)
|
|
{
|
|
// Currently docking automatically opens the doors. But maybe in future, check the pressure difference before opening doors?
|
|
return (false, false);
|
|
}
|
|
|
|
if (!TryComp(xform.ParentUid, out GridAtmosphereComponent? gridAtmosphere))
|
|
return (false, false);
|
|
|
|
var grid = Comp<MapGridComponent>(xform.ParentUid);
|
|
var pos = grid.CoordinatesToTile(xform.Coordinates);
|
|
var minPressure = float.MaxValue;
|
|
var maxPressure = float.MinValue;
|
|
var minTemperature = float.MaxValue;
|
|
var maxTemperature = float.MinValue;
|
|
bool holdingFire = false;
|
|
bool holdingPressure = false;
|
|
|
|
// We cannot simply use `_atmosSystem.GetAdjacentTileMixtures` because of how the `includeBlocked` option
|
|
// works, we want to ignore the firelock's blocking, while including blockers on other tiles.
|
|
// GetAdjacentTileMixtures also ignores empty/non-existent tiles, which we don't want. Additionally, for
|
|
// edge-fire locks, we only want to enumerate over a single directions. So AFAIK there is no nice way of
|
|
// achieving all this using existing atmos functions, and the functionality is too specialized to bother
|
|
// adding new public atmos system functions.
|
|
|
|
List<Vector2i> tiles = new(4);
|
|
List<AtmosDirection> directions = new(4);
|
|
for (var i = 0; i < Atmospherics.Directions; i++)
|
|
{
|
|
var dir = (AtmosDirection)(1 << i);
|
|
if (airtight.AirBlockedDirection.HasFlag(dir))
|
|
{
|
|
directions.Add(dir);
|
|
tiles.Add(pos.Offset(dir));
|
|
}
|
|
}
|
|
|
|
// May also have to consider pressure on the same tile as the firelock.
|
|
var count = tiles.Count;
|
|
if (airtight.AirBlockedDirection != AtmosDirection.All)
|
|
tiles.Add(pos);
|
|
|
|
var gasses = _atmosSystem.GetTileMixtures(gridAtmosphere.Owner, xform.MapUid, tiles);
|
|
if (gasses == null)
|
|
return (false, false);
|
|
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var gas = gasses[i];
|
|
var dir = directions[i];
|
|
var adjacentPos = tiles[i];
|
|
|
|
if (gas != null)
|
|
{
|
|
// Is there some airtight entity blocking this direction? If yes, don't include this direction in the
|
|
// pressure differential
|
|
if (HasAirtightBlocker(grid.GetAnchoredEntities(adjacentPos), dir.GetOpposite(), airtightQuery))
|
|
continue;
|
|
|
|
var p = gas.Pressure;
|
|
minPressure = Math.Min(minPressure, p);
|
|
maxPressure = Math.Max(maxPressure, p);
|
|
minTemperature = Math.Min(minTemperature, gas.Temperature);
|
|
maxTemperature = Math.Max(maxTemperature, gas.Temperature);
|
|
}
|
|
|
|
holdingPressure |= maxPressure - minPressure > firelock.PressureThreshold;
|
|
holdingFire |= maxTemperature - minTemperature > firelock.TemperatureThreshold;
|
|
|
|
if (holdingPressure && holdingFire)
|
|
return (holdingPressure, holdingFire);
|
|
}
|
|
|
|
if (airtight.AirBlockedDirection == AtmosDirection.All)
|
|
return (holdingPressure, holdingFire);
|
|
|
|
var local = gasses[count];
|
|
if (local != null)
|
|
{
|
|
var p = local.Pressure;
|
|
minPressure = Math.Min(minPressure, p);
|
|
maxPressure = Math.Max(maxPressure, p);
|
|
minTemperature = Math.Min(minTemperature, local.Temperature);
|
|
maxTemperature = Math.Max(maxTemperature, local.Temperature);
|
|
}
|
|
else
|
|
{
|
|
minPressure = Math.Min(minPressure, 0);
|
|
maxPressure = Math.Max(maxPressure, 0);
|
|
minTemperature = Math.Min(minTemperature, 0);
|
|
maxTemperature = Math.Max(maxTemperature, 0);
|
|
}
|
|
|
|
holdingPressure |= maxPressure - minPressure > firelock.PressureThreshold;
|
|
holdingFire |= maxTemperature - minTemperature > firelock.TemperatureThreshold;
|
|
|
|
return (holdingPressure, holdingFire);
|
|
}
|
|
|
|
private bool HasAirtightBlocker(IEnumerable<EntityUid> enumerable, AtmosDirection dir, EntityQuery<AirtightComponent> airtightQuery)
|
|
{
|
|
foreach (var ent in enumerable)
|
|
{
|
|
if (!airtightQuery.TryGetComponent(ent, out var airtight) || !airtight.AirBlocked)
|
|
continue;
|
|
|
|
if ((airtight.AirBlockedDirection & dir) == dir)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|