mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-02-15 00:54:51 +01:00
Merge remote-tracking branch 'upstream/master' into upstream-sync
# Conflicts: # Content.Client/Preferences/ClientPreferencesManager.cs # Content.Server/Connection/ConnectionManager.cs # Content.Server/Preferences/Managers/ServerPreferencesManager.cs # Content.Shared/Preferences/HumanoidCharacterProfile.cs # Content.Shared/Preferences/ICharacterProfile.cs # Resources/Prototypes/Catalog/VendingMachines/Inventories/janidrobe.yml # Resources/Prototypes/Datasets/Names/fortunes.yml # Resources/Prototypes/Entities/Clothing/Shoes/specific.yml # Resources/Textures/Structures/Wallmounts/posters.rsi/meta.json # SpaceStation14.sln
This commit is contained in:
@@ -2,6 +2,7 @@ using Content.Client.Message;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -14,6 +15,10 @@ public sealed class InjectorStatusControl : Control
|
||||
private readonly SharedSolutionContainerSystem _solutionContainers;
|
||||
private readonly RichTextLabel _label;
|
||||
|
||||
private FixedPoint2 PrevVolume;
|
||||
private FixedPoint2 PrevMaxVolume;
|
||||
private InjectorToggleMode PrevToggleState;
|
||||
|
||||
public InjectorStatusControl(Entity<InjectorComponent> parent, SharedSolutionContainerSystem solutionContainers)
|
||||
{
|
||||
_parent = parent;
|
||||
@@ -29,6 +34,16 @@ public sealed class InjectorStatusControl : Control
|
||||
if (!_solutionContainers.TryGetSolution(_parent.Owner, InjectorComponent.SolutionName, out _, out var solution))
|
||||
return;
|
||||
|
||||
// only updates the UI if any of the details are different than they previously were
|
||||
if (PrevVolume == solution.Volume
|
||||
&& PrevMaxVolume == solution.MaxVolume
|
||||
&& PrevToggleState == _parent.Comp.ToggleState)
|
||||
return;
|
||||
|
||||
PrevVolume = solution.Volume;
|
||||
PrevMaxVolume = solution.MaxVolume;
|
||||
PrevToggleState = _parent.Comp.ToggleState;
|
||||
|
||||
// Update current volume and injector state
|
||||
var modeStringLocalized = Loc.GetString(_parent.Comp.ToggleState switch
|
||||
{
|
||||
|
||||
@@ -32,19 +32,22 @@
|
||||
StyleClasses="OpenLeft"/>
|
||||
</BoxContainer>
|
||||
<Control MinSize="0 10"/>
|
||||
<ScrollContainer HScrollEnabled="False" HorizontalExpand="True" VerticalExpand="True" MinSize="0 160">
|
||||
<PanelContainer VerticalExpand="True"
|
||||
SizeFlagsStretchRatio="6"
|
||||
MinSize="0 150">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#1b1b1e" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Name="ContainerInfo"
|
||||
Orientation="Vertical"
|
||||
HorizontalExpand="True">
|
||||
<Label Text="{Loc 'reagent-dispenser-window-no-container-loaded-text'}"/>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</ScrollContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<SpriteView Name="View" Scale="4 4" MinSize="150 150"/>
|
||||
<ScrollContainer HScrollEnabled="False" HorizontalExpand="True" VerticalExpand="True" MinSize="0 160">
|
||||
<PanelContainer VerticalExpand="True"
|
||||
SizeFlagsStretchRatio="6"
|
||||
MinSize="0 150">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#1b1b1e" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Name="ContainerInfo"
|
||||
Orientation="Vertical"
|
||||
HorizontalExpand="True">
|
||||
<Label Text="{Loc 'reagent-dispenser-window-no-container-loaded-text'}"/>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Linq;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
@@ -19,6 +18,7 @@ namespace Content.Client.Chemistry.UI
|
||||
public sealed partial class ReagentDispenserWindow : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
public event Action<BaseButton.ButtonEventArgs, DispenseReagentButton>? OnDispenseReagentButtonPressed;
|
||||
public event Action<GUIMouseHoverEventArgs, DispenseReagentButton>? OnDispenseReagentButtonMouseEntered;
|
||||
public event Action<GUIMouseHoverEventArgs, DispenseReagentButton>? OnDispenseReagentButtonMouseExited;
|
||||
@@ -52,7 +52,7 @@ namespace Content.Client.Chemistry.UI
|
||||
/// Update the button grid of reagents which can be dispensed.
|
||||
/// </summary>
|
||||
/// <param name="inventory">Reagents which can be dispensed by this dispenser</param>
|
||||
public void UpdateReagentsList(List<KeyValuePair<string, KeyValuePair<string,string>>> inventory)
|
||||
public void UpdateReagentsList(List<KeyValuePair<string, KeyValuePair<string, string>>> inventory)
|
||||
{
|
||||
if (ChemicalList == null)
|
||||
return;
|
||||
@@ -86,6 +86,9 @@ namespace Content.Client.Chemistry.UI
|
||||
UpdateContainerInfo(castState);
|
||||
UpdateReagentsList(castState.Inventory);
|
||||
|
||||
_entityManager.TryGetEntity(castState.OutputContainerEntity, out var outputContainerEnt);
|
||||
View.SetEntity(outputContainerEnt);
|
||||
|
||||
// Disable the Clear & Eject button if no beaker
|
||||
ClearButton.Disabled = castState.OutputContainer is null;
|
||||
EjectButton.Disabled = castState.OutputContainer is null;
|
||||
@@ -134,7 +137,7 @@ namespace Content.Client.Chemistry.UI
|
||||
|
||||
if (state.OutputContainer is null)
|
||||
{
|
||||
ContainerInfo.Children.Add(new Label {Text = Loc.GetString("reagent-dispenser-window-no-container-loaded-text") });
|
||||
ContainerInfo.Children.Add(new Label { Text = Loc.GetString("reagent-dispenser-window-no-container-loaded-text") });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -159,11 +162,11 @@ namespace Content.Client.Chemistry.UI
|
||||
? p.LocalizedName
|
||||
: Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text");
|
||||
|
||||
var nameLabel = new Label {Text = $"{localizedName}: "};
|
||||
var nameLabel = new Label { Text = $"{localizedName}: " };
|
||||
var quantityLabel = new Label
|
||||
{
|
||||
Text = Loc.GetString("reagent-dispenser-window-quantity-label-text", ("quantity", quantity)),
|
||||
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor},
|
||||
StyleClasses = { StyleNano.StyleClassLabelSecondaryColor },
|
||||
};
|
||||
|
||||
ContainerInfo.Children.Add(new BoxContainer
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<LangVersion>11</LangVersion>
|
||||
<LangVersion>12</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>..\bin\Content.Client\</OutputPath>
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.CrewManifest;
|
||||
using Content.Shared.CrewManifest;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Client.CrewManifest.UI;
|
||||
|
||||
public sealed class CrewManifestListing : BoxContainer
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
@@ -25,7 +21,7 @@ public sealed class CrewManifestListing : BoxContainer
|
||||
|
||||
public void AddCrewManifestEntries(CrewManifestEntries entries)
|
||||
{
|
||||
var entryDict = new Dictionary<string, List<CrewManifestEntry>>();
|
||||
var entryDict = new Dictionary<DepartmentPrototype, List<CrewManifestEntry>>();
|
||||
|
||||
foreach (var entry in entries.Entries)
|
||||
{
|
||||
@@ -34,37 +30,19 @@ public sealed class CrewManifestListing : BoxContainer
|
||||
// this is a little expensive, and could be better
|
||||
if (department.Roles.Contains(entry.JobPrototype))
|
||||
{
|
||||
entryDict.GetOrNew(department.ID).Add(entry);
|
||||
entryDict.GetOrNew(department).Add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var entryList = new List<(string section, List<CrewManifestEntry> entries)>();
|
||||
var entryList = new List<(DepartmentPrototype section, List<CrewManifestEntry> entries)>();
|
||||
|
||||
foreach (var (section, listing) in entryDict)
|
||||
{
|
||||
entryList.Add((section, listing));
|
||||
}
|
||||
|
||||
var sortOrder = _configManager.GetCVar(CCVars.CrewManifestOrdering).Split(",").ToList();
|
||||
|
||||
entryList.Sort((a, b) =>
|
||||
{
|
||||
var ai = sortOrder.IndexOf(a.section);
|
||||
var bi = sortOrder.IndexOf(b.section);
|
||||
|
||||
// this is up here so -1 == -1 occurs first
|
||||
if (ai == bi)
|
||||
return 0;
|
||||
|
||||
if (ai == -1)
|
||||
return -1;
|
||||
|
||||
if (bi == -1)
|
||||
return 1;
|
||||
|
||||
return ai.CompareTo(bi);
|
||||
});
|
||||
entryList.Sort((a, b) => DepartmentUIComparer.Instance.Compare(a.section, b.section));
|
||||
|
||||
foreach (var item in entryList)
|
||||
{
|
||||
|
||||
@@ -4,24 +4,25 @@ using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Roles;
|
||||
|
||||
namespace Content.Client.CrewManifest.UI;
|
||||
|
||||
public sealed class CrewManifestSection : BoxContainer
|
||||
{
|
||||
public CrewManifestSection(IPrototypeManager prototypeManager, SpriteSystem spriteSystem, string sectionTitle,
|
||||
public CrewManifestSection(
|
||||
IPrototypeManager prototypeManager,
|
||||
SpriteSystem spriteSystem,
|
||||
DepartmentPrototype section,
|
||||
List<CrewManifestEntry> entries)
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical;
|
||||
HorizontalExpand = true;
|
||||
|
||||
if (Loc.TryGetString($"department-{sectionTitle}", out var localizedDepart))
|
||||
sectionTitle = localizedDepart;
|
||||
|
||||
AddChild(new Label()
|
||||
{
|
||||
StyleClasses = { "LabelBig" },
|
||||
Text = Loc.GetString(sectionTitle)
|
||||
Text = Loc.GetString($"department-{section.ID}")
|
||||
});
|
||||
|
||||
var gridContainer = new GridContainer()
|
||||
@@ -55,8 +56,9 @@ public sealed class CrewManifestSection : BoxContainer
|
||||
var icon = new TextureRect()
|
||||
{
|
||||
TextureScale = new Vector2(2, 2),
|
||||
Stretch = TextureRect.StretchMode.KeepCentered,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
Texture = spriteSystem.Frame0(jobIcon.Icon),
|
||||
Margin = new Thickness(0, 0, 4, 0)
|
||||
};
|
||||
|
||||
titleContainer.AddChild(icon);
|
||||
|
||||
@@ -50,16 +50,8 @@ namespace Content.Client.Decals
|
||||
protected override void OnDecalRemoved(EntityUid gridId, uint decalId, DecalGridComponent component, Vector2i indices, DecalChunk chunk)
|
||||
{
|
||||
base.OnDecalRemoved(gridId, decalId, component, indices, chunk);
|
||||
|
||||
if (!component.DecalZIndexIndex.Remove(decalId, out var zIndex))
|
||||
return;
|
||||
|
||||
if (!component.DecalRenderIndex.TryGetValue(zIndex, out var renderIndex))
|
||||
return;
|
||||
|
||||
renderIndex.Remove(decalId);
|
||||
if (renderIndex.Count == 0)
|
||||
component.DecalRenderIndex.Remove(zIndex);
|
||||
DebugTools.Assert(chunk.Decals.ContainsKey(decalId));
|
||||
chunk.Decals.Remove(decalId);
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid gridUid, DecalGridComponent gridComp, ref ComponentHandleState args)
|
||||
@@ -133,8 +125,6 @@ namespace Content.Client.Decals
|
||||
private void UpdateChunks(EntityUid gridId, DecalGridComponent gridComp, Dictionary<Vector2i, DecalChunk> updatedGridChunks)
|
||||
{
|
||||
var chunkCollection = gridComp.ChunkCollection.ChunkCollection;
|
||||
var renderIndex = gridComp.DecalRenderIndex;
|
||||
var zIndexIndex = gridComp.DecalZIndexIndex;
|
||||
|
||||
// Update any existing data / remove decals we didn't receive data for.
|
||||
foreach (var (indices, newChunkData) in updatedGridChunks)
|
||||
@@ -155,11 +145,6 @@ namespace Content.Client.Decals
|
||||
|
||||
foreach (var (uid, decal) in newChunkData.Decals)
|
||||
{
|
||||
if (zIndexIndex.TryGetValue(uid, out var zIndex))
|
||||
renderIndex[zIndex].Remove(uid);
|
||||
|
||||
renderIndex.GetOrNew(decal.ZIndex)[uid] = decal;
|
||||
zIndexIndex[uid] = decal.ZIndex;
|
||||
gridComp.DecalIndex[uid] = indices;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Enumerators;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Decals.Overlays
|
||||
@@ -15,6 +16,8 @@ namespace Content.Client.Decals.Overlays
|
||||
|
||||
private readonly Dictionary<string, (Texture Texture, bool SnapCardinals)> _cachedTextures = new(64);
|
||||
|
||||
private readonly List<(uint Id, Decal Decal)> _decals = new();
|
||||
|
||||
public DecalOverlay(
|
||||
SpriteSystem sprites,
|
||||
IEntityManager entManager,
|
||||
@@ -30,10 +33,10 @@ namespace Content.Client.Decals.Overlays
|
||||
if (args.MapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
var grid = Grid;
|
||||
var owner = Grid.Owner;
|
||||
|
||||
if (!_entManager.TryGetComponent(grid, out DecalGridComponent? decalGrid) ||
|
||||
!_entManager.TryGetComponent(grid, out TransformComponent? xform))
|
||||
if (!_entManager.TryGetComponent(owner, out DecalGridComponent? decalGrid) ||
|
||||
!_entManager.TryGetComponent(owner, out TransformComponent? xform))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -46,46 +49,68 @@ namespace Content.Client.Decals.Overlays
|
||||
var xformSystem = _entManager.System<TransformSystem>();
|
||||
var eyeAngle = args.Viewport.Eye?.Rotation ?? Angle.Zero;
|
||||
|
||||
var zIndexDictionary = decalGrid.DecalRenderIndex;
|
||||
var gridAABB = xformSystem.GetInvWorldMatrix(xform).TransformBox(args.WorldBounds.Enlarged(1f));
|
||||
var chunkEnumerator = new ChunkIndicesEnumerator(gridAABB, SharedDecalSystem.ChunkSize);
|
||||
_decals.Clear();
|
||||
|
||||
if (zIndexDictionary.Count == 0)
|
||||
while (chunkEnumerator.MoveNext(out var index))
|
||||
{
|
||||
if (!decalGrid.ChunkCollection.ChunkCollection.TryGetValue(index.Value, out var chunk))
|
||||
continue;
|
||||
|
||||
foreach (var (id, decal) in chunk.Decals)
|
||||
{
|
||||
if (!gridAABB.Contains(decal.Coordinates))
|
||||
continue;
|
||||
|
||||
_decals.Add((id, decal));
|
||||
}
|
||||
}
|
||||
|
||||
if (_decals.Count == 0)
|
||||
return;
|
||||
|
||||
var (_, worldRot, worldMatrix) = xformSystem.GetWorldPositionRotationMatrix(xform);
|
||||
_decals.Sort((x, y) =>
|
||||
{
|
||||
var zComp = x.Decal.ZIndex.CompareTo(y.Decal.ZIndex);
|
||||
|
||||
if (zComp != 0)
|
||||
return zComp;
|
||||
|
||||
return x.Id.CompareTo(y.Id);
|
||||
});
|
||||
|
||||
var (_, worldRot, worldMatrix) = xformSystem.GetWorldPositionRotationMatrix(xform);
|
||||
handle.SetTransform(worldMatrix);
|
||||
|
||||
foreach (var decals in zIndexDictionary.Values)
|
||||
foreach (var (_, decal) in _decals)
|
||||
{
|
||||
foreach (var decal in decals.Values)
|
||||
if (!_cachedTextures.TryGetValue(decal.Id, out var cache))
|
||||
{
|
||||
if (!_cachedTextures.TryGetValue(decal.Id, out var cache))
|
||||
// Nothing to cache someone messed up
|
||||
if (!_prototypeManager.TryIndex<DecalPrototype>(decal.Id, out var decalProto))
|
||||
{
|
||||
// Nothing to cache someone messed up
|
||||
if (!_prototypeManager.TryIndex<DecalPrototype>(decal.Id, out var decalProto))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
cache = (_sprites.Frame0(decalProto.Sprite), decalProto.SnapCardinals);
|
||||
_cachedTextures[decal.Id] = cache;
|
||||
continue;
|
||||
}
|
||||
|
||||
var cardinal = Angle.Zero;
|
||||
|
||||
if (cache.SnapCardinals)
|
||||
{
|
||||
var worldAngle = eyeAngle + worldRot;
|
||||
cardinal = worldAngle.GetCardinalDir().ToAngle();
|
||||
}
|
||||
|
||||
var angle = decal.Angle - cardinal;
|
||||
|
||||
if (angle.Equals(Angle.Zero))
|
||||
handle.DrawTexture(cache.Texture, decal.Coordinates, decal.Color);
|
||||
else
|
||||
handle.DrawTexture(cache.Texture, decal.Coordinates, angle, decal.Color);
|
||||
cache = (_sprites.Frame0(decalProto.Sprite), decalProto.SnapCardinals);
|
||||
_cachedTextures[decal.Id] = cache;
|
||||
}
|
||||
|
||||
var cardinal = Angle.Zero;
|
||||
|
||||
if (cache.SnapCardinals)
|
||||
{
|
||||
var worldAngle = eyeAngle + worldRot;
|
||||
cardinal = worldAngle.GetCardinalDir().ToAngle();
|
||||
}
|
||||
|
||||
var angle = decal.Angle - cardinal;
|
||||
|
||||
if (angle.Equals(Angle.Zero))
|
||||
handle.DrawTexture(cache.Texture, decal.Coordinates, decal.Color);
|
||||
else
|
||||
handle.DrawTexture(cache.Texture, decal.Coordinates, angle, decal.Color);
|
||||
}
|
||||
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Content.Client.Wires.Visualizers;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Content.Shared.Prying.Components;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
@@ -16,13 +15,6 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<AirlockComponent, ComponentStartup>(OnComponentStartup);
|
||||
SubscribeLocalEvent<AirlockComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
SubscribeLocalEvent<AirlockComponent, BeforePryEvent>(OnAirlockPryAttempt);
|
||||
}
|
||||
|
||||
private void OnAirlockPryAttempt(EntityUid uid, AirlockComponent component, ref BeforePryEvent args)
|
||||
{
|
||||
// TODO: Temporary until airlocks predicted.
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnComponentStartup(EntityUid uid, AirlockComponent comp, ComponentStartup args)
|
||||
@@ -104,7 +96,7 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
||||
|| state == DoorState.Denying
|
||||
|| (state == DoorState.Open && comp.OpenUnlitVisible)
|
||||
|| (_appearanceSystem.TryGetData<bool>(uid, DoorVisuals.ClosedLights, out var closedLights, args.Component) && closedLights))
|
||||
&& !boltedVisible && !emergencyLightsVisible; ;
|
||||
&& !boltedVisible && !emergencyLightsVisible;
|
||||
}
|
||||
|
||||
args.Sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, unlitVisible);
|
||||
@@ -120,5 +112,17 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
||||
&& !boltedVisible
|
||||
);
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case DoorState.Open:
|
||||
args.Sprite.LayerSetState(DoorVisualLayers.BaseUnlit, comp.ClosingSpriteState);
|
||||
args.Sprite.LayerSetAnimationTime(DoorVisualLayers.BaseUnlit, 0);
|
||||
break;
|
||||
case DoorState.Closed:
|
||||
args.Sprite.LayerSetState(DoorVisualLayers.BaseUnlit, comp.OpeningSpriteState);
|
||||
args.Sprite.LayerSetAnimationTime(DoorVisualLayers.BaseUnlit, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using Content.Client.Wires.Visualizers;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Doors;
|
||||
|
||||
public sealed class DoorBoltSystem : SharedDoorBoltSystem
|
||||
{
|
||||
// Instantiate sub-class on client for prediction.
|
||||
}
|
||||
@@ -3,8 +3,6 @@ using Content.Shared.Doors.Systems;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -73,7 +71,7 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
|
||||
private void OnAppearanceChange(EntityUid uid, DoorComponent comp, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null || !_gameTiming.IsFirstTimePredicted)
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if(!AppearanceSystem.TryGetData<DoorState>(uid, DoorVisuals.State, out var state, args.Component))
|
||||
@@ -83,9 +81,9 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
{
|
||||
if (!_resourceCache.TryGetResource<RSIResource>(SpriteSpecifierSerializer.TextureRoot / baseRsi, out var res))
|
||||
{
|
||||
Logger.Error("Unable to load RSI '{0}'. Trace:\n{1}", baseRsi, Environment.StackTrace);
|
||||
Log.Error("Unable to load RSI '{0}'. Trace:\n{1}", baseRsi, Environment.StackTrace);
|
||||
}
|
||||
foreach (ISpriteLayer layer in args.Sprite.AllLayers)
|
||||
foreach (var layer in args.Sprite.AllLayers)
|
||||
{
|
||||
layer.Rsi = res?.RSI;
|
||||
}
|
||||
@@ -113,31 +111,24 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
break;
|
||||
case DoorState.Opening:
|
||||
if (animPlayer != null && comp.OpeningAnimationTime != 0.0)
|
||||
_animationSystem.Play(uid, animPlayer, (Animation)comp.OpeningAnimation, DoorComponent.AnimationKey);
|
||||
_animationSystem.Play((uid, animPlayer), (Animation)comp.OpeningAnimation, DoorComponent.AnimationKey);
|
||||
break;
|
||||
case DoorState.Closing:
|
||||
if (animPlayer != null && comp.ClosingAnimationTime != 0.0 && comp.CurrentlyCrushing.Count == 0)
|
||||
_animationSystem.Play(uid, animPlayer, (Animation)comp.ClosingAnimation, DoorComponent.AnimationKey);
|
||||
_animationSystem.Play((uid, animPlayer), (Animation)comp.ClosingAnimation, DoorComponent.AnimationKey);
|
||||
break;
|
||||
case DoorState.Denying:
|
||||
if (animPlayer != null && comp.DenyingAnimation != default)
|
||||
_animationSystem.Play(uid, animPlayer, (Animation)comp.DenyingAnimation, DoorComponent.AnimationKey);
|
||||
if (animPlayer != null)
|
||||
_animationSystem.Play((uid, animPlayer), (Animation)comp.DenyingAnimation, DoorComponent.AnimationKey);
|
||||
break;
|
||||
case DoorState.Welded:
|
||||
break;
|
||||
case DoorState.Emagging:
|
||||
if (animPlayer != null && comp.EmaggingAnimation != default)
|
||||
_animationSystem.Play(uid, animPlayer, (Animation)comp.EmaggingAnimation, DoorComponent.AnimationKey);
|
||||
if (animPlayer != null)
|
||||
_animationSystem.Play((uid, animPlayer), (Animation)comp.EmaggingAnimation, DoorComponent.AnimationKey);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"Invalid door visual state {state}");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO AUDIO PREDICT see comments in server-side PlaySound()
|
||||
protected override void PlaySound(EntityUid uid, SoundSpecifier soundSpecifier, AudioParams audioParams, EntityUid? predictingPlayer, bool predicted)
|
||||
{
|
||||
if (GameTiming.InPrediction && GameTiming.IsFirstTimePredicted)
|
||||
Audio.PlayEntity(soundSpecifier, Filter.Local(), uid, false, audioParams);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.CrewManifest;
|
||||
using Content.Client.GameTicking.Managers;
|
||||
@@ -159,8 +160,10 @@ namespace Content.Client.LateJoin
|
||||
};
|
||||
|
||||
var firstCategory = true;
|
||||
var departments = _prototypeManager.EnumeratePrototypes<DepartmentPrototype>().ToArray();
|
||||
Array.Sort(departments, DepartmentUIComparer.Instance);
|
||||
|
||||
foreach (var department in _prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
|
||||
foreach (var department in departments)
|
||||
{
|
||||
var departmentName = Loc.GetString($"department-{department.ID}");
|
||||
_jobCategories[id] = new Dictionary<string, BoxContainer>();
|
||||
@@ -176,7 +179,7 @@ namespace Content.Client.LateJoin
|
||||
jobsAvailable.Add(_prototypeManager.Index<JobPrototype>(jobId));
|
||||
}
|
||||
|
||||
jobsAvailable.Sort((x, y) => -string.Compare(x.LocalizedName, y.LocalizedName, StringComparison.CurrentCultureIgnoreCase));
|
||||
jobsAvailable.Sort(JobUIComparer.Instance);
|
||||
|
||||
// Do not display departments with no jobs available.
|
||||
if (jobsAvailable.Count == 0)
|
||||
@@ -231,7 +234,7 @@ namespace Content.Client.LateJoin
|
||||
var icon = new TextureRect
|
||||
{
|
||||
TextureScale = new Vector2(2, 2),
|
||||
Stretch = TextureRect.StretchMode.KeepCentered
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
|
||||
var jobIcon = _prototypeManager.Index<StatusIconPrototype>(prototype.Icon);
|
||||
|
||||
@@ -265,7 +265,7 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
||||
var jobIcon = new TextureRect()
|
||||
{
|
||||
TextureScale = new Vector2(2f, 2f),
|
||||
Stretch = TextureRect.StretchMode.KeepCentered,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
Texture = _spriteSystem.Frame0(proto.Icon),
|
||||
Margin = new Thickness(5, 0, 5, 0),
|
||||
};
|
||||
|
||||
7
Content.Client/Nutrition/EntitySystems/OpenableSystem.cs
Normal file
7
Content.Client/Nutrition/EntitySystems/OpenableSystem.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
|
||||
namespace Content.Client.Nutrition.EntitySystems;
|
||||
|
||||
public sealed class OpenableSystem : SharedOpenableSystem
|
||||
{
|
||||
}
|
||||
@@ -4,8 +4,10 @@ using System.Linq;
|
||||
using Content.Corvax.Interfaces.Client;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Client;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Preferences
|
||||
@@ -19,6 +21,8 @@ namespace Content.Client.Preferences
|
||||
{
|
||||
[Dependency] private readonly IClientNetManager _netManager = default!;
|
||||
[Dependency] private readonly IBaseClient _baseClient = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
private IClientSponsorsManager? _sponsorsManager; // Corvax-Sponsors
|
||||
|
||||
public event Action? OnServerDataLoaded;
|
||||
@@ -65,7 +69,7 @@ namespace Content.Client.Preferences
|
||||
{
|
||||
// Corvax-Sponsors-Start
|
||||
var sponsorPrototypes = _sponsorsManager?.Prototypes.ToArray() ?? new string[]{};
|
||||
profile.EnsureValid(sponsorPrototypes);
|
||||
profile.EnsureValid(_cfg, _prototypes, sponsorPrototypes);
|
||||
// Corvax-Sponsors-End
|
||||
var characters = new Dictionary<int, ICharacterProfile>(Preferences.Characters) {[slot] = profile};
|
||||
Preferences = new PlayerPreferences(characters, Preferences.SelectedCharacterIndex, Preferences.AdminOOCColor);
|
||||
|
||||
@@ -553,10 +553,8 @@ namespace Content.Client.Preferences.UI
|
||||
_jobCategories.Clear();
|
||||
var firstCategory = true;
|
||||
|
||||
var departments = _prototypeManager.EnumeratePrototypes<DepartmentPrototype>()
|
||||
.OrderByDescending(department => department.Weight)
|
||||
.ThenBy(department => Loc.GetString($"department-{department.ID}"))
|
||||
.ToList();
|
||||
var departments = _prototypeManager.EnumeratePrototypes<DepartmentPrototype>().ToArray();
|
||||
Array.Sort(departments, DepartmentUIComparer.Instance);
|
||||
|
||||
foreach (var department in departments)
|
||||
{
|
||||
@@ -604,9 +602,8 @@ namespace Content.Client.Preferences.UI
|
||||
|
||||
var jobs = department.Roles.Select(jobId => _prototypeManager.Index<JobPrototype>(jobId))
|
||||
.Where(job => job.SetPreference)
|
||||
.OrderByDescending(job => job.Weight)
|
||||
.ThenBy(job => job.LocalizedName)
|
||||
.ToList();
|
||||
.ToArray();
|
||||
Array.Sort(jobs, JobUIComparer.Instance);
|
||||
|
||||
foreach (var job in jobs)
|
||||
{
|
||||
@@ -1339,7 +1336,7 @@ namespace Content.Client.Preferences.UI
|
||||
var icon = new TextureRect
|
||||
{
|
||||
TextureScale = new Vector2(2, 2),
|
||||
Stretch = TextureRect.StretchMode.KeepCentered
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
var jobIcon = protoMan.Index<StatusIconPrototype>(proto.Icon);
|
||||
icon.Texture = jobIcon.Icon.Frame0();
|
||||
|
||||
@@ -233,6 +233,8 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
||||
|
||||
if (args.Function == ContentKeyFunctions.MoveStoredItem)
|
||||
{
|
||||
DraggingRotation = control.Location.Rotation;
|
||||
|
||||
_menuDragHelper.MouseDown(control);
|
||||
_menuDragHelper.Update(0f);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<OutputPath>..\bin\Content.IntegrationTests\</OutputPath>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<LangVersion>11</LangVersion>
|
||||
<LangVersion>12</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NUnit" />
|
||||
|
||||
@@ -239,25 +239,13 @@ namespace Content.IntegrationTests.Tests
|
||||
|
||||
if (entManager.HasComponent<StationJobsComponent>(station))
|
||||
{
|
||||
// Test that the map has valid latejoin spawn points
|
||||
// Test that the map has valid latejoin spawn points or container spawn points
|
||||
if (!NoSpawnMaps.Contains(mapProto))
|
||||
{
|
||||
var lateSpawns = 0;
|
||||
|
||||
var query = entManager.AllEntityQueryEnumerator<SpawnPointComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (comp.SpawnType != SpawnPointType.LateJoin
|
||||
|| !xformQuery.TryGetComponent(uid, out var xform)
|
||||
|| xform.GridUid == null
|
||||
|| !gridUids.Contains(xform.GridUid.Value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
lateSpawns++;
|
||||
break;
|
||||
}
|
||||
lateSpawns += GetCountLateSpawn<SpawnPointComponent>(gridUids, entManager);
|
||||
lateSpawns += GetCountLateSpawn<ContainerSpawnPointComponent>(gridUids, entManager);
|
||||
|
||||
Assert.That(lateSpawns, Is.GreaterThan(0), $"Found no latejoin spawn points on {mapProto}");
|
||||
}
|
||||
@@ -296,6 +284,32 @@ namespace Content.IntegrationTests.Tests
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static int GetCountLateSpawn<T>(List<EntityUid> gridUids, IEntityManager entManager)
|
||||
where T : ISpawnPoint, IComponent
|
||||
{
|
||||
var resultCount = 0;
|
||||
var queryPoint = entManager.AllEntityQueryEnumerator<T, TransformComponent>();
|
||||
#nullable enable
|
||||
while (queryPoint.MoveNext(out T? comp, out var xform))
|
||||
{
|
||||
var spawner = (ISpawnPoint) comp;
|
||||
|
||||
if (spawner.SpawnType is not SpawnPointType.LateJoin
|
||||
|| xform.GridUid == null
|
||||
|| !gridUids.Contains(xform.GridUid.Value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#nullable disable
|
||||
resultCount++;
|
||||
break;
|
||||
}
|
||||
|
||||
return resultCount;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task AllMapsTested()
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
<LangVersion>12</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>..\bin\Content.Replay\</OutputPath>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<LangVersion>11</LangVersion>
|
||||
<LangVersion>12</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>..\bin\Content.Server.Database\</OutputPath>
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Content.Server.Administration.Systems;
|
||||
|
||||
public sealed partial class AdminVerbSystem
|
||||
{
|
||||
[Dependency] private readonly DoorBoltSystem _boltsSystem = default!;
|
||||
[Dependency] private readonly DoorSystem _door = default!;
|
||||
[Dependency] private readonly AirlockSystem _airlockSystem = default!;
|
||||
[Dependency] private readonly StackSystem _stackSystem = default!;
|
||||
[Dependency] private readonly SharedAccessSystem _accessSystem = default!;
|
||||
@@ -78,7 +78,7 @@ public sealed partial class AdminVerbSystem
|
||||
: new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/bolt.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_boltsSystem.SetBoltsWithAudio(args.Target, bolts, !bolts.BoltsDown);
|
||||
_door.SetBoltsDown((args.Target, bolts), !bolts.BoltsDown);
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString(bolts.BoltsDown
|
||||
|
||||
@@ -33,9 +33,7 @@ public sealed partial class AnomalySystem
|
||||
SubscribeLocalEvent<AnomalyGeneratorComponent, MaterialAmountChangedEvent>(OnGeneratorMaterialAmountChanged);
|
||||
SubscribeLocalEvent<AnomalyGeneratorComponent, AnomalyGeneratorGenerateButtonPressedEvent>(OnGenerateButtonPressed);
|
||||
SubscribeLocalEvent<AnomalyGeneratorComponent, PowerChangedEvent>(OnGeneratorPowerChanged);
|
||||
SubscribeLocalEvent<AnomalyGeneratorComponent, EntityUnpausedEvent>(OnGeneratorUnpaused);
|
||||
SubscribeLocalEvent<GeneratingAnomalyGeneratorComponent, ComponentStartup>(OnGeneratingStartup);
|
||||
SubscribeLocalEvent<GeneratingAnomalyGeneratorComponent, EntityUnpausedEvent>(OnGeneratingUnpaused);
|
||||
}
|
||||
|
||||
private void OnGeneratorPowerChanged(EntityUid uid, AnomalyGeneratorComponent component, ref PowerChangedEvent args)
|
||||
@@ -58,11 +56,6 @@ public sealed partial class AnomalySystem
|
||||
TryGeneratorCreateAnomaly(uid, component);
|
||||
}
|
||||
|
||||
private void OnGeneratorUnpaused(EntityUid uid, AnomalyGeneratorComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.CooldownEndTime += args.PausedTime;
|
||||
}
|
||||
|
||||
public void UpdateGeneratorUi(EntityUid uid, AnomalyGeneratorComponent component)
|
||||
{
|
||||
var materialAmount = _material.GetMaterialAmount(uid, component.RequiredMaterial);
|
||||
@@ -169,11 +162,6 @@ public sealed partial class AnomalySystem
|
||||
Appearance.SetData(uid, AnomalyGeneratorVisuals.Generating, true);
|
||||
}
|
||||
|
||||
private void OnGeneratingUnpaused(EntityUid uid, GeneratingAnomalyGeneratorComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.EndTime += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnGeneratingFinished(EntityUid uid, AnomalyGeneratorComponent component)
|
||||
{
|
||||
var xform = Transform(uid);
|
||||
|
||||
@@ -22,7 +22,6 @@ public sealed partial class AnomalySystem
|
||||
SubscribeLocalEvent<AnomalyVesselComponent, InteractUsingEvent>(OnVesselInteractUsing);
|
||||
SubscribeLocalEvent<AnomalyVesselComponent, ExaminedEvent>(OnExamined);
|
||||
SubscribeLocalEvent<AnomalyVesselComponent, ResearchServerGetPointsPerSecondEvent>(OnVesselGetPointsPerSecond);
|
||||
SubscribeLocalEvent<AnomalyVesselComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<AnomalyShutdownEvent>(OnShutdown);
|
||||
SubscribeLocalEvent<AnomalyStabilityChangedEvent>(OnStabilityChanged);
|
||||
}
|
||||
@@ -92,11 +91,6 @@ public sealed partial class AnomalySystem
|
||||
args.Points += (int) (GetAnomalyPointValue(anomaly) * component.PointMultiplier);
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, AnomalyVesselComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextBeep += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnVesselAnomalyShutdown(ref AnomalyShutdownEvent args)
|
||||
{
|
||||
var query = EntityQueryEnumerator<AnomalyVesselComponent>();
|
||||
|
||||
@@ -12,13 +12,14 @@ namespace Content.Server.Anomaly.Components;
|
||||
/// This is used for a machine that is able to generate
|
||||
/// anomalies randomly on the station.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(SharedAnomalySystem))]
|
||||
[RegisterComponent, Access(typeof(SharedAnomalySystem)), AutoGenerateComponentPause]
|
||||
public sealed partial class AnomalyGeneratorComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The time at which the cooldown for generating another anomaly will be over
|
||||
/// </summary>
|
||||
[DataField("cooldownEndTime", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoPausedField]
|
||||
public TimeSpan CooldownEndTime = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Content.Server.Anomaly.Components;
|
||||
/// they generate points for the selected server based on
|
||||
/// the anomaly's stability and severity.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(SharedAnomalySystem))]
|
||||
[RegisterComponent, Access(typeof(SharedAnomalySystem)), AutoGenerateComponentPause]
|
||||
public sealed partial class AnomalyVesselComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
@@ -42,6 +42,7 @@ public sealed partial class AnomalyVesselComponent : Component
|
||||
/// When the next beep sound will play
|
||||
/// </summary>
|
||||
[DataField("nextBeep", customTypeSerializer:typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextBeep = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,13 +4,14 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.Anomaly.Components;
|
||||
|
||||
[RegisterComponent, Access(typeof(SharedAnomalySystem))]
|
||||
[RegisterComponent, Access(typeof(SharedAnomalySystem)), AutoGenerateComponentPause]
|
||||
public sealed partial class GeneratingAnomalyGeneratorComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// When the generating period will end.
|
||||
/// </summary>
|
||||
[DataField("endTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan EndTime = TimeSpan.Zero;
|
||||
|
||||
public EntityUid? AudioStream;
|
||||
|
||||
@@ -29,11 +29,9 @@ public sealed class RottingSystem : SharedRottingSystem
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PerishableComponent, MapInitEvent>(OnPerishableMapInit);
|
||||
SubscribeLocalEvent<PerishableComponent, EntityUnpausedEvent>(OnPerishableUnpaused);
|
||||
SubscribeLocalEvent<PerishableComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<PerishableComponent, ExaminedEvent>(OnPerishableExamined);
|
||||
|
||||
SubscribeLocalEvent<RottingComponent, EntityUnpausedEvent>(OnRottingUnpaused);
|
||||
SubscribeLocalEvent<RottingComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<RottingComponent, MobStateChangedEvent>(OnRottingMobStateChanged);
|
||||
SubscribeLocalEvent<RottingComponent, BeingGibbedEvent>(OnGibbed);
|
||||
@@ -47,11 +45,6 @@ public sealed class RottingSystem : SharedRottingSystem
|
||||
component.RotNextUpdate = _timing.CurTime + component.PerishUpdateRate;
|
||||
}
|
||||
|
||||
private void OnPerishableUnpaused(EntityUid uid, PerishableComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.RotNextUpdate += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnMobStateChanged(EntityUid uid, PerishableComponent component, MobStateChangedEvent args)
|
||||
{
|
||||
if (args.NewMobState != MobState.Dead && args.OldMobState != MobState.Dead)
|
||||
@@ -64,11 +57,6 @@ public sealed class RottingSystem : SharedRottingSystem
|
||||
component.RotNextUpdate = _timing.CurTime + component.PerishUpdateRate;
|
||||
}
|
||||
|
||||
private void OnRottingUnpaused(EntityUid uid, RottingComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextRotUpdate += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnShutdown(EntityUid uid, RottingComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (TryComp<PerishableComponent>(uid, out var perishable))
|
||||
|
||||
@@ -209,15 +209,19 @@ namespace Content.Server.Cargo.Systems
|
||||
_random.Shuffle(tradePads);
|
||||
|
||||
var freePads = GetFreeCargoPallets(trade, tradePads);
|
||||
|
||||
foreach (var pad in freePads)
|
||||
if (freePads.Count >= order.OrderQuantity) //check if the station has enough free pallets
|
||||
{
|
||||
var coordinates = new EntityCoordinates(trade, pad.Transform.LocalPosition);
|
||||
|
||||
if (FulfillOrder(order, coordinates, orderDatabase.PrinterOutput))
|
||||
foreach (var pad in freePads)
|
||||
{
|
||||
tradeDestination = trade;
|
||||
break;
|
||||
var coordinates = new EntityCoordinates(trade, pad.Transform.LocalPosition);
|
||||
|
||||
if (FulfillOrder(order, coordinates, orderDatabase.PrinterOutput))
|
||||
{
|
||||
tradeDestination = trade;
|
||||
order.NumDispatched++;
|
||||
if (order.OrderQuantity <= order.NumDispatched) //Spawn a crate on free pellets until the order is fulfilled.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -196,12 +196,14 @@ public sealed partial class CargoSystem
|
||||
return _pads;
|
||||
}
|
||||
|
||||
private IEnumerable<(EntityUid Entity, CargoPalletComponent Component, TransformComponent Transform)>
|
||||
private List<(EntityUid Entity, CargoPalletComponent Component, TransformComponent Transform)>
|
||||
GetFreeCargoPallets(EntityUid gridUid,
|
||||
List<(EntityUid Entity, CargoPalletComponent Component, TransformComponent Transform)> pallets)
|
||||
{
|
||||
_setEnts.Clear();
|
||||
|
||||
List<(EntityUid Entity, CargoPalletComponent Component, TransformComponent Transform)> outList = new();
|
||||
|
||||
foreach (var pallet in pallets)
|
||||
{
|
||||
var aabb = _lookup.GetAABBNoContainer(pallet.Entity, pallet.Transform.LocalPosition, pallet.Transform.LocalRotation);
|
||||
@@ -209,8 +211,10 @@ public sealed partial class CargoSystem
|
||||
if (_lookup.AnyLocalEntitiesIntersecting(gridUid, aabb, LookupFlags.Dynamic))
|
||||
continue;
|
||||
|
||||
yield return pallet;
|
||||
outList.Add(pallet);
|
||||
}
|
||||
|
||||
return outList;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Content.Server.Charges.Components;
|
||||
/// Something with limited charges that can be recharged automatically.
|
||||
/// Requires LimitedChargesComponent to function.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
[Access(typeof(ChargesSystem))]
|
||||
public sealed partial class AutoRechargeComponent : Component
|
||||
{
|
||||
@@ -21,5 +21,6 @@ public sealed partial class AutoRechargeComponent : Component
|
||||
/// The time when the next charge will be added
|
||||
/// </summary>
|
||||
[DataField("nextChargeTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextChargeTime;
|
||||
}
|
||||
|
||||
@@ -10,13 +10,6 @@ public sealed class ChargesSystem : SharedChargesSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AutoRechargeComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
@@ -32,11 +25,6 @@ public sealed class ChargesSystem : SharedChargesSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, AutoRechargeComponent comp, ref EntityUnpausedEvent args)
|
||||
{
|
||||
comp.NextChargeTime += args.PausedTime;
|
||||
}
|
||||
|
||||
protected override void OnExamine(EntityUid uid, LimitedChargesComponent comp, ExaminedEvent args)
|
||||
{
|
||||
base.OnExamine(uid, comp, args);
|
||||
|
||||
@@ -8,7 +8,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
|
||||
/// <summary>
|
||||
/// Causes an entity to automatically emote when taking damage.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(EmoteOnDamageSystem))]
|
||||
[RegisterComponent, Access(typeof(EmoteOnDamageSystem)), AutoGenerateComponentPause]
|
||||
public sealed partial class EmoteOnDamageComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
@@ -41,6 +41,7 @@ public sealed partial class EmoteOnDamageComponent : Component
|
||||
/// The simulation time of the last emote preformed due to taking damage.
|
||||
/// </summary>
|
||||
[DataField("lastEmoteTime", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoPausedField]
|
||||
public TimeSpan LastEmoteTime = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -18,15 +18,9 @@ public sealed class EmoteOnDamageSystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<EmoteOnDamageComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<EmoteOnDamageComponent, DamageChangedEvent>(OnDamage);
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, EmoteOnDamageComponent emoteOnDamage, ref EntityUnpausedEvent args)
|
||||
{
|
||||
emoteOnDamage.LastEmoteTime += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnDamage(EntityUid uid, EmoteOnDamageComponent emoteOnDamage, DamageChangedEvent args)
|
||||
{
|
||||
if (!args.DamageIncreased)
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Content.Server.Chemistry.Components;
|
||||
/// <summary>
|
||||
/// Passively decreases a solution's quantity of reagent(s).
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
[Access(typeof(SolutionPurgeSystem))]
|
||||
public sealed partial class SolutionPurgeComponent : Component
|
||||
{
|
||||
@@ -42,5 +42,6 @@ public sealed partial class SolutionPurgeComponent : Component
|
||||
/// The time when the next purge will occur.
|
||||
/// </summary>
|
||||
[DataField("nextPurgeTime", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextPurgeTime = TimeSpan.FromSeconds(0);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Content.Server.Chemistry.Components;
|
||||
/// <summary>
|
||||
/// Passively increases a solution's quantity of a reagent.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
[Access(typeof(SolutionRegenerationSystem))]
|
||||
public sealed partial class SolutionRegenerationComponent : Component
|
||||
{
|
||||
@@ -39,5 +39,6 @@ public sealed partial class SolutionRegenerationComponent : Component
|
||||
/// The time when the next regeneration will occur.
|
||||
/// </summary>
|
||||
[DataField("nextChargeTime", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextRegenTime = TimeSpan.FromSeconds(0);
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
|
||||
var inventory = GetInventory(reagentDispenser);
|
||||
|
||||
var state = new ReagentDispenserBoundUserInterfaceState(outputContainerInfo, inventory, reagentDispenser.Comp.DispenseAmount);
|
||||
var state = new ReagentDispenserBoundUserInterfaceState(outputContainerInfo, GetNetEntity(outputContainer), inventory, reagentDispenser.Comp.DispenseAmount);
|
||||
_userInterfaceSystem.TrySetUiState(reagentDispenser, ReagentDispenserUiKey.Key, state);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,13 +10,6 @@ public sealed class SolutionPurgeSystem : EntitySystem
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SolutionPurgeComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
@@ -33,9 +26,4 @@ public sealed class SolutionPurgeSystem : EntitySystem
|
||||
_solutionContainer.SplitSolutionWithout(solution.Value, purge.Quantity, purge.Preserve.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUnpaused(Entity<SolutionPurgeComponent> entity, ref EntityUnpausedEvent args)
|
||||
{
|
||||
entity.Comp.NextPurgeTime += args.PausedTime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,6 @@ public sealed class SolutionRegenerationSystem : EntitySystem
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SolutionRegenerationComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
@@ -52,9 +45,4 @@ public sealed class SolutionRegenerationSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUnpaused(Entity<SolutionRegenerationComponent> entity, ref EntityUnpausedEvent args)
|
||||
{
|
||||
entity.Comp.NextRegenTime += args.PausedTime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
using ChunkIndicesEnumerator = Robust.Shared.Map.Enumerators.ChunkIndicesEnumerator;
|
||||
|
||||
namespace Content.Shared.Chunking;
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ namespace Content.Server.Connection
|
||||
|
||||
// Corvax-Start: Allow privileged players bypass bunker
|
||||
var isPrivileged = await HavePrivilegedJoin(e.UserId);
|
||||
if (_cfg.GetCVar(CCVars.PanicBunkerEnabled) && !isPrivileged)
|
||||
if (_cfg.GetCVar(CCVars.PanicBunkerEnabled) && adminData == null && !isPrivileged)
|
||||
// Corvax-End
|
||||
{
|
||||
var showReason = _cfg.GetCVar(CCVars.PanicBunkerShowReason);
|
||||
@@ -236,13 +236,11 @@ namespace Content.Server.Connection
|
||||
// Corvax-Queue-Start: Make these conditions in one place, for checks in the connection and in the queue
|
||||
public async Task<bool> HavePrivilegedJoin(NetUserId userId)
|
||||
{
|
||||
var isAdmin = await _dbManager.GetAdminDataForAsync(userId) != null;
|
||||
var havePriorityJoin = _sponsorsMgr != null && _sponsorsMgr.HavePriorityJoin(userId); // Corvax-Sponsors
|
||||
var wasInGame = EntitySystem.TryGet<GameTicker>(out var ticker) &&
|
||||
ticker.PlayerGameStatuses.TryGetValue(userId, out var status) &&
|
||||
status == PlayerGameStatus.JoinedGame;
|
||||
return isAdmin ||
|
||||
havePriorityJoin || // Corvax-Sponsors
|
||||
return havePriorityJoin || // Corvax-Sponsors
|
||||
wasInGame;
|
||||
}
|
||||
// Corvax-Queue-End
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<LangVersion>11</LangVersion>
|
||||
<LangVersion>12</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>..\bin\Content.Server\</OutputPath>
|
||||
|
||||
@@ -9,10 +9,12 @@ using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.CrewManifest;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.CrewManifest;
|
||||
@@ -23,6 +25,7 @@ public sealed class CrewManifestSystem : EntitySystem
|
||||
[Dependency] private readonly StationRecordsSystem _recordsSystem = default!;
|
||||
[Dependency] private readonly EuiManager _euiManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Cached crew manifest entries. The alternative is to outright
|
||||
@@ -223,15 +226,26 @@ public sealed class CrewManifestSystem : EntitySystem
|
||||
|
||||
var entries = new CrewManifestEntries();
|
||||
|
||||
var entriesSort = new List<(JobPrototype? job, CrewManifestEntry entry)>();
|
||||
foreach (var recordObject in iter)
|
||||
{
|
||||
var record = recordObject.Item2;
|
||||
var entry = new CrewManifestEntry(record.Name, record.JobTitle, record.JobIcon, record.JobPrototype);
|
||||
|
||||
entries.Entries.Add(entry);
|
||||
_prototypeManager.TryIndex(record.JobPrototype, out JobPrototype? job);
|
||||
entriesSort.Add((job, entry));
|
||||
}
|
||||
|
||||
entries.Entries = entries.Entries.OrderBy(e => e.JobTitle).ThenBy(e => e.Name).ToList();
|
||||
entriesSort.Sort((a, b) =>
|
||||
{
|
||||
var cmp = JobUIComparer.Instance.Compare(a.job, b.job);
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
|
||||
return string.Compare(a.entry.Name, b.entry.Name, StringComparison.CurrentCultureIgnoreCase);
|
||||
});
|
||||
|
||||
entries.Entries = entriesSort.Select(x => x.entry).ToArray();
|
||||
_cachedEntries[station] = entries;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ using Robust.Shared.Threading;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using static Content.Shared.Decals.DecalGridComponent;
|
||||
using ChunkIndicesEnumerator = Robust.Shared.Map.Enumerators.ChunkIndicesEnumerator;
|
||||
|
||||
namespace Content.Server.Decals
|
||||
{
|
||||
|
||||
@@ -12,7 +12,6 @@ namespace Content.Server.DeviceLinking.Systems
|
||||
[UsedImplicitly]
|
||||
public sealed class DoorSignalControlSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DoorBoltSystem _bolts = default!;
|
||||
[Dependency] private readonly DoorSystem _doorSystem = default!;
|
||||
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
|
||||
|
||||
@@ -79,7 +78,7 @@ namespace Content.Server.DeviceLinking.Systems
|
||||
bolt = state == SignalState.High;
|
||||
}
|
||||
|
||||
_bolts.SetBoltsWithAudio(uid, bolts, bolt);
|
||||
_doorSystem.SetBoltsDown((uid, bolts), bolt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,6 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
|
||||
|
||||
// Shouldn't need re-anchoring.
|
||||
SubscribeLocalEvent<DisposalUnitComponent, AnchorStateChangedEvent>(OnAnchorChanged);
|
||||
SubscribeLocalEvent<DisposalUnitComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
// TODO: Predict me when hands predicted
|
||||
SubscribeLocalEvent<DisposalUnitComponent, ContainerRelayMovementEntityEvent>(OnMovement);
|
||||
SubscribeLocalEvent<DisposalUnitComponent, PowerChangedEvent>(OnPowerChange);
|
||||
@@ -103,14 +102,6 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
|
||||
GetNetEntityList(component.RecentlyEjected));
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, SharedDisposalUnitComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
if (component.NextFlush != null)
|
||||
component.NextFlush = component.NextFlush.Value + args.PausedTime;
|
||||
|
||||
component.NextPressurized += args.PausedTime;
|
||||
}
|
||||
|
||||
private void AddDisposalAltVerbs(EntityUid uid, SharedDisposalUnitComponent component, GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
using Content.Server.DeviceLinking.Events;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Wires;
|
||||
using Content.Shared.Doors;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Wires;
|
||||
using Content.Shared.Prying.Components;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Doors.Systems;
|
||||
@@ -15,8 +12,6 @@ namespace Content.Server.Doors.Systems;
|
||||
public sealed class AirlockSystem : SharedAirlockSystem
|
||||
{
|
||||
[Dependency] private readonly WiresSystem _wiresSystem = default!;
|
||||
[Dependency] private readonly PowerReceiverSystem _power = default!;
|
||||
[Dependency] private readonly DoorBoltSystem _bolts = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -26,13 +21,7 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
||||
SubscribeLocalEvent<AirlockComponent, SignalReceivedEvent>(OnSignalReceived);
|
||||
|
||||
SubscribeLocalEvent<AirlockComponent, PowerChangedEvent>(OnPowerChanged);
|
||||
SubscribeLocalEvent<AirlockComponent, DoorStateChangedEvent>(OnStateChanged);
|
||||
SubscribeLocalEvent<AirlockComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
|
||||
SubscribeLocalEvent<AirlockComponent, BeforeDoorDeniedEvent>(OnBeforeDoorDenied);
|
||||
SubscribeLocalEvent<AirlockComponent, ActivateInWorldEvent>(OnActivate, before: new[] { typeof(DoorSystem) });
|
||||
SubscribeLocalEvent<AirlockComponent, GetPryTimeModifierEvent>(OnGetPryMod);
|
||||
SubscribeLocalEvent<AirlockComponent, BeforePryEvent>(OnBeforePry);
|
||||
|
||||
}
|
||||
|
||||
private void OnAirlockInit(EntityUid uid, AirlockComponent component, ComponentInit args)
|
||||
@@ -54,6 +43,9 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
||||
|
||||
private void OnPowerChanged(EntityUid uid, AirlockComponent component, ref PowerChangedEvent args)
|
||||
{
|
||||
component.Powered = args.Powered;
|
||||
Dirty(uid, component);
|
||||
|
||||
if (TryComp<AppearanceComponent>(uid, out var appearanceComponent))
|
||||
{
|
||||
Appearance.SetData(uid, DoorVisuals.Powered, args.Powered, appearanceComponent);
|
||||
@@ -74,80 +66,6 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStateChanged(EntityUid uid, AirlockComponent component, DoorStateChangedEvent args)
|
||||
{
|
||||
// TODO move to shared? having this be server-side, but having client-side door opening/closing & prediction
|
||||
// means that sometimes the panels & bolt lights may be visible despite a door being completely open.
|
||||
|
||||
// Only show the maintenance panel if the airlock is closed
|
||||
if (TryComp<WiresPanelComponent>(uid, out var wiresPanel))
|
||||
{
|
||||
_wiresSystem.ChangePanelVisibility(uid, wiresPanel, component.OpenPanelVisible || args.State != DoorState.Open);
|
||||
}
|
||||
// If the door is closed, we should look if the bolt was locked while closing
|
||||
UpdateAutoClose(uid, component);
|
||||
|
||||
// Make sure the airlock auto closes again next time it is opened
|
||||
if (args.State == DoorState.Closed)
|
||||
component.AutoClose = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the auto close timer.
|
||||
/// </summary>
|
||||
public void UpdateAutoClose(EntityUid uid, AirlockComponent? airlock = null, DoorComponent? door = null)
|
||||
{
|
||||
if (!Resolve(uid, ref airlock, ref door))
|
||||
return;
|
||||
|
||||
if (door.State != DoorState.Open)
|
||||
return;
|
||||
|
||||
if (!airlock.AutoClose)
|
||||
return;
|
||||
|
||||
if (!CanChangeState(uid, airlock))
|
||||
return;
|
||||
|
||||
var autoev = new BeforeDoorAutoCloseEvent();
|
||||
RaiseLocalEvent(uid, autoev, false);
|
||||
if (autoev.Cancelled)
|
||||
return;
|
||||
|
||||
DoorSystem.SetNextStateChange(uid, airlock.AutoCloseDelay * airlock.AutoCloseDelayModifier);
|
||||
}
|
||||
|
||||
private void OnBeforeDoorOpened(EntityUid uid, AirlockComponent component, BeforeDoorOpenedEvent args)
|
||||
{
|
||||
if (!CanChangeState(uid, component))
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
protected override void OnBeforeDoorClosed(EntityUid uid, AirlockComponent component, BeforeDoorClosedEvent args)
|
||||
{
|
||||
base.OnBeforeDoorClosed(uid, component, args);
|
||||
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
// only block based on bolts / power status when initially closing the door, not when its already
|
||||
// mid-transition. Particularly relevant for when the door was pried-closed with a crowbar, which bypasses
|
||||
// the initial power-check.
|
||||
|
||||
if (TryComp(uid, out DoorComponent? door)
|
||||
&& !door.Partial
|
||||
&& !CanChangeState(uid, component))
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBeforeDoorDenied(EntityUid uid, AirlockComponent component, BeforeDoorDeniedEvent args)
|
||||
{
|
||||
if (!CanChangeState(uid, component))
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnActivate(EntityUid uid, AirlockComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (TryComp<WiresPanelComponent>(uid, out var panel) &&
|
||||
@@ -169,32 +87,4 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
||||
component.AutoClose = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGetPryMod(EntityUid uid, AirlockComponent component, ref GetPryTimeModifierEvent args)
|
||||
{
|
||||
if (_power.IsPowered(uid))
|
||||
args.PryTimeModifier *= component.PoweredPryModifier;
|
||||
|
||||
if (_bolts.IsBolted(uid))
|
||||
args.PryTimeModifier *= component.BoltedPryModifier;
|
||||
}
|
||||
|
||||
private void OnBeforePry(EntityUid uid, AirlockComponent component, ref BeforePryEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (!this.IsPowered(uid, EntityManager) || args.PryPowered)
|
||||
return;
|
||||
|
||||
args.Message = "airlock-component-cannot-pry-is-powered-message";
|
||||
|
||||
args.Cancelled = true;
|
||||
|
||||
}
|
||||
|
||||
public bool CanChangeState(EntityUid uid, AirlockComponent component)
|
||||
{
|
||||
return this.IsPowered(uid, EntityManager) && !_bolts.IsBolted(uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Shared.Doors;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors.Systems;
|
||||
|
||||
namespace Content.Server.Doors.Systems;
|
||||
|
||||
public sealed class DoorBoltSystem : SharedDoorBoltSystem
|
||||
{
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DoorBoltComponent, PowerChangedEvent>(OnPowerChanged);
|
||||
SubscribeLocalEvent<DoorBoltComponent, DoorStateChangedEvent>(OnStateChanged);
|
||||
}
|
||||
|
||||
private void OnPowerChanged(EntityUid uid, DoorBoltComponent component, ref PowerChangedEvent args)
|
||||
{
|
||||
if (args.Powered)
|
||||
{
|
||||
if (component.BoltWireCut)
|
||||
SetBoltsWithAudio(uid, component, true);
|
||||
}
|
||||
|
||||
UpdateBoltLightStatus(uid, component);
|
||||
}
|
||||
|
||||
public void UpdateBoltLightStatus(EntityUid uid, DoorBoltComponent component)
|
||||
{
|
||||
if (!TryComp<AppearanceComponent>(uid, out var appearance))
|
||||
return;
|
||||
|
||||
Appearance.SetData(uid, DoorVisuals.BoltLights, GetBoltLightsVisible(uid, component), appearance);
|
||||
}
|
||||
|
||||
public bool GetBoltLightsVisible(EntityUid uid, DoorBoltComponent component)
|
||||
{
|
||||
return component.BoltLightsEnabled &&
|
||||
component.BoltsDown &&
|
||||
this.IsPowered(uid, EntityManager);
|
||||
}
|
||||
|
||||
public void SetBoltLightsEnabled(EntityUid uid, DoorBoltComponent component, bool value)
|
||||
{
|
||||
if (component.BoltLightsEnabled == value)
|
||||
return;
|
||||
|
||||
component.BoltLightsEnabled = value;
|
||||
UpdateBoltLightStatus(uid, component);
|
||||
}
|
||||
|
||||
public void SetBoltsDown(EntityUid uid, DoorBoltComponent component, bool value)
|
||||
{
|
||||
if (component.BoltsDown == value)
|
||||
return;
|
||||
|
||||
component.BoltsDown = value;
|
||||
UpdateBoltLightStatus(uid, component);
|
||||
}
|
||||
|
||||
private void OnStateChanged(EntityUid uid, DoorBoltComponent component, DoorStateChangedEvent args)
|
||||
{
|
||||
// If the door is closed, we should look if the bolt was locked while closing
|
||||
UpdateBoltLightStatus(uid, component);
|
||||
}
|
||||
|
||||
public void SetBoltsWithAudio(EntityUid uid, DoorBoltComponent component, bool newBolts)
|
||||
{
|
||||
if (newBolts == component.BoltsDown)
|
||||
return;
|
||||
|
||||
component.BoltsDown = newBolts;
|
||||
Audio.PlayPvs(newBolts ? component.BoltDownSound : component.BoltUpSound, uid);
|
||||
UpdateBoltLightStatus(uid, component);
|
||||
}
|
||||
|
||||
public bool IsBolted(EntityUid uid, DoorBoltComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return component.BoltsDown;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +1,22 @@
|
||||
using Content.Server.Access;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Shared.Database;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Content.Shared.Emag.Systems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Prying.Components;
|
||||
using Content.Shared.Prying.Systems;
|
||||
using Content.Shared.Tools.Systems;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Events;
|
||||
|
||||
namespace Content.Server.Doors.Systems;
|
||||
|
||||
public sealed class DoorSystem : SharedDoorSystem
|
||||
{
|
||||
[Dependency] private readonly IAdminLogManager _adminLog = default!;
|
||||
[Dependency] private readonly DoorBoltSystem _bolts = default!;
|
||||
[Dependency] private readonly AirtightSystem _airtightSystem = default!;
|
||||
[Dependency] private readonly PryingSystem _pryingSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DoorComponent, WeldableAttemptEvent>(OnWeldAttempt);
|
||||
SubscribeLocalEvent<DoorComponent, WeldableChangedEvent>(OnWeldChanged);
|
||||
SubscribeLocalEvent<DoorComponent, GotEmaggedEvent>(OnEmagged);
|
||||
SubscribeLocalEvent<DoorComponent, PriedEvent>(OnAfterPry);
|
||||
}
|
||||
|
||||
protected override void OnActivate(EntityUid uid, DoorComponent door, ActivateInWorldEvent args)
|
||||
{
|
||||
// TODO once access permissions are shared, move this back to shared.
|
||||
if (args.Handled || !door.ClickOpen)
|
||||
return;
|
||||
|
||||
if (!TryToggleDoor(uid, door, args.User))
|
||||
_pryingSystem.TryPry(uid, args.User, out _);
|
||||
|
||||
args.Handled = true;
|
||||
SubscribeLocalEvent<DoorBoltComponent, PowerChangedEvent>(OnBoltPowerChanged);
|
||||
}
|
||||
|
||||
protected override void SetCollidable(
|
||||
@@ -65,144 +38,16 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
base.SetCollidable(uid, collidable, door, physics, occluder);
|
||||
}
|
||||
|
||||
// TODO AUDIO PREDICT Figure out a better way to handle sound and prediction. For now, this works well enough?
|
||||
//
|
||||
// Currently a client will predict when a door is going to close automatically. So any client in PVS range can just
|
||||
// play their audio locally. Playing it server-side causes an odd delay, while in shared it causes double-audio.
|
||||
//
|
||||
// But if we just do that, then if a door is closed prematurely as the result of an interaction (i.e., using "E" on
|
||||
// an open door), then the audio would only be played for the client performing the interaction.
|
||||
//
|
||||
// So we do this:
|
||||
// - Play audio client-side IF the closing is being predicted (auto-close or predicted interaction)
|
||||
// - Server assumes automated closing is predicted by clients and does not play audio unless otherwise specified.
|
||||
// - Major exception is player interactions, which other players cannot predict
|
||||
// - In that case, send audio to all players, except possibly the interacting player if it was a predicted
|
||||
// interaction.
|
||||
|
||||
/// <summary>
|
||||
/// Selectively send sound to clients, taking care to not send the double-audio.
|
||||
/// </summary>
|
||||
/// <param name="uid">The audio source</param>
|
||||
/// <param name="soundSpecifier">The sound</param>
|
||||
/// <param name="audioParams">The audio parameters.</param>
|
||||
/// <param name="predictingPlayer">The user (if any) that instigated an interaction</param>
|
||||
/// <param name="predicted">Whether this interaction would have been predicted. If the predicting player is null,
|
||||
/// this assumes it would have been predicted by all players in PVS range.</param>
|
||||
protected override void PlaySound(EntityUid uid, SoundSpecifier soundSpecifier, AudioParams audioParams, EntityUid? predictingPlayer, bool predicted)
|
||||
private void OnBoltPowerChanged(Entity<DoorBoltComponent> ent, ref PowerChangedEvent args)
|
||||
{
|
||||
// If this sound would have been predicted by all clients, do not play any audio.
|
||||
if (predicted && predictingPlayer == null)
|
||||
return;
|
||||
|
||||
if (predicted)
|
||||
Audio.PlayPredicted(soundSpecifier, uid, predictingPlayer, audioParams);
|
||||
else
|
||||
Audio.PlayPvs(soundSpecifier, uid, audioParams);
|
||||
}
|
||||
|
||||
#region DoAfters
|
||||
private void OnWeldAttempt(EntityUid uid, DoorComponent component, WeldableAttemptEvent args)
|
||||
{
|
||||
if (component.CurrentlyCrushing.Count > 0)
|
||||
if (args.Powered)
|
||||
{
|
||||
args.Cancel();
|
||||
return;
|
||||
if (ent.Comp.BoltWireCut)
|
||||
SetBoltsDown(ent, true);
|
||||
}
|
||||
if (component.State != DoorState.Closed && component.State != DoorState.Welded)
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWeldChanged(EntityUid uid, DoorComponent component, ref WeldableChangedEvent args)
|
||||
{
|
||||
if (component.State == DoorState.Closed)
|
||||
SetState(uid, DoorState.Welded, component);
|
||||
else if (component.State == DoorState.Welded)
|
||||
SetState(uid, DoorState.Closed, component);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Open a door if a player or door-bumper (PDA, ID-card) collide with the door. Sadly, bullets no longer
|
||||
/// generate "access denied" sounds as you fire at a door.
|
||||
/// </summary>
|
||||
protected override void HandleCollide(EntityUid uid, DoorComponent door, ref StartCollideEvent args)
|
||||
{
|
||||
// TODO ACCESS READER move access reader to shared and predict door opening/closing
|
||||
// Then this can be moved to the shared system without mispredicting.
|
||||
if (!door.BumpOpen)
|
||||
return;
|
||||
|
||||
if (door.State is not (DoorState.Closed or DoorState.Denying))
|
||||
return;
|
||||
|
||||
var otherUid = args.OtherEntity;
|
||||
|
||||
if (Tags.HasTag(otherUid, "DoorBumpOpener"))
|
||||
TryOpen(uid, door, otherUid, quiet: door.State == DoorState.Denying);
|
||||
}
|
||||
private void OnEmagged(EntityUid uid, DoorComponent door, ref GotEmaggedEvent args)
|
||||
{
|
||||
if (TryComp<AirlockComponent>(uid, out var airlockComponent))
|
||||
{
|
||||
if (_bolts.IsBolted(uid) || !this.IsPowered(uid, EntityManager))
|
||||
return;
|
||||
|
||||
if (door.State == DoorState.Closed)
|
||||
{
|
||||
SetState(uid, DoorState.Emagging, door);
|
||||
PlaySound(uid, door.SparkSound, AudioParams.Default.WithVolume(8), args.UserUid, false);
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void StartOpening(EntityUid uid, DoorComponent? door = null, EntityUid? user = null, bool predicted = false)
|
||||
{
|
||||
if (!Resolve(uid, ref door))
|
||||
return;
|
||||
|
||||
var lastState = door.State;
|
||||
|
||||
SetState(uid, DoorState.Opening, door);
|
||||
|
||||
if (door.OpenSound != null)
|
||||
PlaySound(uid, door.OpenSound, AudioParams.Default.WithVolume(-5), user, predicted);
|
||||
|
||||
if (lastState == DoorState.Emagging && TryComp<DoorBoltComponent>(uid, out var doorBoltComponent))
|
||||
_bolts.SetBoltsWithAudio(uid, doorBoltComponent, !doorBoltComponent.BoltsDown);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open or close a door after it has been successfuly pried.
|
||||
/// </summary>
|
||||
private void OnAfterPry(EntityUid uid, DoorComponent door, ref PriedEvent args)
|
||||
{
|
||||
if (door.State == DoorState.Closed)
|
||||
{
|
||||
_adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User)} pried {ToPrettyString(uid)} open");
|
||||
StartOpening(uid, door, args.User);
|
||||
}
|
||||
else if (door.State == DoorState.Open)
|
||||
{
|
||||
_adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User)} pried {ToPrettyString(uid)} closed");
|
||||
StartClosing(uid, door, args.User);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CheckDoorBump(Entity<DoorComponent, PhysicsComponent> ent)
|
||||
{
|
||||
var (uid, door, physics) = ent;
|
||||
if (door.BumpOpen)
|
||||
{
|
||||
foreach (var other in PhysicsSystem.GetContactingEntities(uid, physics, approximate: true))
|
||||
{
|
||||
if (Tags.HasTag(other, "DoorBumpOpener") && TryOpen(uid, door, other, quiet: true))
|
||||
break;
|
||||
}
|
||||
}
|
||||
UpdateBoltLightStatus(ent);
|
||||
ent.Comp.Powered = args.Powered;
|
||||
Dirty(ent, ent.Comp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,19 +18,18 @@ public sealed partial class DoorBoltLightWireAction : ComponentWireAction<DoorBo
|
||||
|
||||
public override bool Cut(EntityUid user, Wire wire, DoorBoltComponent door)
|
||||
{
|
||||
EntityManager.System<DoorBoltSystem>().SetBoltLightsEnabled(wire.Owner, door, false);
|
||||
EntityManager.System<DoorSystem>().SetBoltLightsEnabled((wire.Owner, door), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Mend(EntityUid user, Wire wire, DoorBoltComponent door)
|
||||
{
|
||||
|
||||
EntityManager.System<DoorBoltSystem>().SetBoltLightsEnabled(wire.Owner, door, true);
|
||||
EntityManager.System<DoorSystem>().SetBoltLightsEnabled((wire.Owner, door), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Pulse(EntityUid user, Wire wire, DoorBoltComponent door)
|
||||
{
|
||||
EntityManager.System<DoorBoltSystem>().SetBoltLightsEnabled(wire.Owner, door, !door.BoltLightsEnabled);
|
||||
EntityManager.System<DoorSystem>().SetBoltLightsEnabled((wire.Owner, door), !door.BoltLightsEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,24 +19,24 @@ public sealed partial class DoorBoltWireAction : ComponentWireAction<DoorBoltCom
|
||||
|
||||
public override bool Cut(EntityUid user, Wire wire, DoorBoltComponent airlock)
|
||||
{
|
||||
EntityManager.System<DoorBoltSystem>().SetBoltWireCut(airlock, true);
|
||||
EntityManager.System<DoorSystem>().SetBoltWireCut((wire.Owner, airlock), true);
|
||||
if (!airlock.BoltsDown && IsPowered(wire.Owner))
|
||||
EntityManager.System<DoorBoltSystem>().SetBoltsWithAudio(wire.Owner, airlock, true);
|
||||
EntityManager.System<DoorSystem>().SetBoltsDown((wire.Owner, airlock), true, user);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Mend(EntityUid user, Wire wire, DoorBoltComponent door)
|
||||
{
|
||||
EntityManager.System<DoorBoltSystem>().SetBoltWireCut(door, false);
|
||||
EntityManager.System<DoorSystem>().SetBoltWireCut((wire.Owner, door), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Pulse(EntityUid user, Wire wire, DoorBoltComponent door)
|
||||
{
|
||||
if (IsPowered(wire.Owner))
|
||||
EntityManager.System<DoorBoltSystem>().SetBoltsWithAudio(wire.Owner, door, !door.BoltsDown);
|
||||
EntityManager.System<DoorSystem>().SetBoltsDown((wire.Owner, door), !door.BoltsDown);
|
||||
else if (!door.BoltsDown)
|
||||
EntityManager.System<DoorBoltSystem>().SetBoltsWithAudio(wire.Owner, door, true);
|
||||
EntityManager.System<DoorSystem>().SetBoltsDown((wire.Owner, door), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ public sealed class EmpSystem : SharedEmpSystem
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<EmpDisabledComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<EmpDisabledComponent, ExaminedEvent>(OnExamine);
|
||||
SubscribeLocalEvent<EmpOnTriggerComponent, TriggerEvent>(HandleEmpTrigger);
|
||||
|
||||
@@ -96,12 +95,6 @@ public sealed class EmpSystem : SharedEmpSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, EmpDisabledComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.DisabledUntil += args.PausedTime;
|
||||
component.TargetTime += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnExamine(EntityUid uid, EmpDisabledComponent component, ExaminedEvent args)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("emp-disabled-comp-on-examine"));
|
||||
|
||||
@@ -1,397 +0,0 @@
|
||||
using Content.Server.Interaction;
|
||||
using Content.Server.Kitchen.Components;
|
||||
using Content.Server.Weapons.Ranged.Systems;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Execution;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Projectiles;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using Content.Shared.Weapons.Ranged;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Content.Shared.Weapons.Ranged.Events;
|
||||
using Content.Shared.Weapons.Ranged.Systems;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Execution;
|
||||
|
||||
/// <summary>
|
||||
/// Verb for violently murdering cuffed creatures.
|
||||
/// </summary>
|
||||
public sealed class ExecutionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||
[Dependency] private readonly InteractionSystem _interactionSystem = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
[Dependency] private readonly GunSystem _gunSystem = default!;
|
||||
|
||||
private const float MeleeExecutionTimeModifier = 5.0f;
|
||||
private const float GunExecutionTime = 6.0f;
|
||||
private const float DamageModifier = 9.0f;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SharpComponent, GetVerbsEvent<UtilityVerb>>(OnGetInteractionVerbsMelee);
|
||||
SubscribeLocalEvent<GunComponent, GetVerbsEvent<UtilityVerb>>(OnGetInteractionVerbsGun);
|
||||
|
||||
SubscribeLocalEvent<SharpComponent, ExecutionDoAfterEvent>(OnDoafterMelee);
|
||||
SubscribeLocalEvent<GunComponent, ExecutionDoAfterEvent>(OnDoafterGun);
|
||||
}
|
||||
|
||||
private void OnGetInteractionVerbsMelee(
|
||||
EntityUid uid,
|
||||
SharpComponent component,
|
||||
GetVerbsEvent<UtilityVerb> args)
|
||||
{
|
||||
if (args.Hands == null || args.Using == null || !args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
var attacker = args.User;
|
||||
var weapon = args.Using!.Value;
|
||||
var victim = args.Target;
|
||||
|
||||
if (!CanExecuteWithMelee(weapon, victim, attacker))
|
||||
return;
|
||||
|
||||
UtilityVerb verb = new()
|
||||
{
|
||||
Act = () =>
|
||||
{
|
||||
TryStartMeleeExecutionDoafter(weapon, victim, attacker);
|
||||
},
|
||||
Impact = LogImpact.High,
|
||||
Text = Loc.GetString("execution-verb-name"),
|
||||
Message = Loc.GetString("execution-verb-message"),
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private void OnGetInteractionVerbsGun(
|
||||
EntityUid uid,
|
||||
GunComponent component,
|
||||
GetVerbsEvent<UtilityVerb> args)
|
||||
{
|
||||
if (args.Hands == null || args.Using == null || !args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
var attacker = args.User;
|
||||
var weapon = args.Using!.Value;
|
||||
var victim = args.Target;
|
||||
|
||||
if (!CanExecuteWithGun(weapon, victim, attacker))
|
||||
return;
|
||||
|
||||
UtilityVerb verb = new()
|
||||
{
|
||||
Act = () =>
|
||||
{
|
||||
TryStartGunExecutionDoafter(weapon, victim, attacker);
|
||||
},
|
||||
Impact = LogImpact.High,
|
||||
Text = Loc.GetString("execution-verb-name"),
|
||||
Message = Loc.GetString("execution-verb-message"),
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private bool CanExecuteWithAny(EntityUid weapon, EntityUid victim, EntityUid attacker)
|
||||
{
|
||||
// No point executing someone if they can't take damage
|
||||
if (!TryComp<DamageableComponent>(victim, out var damage))
|
||||
return false;
|
||||
|
||||
// You can't execute something that cannot die
|
||||
if (!TryComp<MobStateComponent>(victim, out var mobState))
|
||||
return false;
|
||||
|
||||
// You're not allowed to execute dead people (no fun allowed)
|
||||
if (_mobStateSystem.IsDead(victim, mobState))
|
||||
return false;
|
||||
|
||||
// You must be able to attack people to execute
|
||||
if (!_actionBlockerSystem.CanAttack(attacker, victim))
|
||||
return false;
|
||||
|
||||
// The victim must be incapacitated to be executed
|
||||
if (victim != attacker && _actionBlockerSystem.CanInteract(victim, null))
|
||||
return false;
|
||||
|
||||
// All checks passed
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool CanExecuteWithMelee(EntityUid weapon, EntityUid victim, EntityUid user)
|
||||
{
|
||||
if (!CanExecuteWithAny(weapon, victim, user)) return false;
|
||||
|
||||
// We must be able to actually hurt people with the weapon
|
||||
if (!TryComp<MeleeWeaponComponent>(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool CanExecuteWithGun(EntityUid weapon, EntityUid victim, EntityUid user)
|
||||
{
|
||||
if (!CanExecuteWithAny(weapon, victim, user)) return false;
|
||||
|
||||
// We must be able to actually fire the gun
|
||||
if (!TryComp<GunComponent>(weapon, out var gun) && _gunSystem.CanShoot(gun!))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void TryStartMeleeExecutionDoafter(EntityUid weapon, EntityUid victim, EntityUid attacker)
|
||||
{
|
||||
if (!CanExecuteWithMelee(weapon, victim, attacker))
|
||||
return;
|
||||
|
||||
var executionTime = (1.0f / Comp<MeleeWeaponComponent>(weapon).AttackRate) * MeleeExecutionTimeModifier;
|
||||
|
||||
if (attacker == victim)
|
||||
{
|
||||
ShowExecutionPopup("suicide-popup-melee-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon);
|
||||
ShowExecutionPopup("suicide-popup-melee-initial-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowExecutionPopup("execution-popup-melee-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon);
|
||||
ShowExecutionPopup("execution-popup-melee-initial-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon);
|
||||
}
|
||||
|
||||
var doAfter =
|
||||
new DoAfterArgs(EntityManager, attacker, executionTime, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnDamage = true,
|
||||
NeedHand = true
|
||||
};
|
||||
|
||||
_doAfterSystem.TryStartDoAfter(doAfter);
|
||||
}
|
||||
|
||||
private void TryStartGunExecutionDoafter(EntityUid weapon, EntityUid victim, EntityUid attacker)
|
||||
{
|
||||
if (!CanExecuteWithGun(weapon, victim, attacker))
|
||||
return;
|
||||
|
||||
if (attacker == victim)
|
||||
{
|
||||
ShowExecutionPopup("suicide-popup-gun-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon);
|
||||
ShowExecutionPopup("suicide-popup-gun-initial-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowExecutionPopup("execution-popup-gun-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon);
|
||||
ShowExecutionPopup("execution-popup-gun-initial-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon);
|
||||
}
|
||||
|
||||
var doAfter =
|
||||
new DoAfterArgs(EntityManager, attacker, GunExecutionTime, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnDamage = true,
|
||||
NeedHand = true
|
||||
};
|
||||
|
||||
_doAfterSystem.TryStartDoAfter(doAfter);
|
||||
}
|
||||
|
||||
private bool OnDoafterChecks(EntityUid uid, DoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled || args.Used == null || args.Target == null)
|
||||
return false;
|
||||
|
||||
if (!CanExecuteWithAny(args.Used.Value, args.Target.Value, uid))
|
||||
return false;
|
||||
|
||||
// All checks passed
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnDoafterMelee(EntityUid uid, SharpComponent component, DoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled || args.Used == null || args.Target == null)
|
||||
return;
|
||||
|
||||
var attacker = args.User;
|
||||
var victim = args.Target!.Value;
|
||||
var weapon = args.Used!.Value;
|
||||
|
||||
if (!CanExecuteWithMelee(weapon, victim, attacker)) return;
|
||||
|
||||
if (!TryComp<MeleeWeaponComponent>(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f)
|
||||
return;
|
||||
|
||||
_damageableSystem.TryChangeDamage(victim, melee.Damage * DamageModifier, true);
|
||||
_audioSystem.PlayEntity(melee.HitSound, Filter.Pvs(weapon), weapon, true, AudioParams.Default);
|
||||
|
||||
if (attacker == victim)
|
||||
{
|
||||
ShowExecutionPopup("suicide-popup-melee-complete-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon);
|
||||
ShowExecutionPopup("suicide-popup-melee-complete-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowExecutionPopup("execution-popup-melee-complete-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon);
|
||||
ShowExecutionPopup("execution-popup-melee-complete-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This repeats a lot of the code of the serverside GunSystem, make it not do that
|
||||
private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled || args.Used == null || args.Target == null)
|
||||
return;
|
||||
|
||||
var attacker = args.User;
|
||||
var weapon = args.Used!.Value;
|
||||
var victim = args.Target!.Value;
|
||||
|
||||
if (!CanExecuteWithGun(weapon, victim, attacker)) return;
|
||||
|
||||
// Check if any systems want to block our shot
|
||||
var prevention = new ShotAttemptedEvent
|
||||
{
|
||||
User = attacker,
|
||||
Used = weapon
|
||||
};
|
||||
|
||||
RaiseLocalEvent(weapon, ref prevention);
|
||||
if (prevention.Cancelled)
|
||||
return;
|
||||
|
||||
RaiseLocalEvent(attacker, ref prevention);
|
||||
if (prevention.Cancelled)
|
||||
return;
|
||||
|
||||
// Not sure what this is for but gunsystem uses it so ehhh
|
||||
var attemptEv = new AttemptShootEvent(attacker, null);
|
||||
RaiseLocalEvent(weapon, ref attemptEv);
|
||||
|
||||
if (attemptEv.Cancelled)
|
||||
{
|
||||
if (attemptEv.Message != null)
|
||||
{
|
||||
_popupSystem.PopupClient(attemptEv.Message, weapon, attacker);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Take some ammunition for the shot (one bullet)
|
||||
var fromCoordinates = Transform(attacker).Coordinates;
|
||||
var ev = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoordinates, attacker);
|
||||
RaiseLocalEvent(weapon, ev);
|
||||
|
||||
// Check if there's any ammo left
|
||||
if (ev.Ammo.Count <= 0)
|
||||
{
|
||||
_audioSystem.PlayEntity(component.SoundEmpty, Filter.Pvs(weapon), weapon, true, AudioParams.Default);
|
||||
ShowExecutionPopup("execution-popup-gun-empty", Filter.Pvs(weapon), PopupType.Medium, attacker, victim, weapon);
|
||||
return;
|
||||
}
|
||||
|
||||
// Information about the ammo like damage
|
||||
DamageSpecifier damage = new DamageSpecifier();
|
||||
|
||||
// Get some information from IShootable
|
||||
var ammoUid = ev.Ammo[0].Entity;
|
||||
switch (ev.Ammo[0].Shootable)
|
||||
{
|
||||
case CartridgeAmmoComponent cartridge:
|
||||
// Get the damage value
|
||||
var prototype = _prototypeManager.Index<EntityPrototype>(cartridge.Prototype);
|
||||
prototype.TryGetComponent<ProjectileComponent>(out var projectileA, _componentFactory); // sloth forgive me
|
||||
if (projectileA != null)
|
||||
{
|
||||
damage = projectileA.Damage * cartridge.Count;
|
||||
}
|
||||
|
||||
// Expend the cartridge
|
||||
cartridge.Spent = true;
|
||||
_appearanceSystem.SetData(ammoUid!.Value, AmmoVisuals.Spent, true);
|
||||
Dirty(ammoUid.Value, cartridge);
|
||||
|
||||
break;
|
||||
|
||||
case AmmoComponent newAmmo:
|
||||
TryComp<ProjectileComponent>(ammoUid, out var projectileB);
|
||||
if (projectileB != null)
|
||||
{
|
||||
damage = projectileB.Damage;
|
||||
}
|
||||
Del(ammoUid);
|
||||
break;
|
||||
|
||||
case HitscanPrototype hitscan:
|
||||
damage = hitscan.Damage!;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
// Clumsy people have a chance to shoot themselves
|
||||
if (TryComp<ClumsyComponent>(attacker, out var clumsy) && component.ClumsyProof == false)
|
||||
{
|
||||
if (_interactionSystem.TryRollClumsy(attacker, 0.33333333f, clumsy))
|
||||
{
|
||||
ShowExecutionPopup("execution-popup-gun-clumsy-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon);
|
||||
ShowExecutionPopup("execution-popup-gun-clumsy-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon);
|
||||
|
||||
// You shoot yourself with the gun (no damage multiplier)
|
||||
_damageableSystem.TryChangeDamage(attacker, damage, origin: attacker);
|
||||
_audioSystem.PlayEntity(component.SoundGunshot, Filter.Pvs(weapon), weapon, true, AudioParams.Default);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Gun successfully fired, deal damage
|
||||
_damageableSystem.TryChangeDamage(victim, damage * DamageModifier, true);
|
||||
_audioSystem.PlayEntity(component.SoundGunshot, Filter.Pvs(weapon), weapon, false, AudioParams.Default);
|
||||
|
||||
// Popups
|
||||
if (attacker != victim)
|
||||
{
|
||||
ShowExecutionPopup("execution-popup-gun-complete-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon);
|
||||
ShowExecutionPopup("execution-popup-gun-complete-external", Filter.PvsExcept(attacker), PopupType.LargeCaution, attacker, victim, weapon);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowExecutionPopup("suicide-popup-gun-complete-internal", Filter.Entities(attacker), PopupType.LargeCaution, attacker, victim, weapon);
|
||||
ShowExecutionPopup("suicide-popup-gun-complete-external", Filter.PvsExcept(attacker), PopupType.LargeCaution, attacker, victim, weapon);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowExecutionPopup(string locString, Filter filter, PopupType type,
|
||||
EntityUid attacker, EntityUid victim, EntityUid weapon)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString(
|
||||
locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)),
|
||||
attacker, filter, true, type);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace Content.Server.Explosion.Components.OnTrigger;
|
||||
/// <summary>
|
||||
/// After being triggered applies the specified components and runs triggers again.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
public sealed partial class TwoStageTriggerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
@@ -23,6 +23,7 @@ public sealed partial class TwoStageTriggerComponent : Component
|
||||
public ComponentRegistry SecondStageComponents = new();
|
||||
|
||||
[DataField("nextTriggerTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan? NextTriggerTime;
|
||||
|
||||
[DataField("triggered")]
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Content.Server.Explosion.Components
|
||||
/// <summary>
|
||||
/// Raises a <see cref="TriggerEvent"/> whenever an entity collides with a fixture attached to the owner of this component.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
public sealed partial class TriggerOnProximityComponent : SharedTriggerOnProximityComponent
|
||||
{
|
||||
public const string FixtureID = "trigger-on-proximity-fixture";
|
||||
@@ -58,6 +58,7 @@ namespace Content.Server.Explosion.Components
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("nextTrigger", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextTrigger = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
@@ -65,6 +66,7 @@ namespace Content.Server.Explosion.Components
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("nextVisualUpdate", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextVisualUpdate = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -17,7 +17,6 @@ public sealed partial class TriggerSystem
|
||||
SubscribeLocalEvent<TriggerOnProximityComponent, StartCollideEvent>(OnProximityStartCollide);
|
||||
SubscribeLocalEvent<TriggerOnProximityComponent, EndCollideEvent>(OnProximityEndCollide);
|
||||
SubscribeLocalEvent<TriggerOnProximityComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<TriggerOnProximityComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<TriggerOnProximityComponent, ComponentShutdown>(OnProximityShutdown);
|
||||
// Shouldn't need re-anchoring.
|
||||
SubscribeLocalEvent<TriggerOnProximityComponent, AnchorStateChangedEvent>(OnProximityAnchor);
|
||||
@@ -65,12 +64,6 @@ public sealed partial class TriggerSystem
|
||||
collisionLayer: component.Layer);
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, TriggerOnProximityComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextTrigger += args.PausedTime;
|
||||
component.NextVisualUpdate += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnProximityStartCollide(EntityUid uid, TriggerOnProximityComponent component, ref StartCollideEvent args)
|
||||
{
|
||||
if (args.OurFixtureId != TriggerOnProximityComponent.FixtureID)
|
||||
|
||||
@@ -14,16 +14,9 @@ public sealed class TwoStageTriggerSystem : EntitySystem
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<TwoStageTriggerComponent, EntityUnpausedEvent>(OnTriggerUnpaused);
|
||||
SubscribeLocalEvent<TwoStageTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
private void OnTriggerUnpaused(EntityUid uid, TwoStageTriggerComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
if (component.NextTriggerTime != null)
|
||||
component.NextTriggerTime = component.NextTriggerTime.Value + args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnTrigger(EntityUid uid, TwoStageTriggerComponent component, TriggerEvent args)
|
||||
{
|
||||
if (component.Triggered)
|
||||
|
||||
@@ -93,7 +93,7 @@ public sealed class FireExtinguisherSystem : EntitySystem
|
||||
|
||||
private void OnGetInteractionVerbs(Entity<FireExtinguisherComponent> entity, ref GetVerbsEvent<InteractionVerb> args)
|
||||
{
|
||||
if (!args.CanInteract)
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
var user = args.User;
|
||||
|
||||
@@ -63,7 +63,6 @@ public sealed class SmokeSystem : EntitySystem
|
||||
SubscribeLocalEvent<SmokeComponent, ReactionAttemptEvent>(OnReactionAttempt);
|
||||
SubscribeLocalEvent<SmokeComponent, SolutionRelayEvent<ReactionAttemptEvent>>(OnReactionAttempt);
|
||||
SubscribeLocalEvent<SmokeComponent, SpreadNeighborsEvent>(OnSmokeSpread);
|
||||
SubscribeLocalEvent<SmokeAffectedComponent, EntityUnpausedEvent>(OnAffectedUnpaused);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -124,11 +123,6 @@ public sealed class SmokeSystem : EntitySystem
|
||||
RemComp(args.OtherEntity, smokeAffectedComponent);
|
||||
}
|
||||
|
||||
private void OnAffectedUnpaused(Entity<SmokeAffectedComponent> entity, ref EntityUnpausedEvent args)
|
||||
{
|
||||
entity.Comp.NextSecond += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnSmokeSpread(Entity<SmokeComponent> entity, ref SpreadNeighborsEvent args)
|
||||
{
|
||||
if (entity.Comp.SpreadAmount == 0 || !_solutionContainerSystem.ResolveSolution(entity.Owner, SmokeComponent.SolutionName, ref entity.Comp.Solution, out var solution))
|
||||
|
||||
@@ -128,6 +128,7 @@ public sealed partial class GameTicker
|
||||
metadata["roundEndPlayers"] = _serialman.WriteValue(_replayRoundPlayerInfo);
|
||||
metadata["roundEndText"] = new ValueDataNode(_replayRoundText);
|
||||
metadata["server_id"] = new ValueDataNode(_configurationManager.GetCVar(CCVars.ServerId));
|
||||
metadata["server_name"] = new ValueDataNode(_configurationManager.GetCVar(CCVars.AdminLogsServerName));
|
||||
metadata["roundId"] = new ValueDataNode(RoundId.ToString());
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Preferences.Managers;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.Roles.Jobs;
|
||||
using Content.Server.RoundEnd;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Server.Station.Systems;
|
||||
@@ -49,6 +50,7 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
|
||||
[Dependency] private readonly SharedRoleSystem _roles = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly JobSystem _jobs = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -271,7 +273,8 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
|
||||
var prefList = new List<ICommonSession>();
|
||||
foreach (var player in allPlayers)
|
||||
{
|
||||
if (player.AttachedEntity == null || !HasComp<HumanoidAppearanceComponent>(player.AttachedEntity) || HasComp<ZombieImmuneComponent>(player.AttachedEntity))
|
||||
if (player.AttachedEntity == null || !HasComp<HumanoidAppearanceComponent>(player.AttachedEntity) ||
|
||||
HasComp<ZombieImmuneComponent>(player.AttachedEntity) || !_jobs.CanBeAntag(player))
|
||||
continue;
|
||||
|
||||
if (HasComp<InitialInfectedExemptComponent>(player.AttachedEntity))
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Content.Server.Gateway.Components;
|
||||
/// <summary>
|
||||
/// Controlling gateway that links to other gateway destinations on the server.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(GatewaySystem))]
|
||||
[RegisterComponent, Access(typeof(GatewaySystem)), AutoGenerateComponentPause]
|
||||
public sealed partial class GatewayComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
@@ -61,5 +61,6 @@ public sealed partial class GatewayComponent : Component
|
||||
/// The time at which the portal can next be opened.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextReady;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Content.Server.Gateway.Components;
|
||||
/// <summary>
|
||||
/// Generates gateway destinations at a regular interval.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
public sealed partial class GatewayGeneratorComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
@@ -20,6 +20,7 @@ public sealed partial class GatewayGeneratorComponent : Component
|
||||
/// Next time another seed unlocks.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer:typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextUnlock;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -67,7 +67,6 @@ public sealed class GatewayGeneratorSystem : EntitySystem
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<GatewayGeneratorComponent, EntityUnpausedEvent>(OnGeneratorUnpaused);
|
||||
SubscribeLocalEvent<GatewayGeneratorComponent, MapInitEvent>(OnGeneratorMapInit);
|
||||
SubscribeLocalEvent<GatewayGeneratorComponent, ComponentShutdown>(OnGeneratorShutdown);
|
||||
SubscribeLocalEvent<GatewayGeneratorDestinationComponent, AttemptGatewayOpenEvent>(OnGeneratorAttemptOpen);
|
||||
@@ -85,11 +84,6 @@ public sealed class GatewayGeneratorSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGeneratorUnpaused(Entity<GatewayGeneratorComponent> ent, ref EntityUnpausedEvent args)
|
||||
{
|
||||
ent.Comp.NextUnlock += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnGeneratorMapInit(EntityUid uid, GatewayGeneratorComponent generator, MapInitEvent args)
|
||||
{
|
||||
if (!_cfgManager.GetCVar(CCVars.GatewayGeneratorEnabled))
|
||||
|
||||
@@ -32,7 +32,6 @@ public sealed class GatewaySystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<GatewayComponent, EntityUnpausedEvent>(OnGatewayUnpaused);
|
||||
SubscribeLocalEvent<GatewayComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<GatewayComponent, ActivatableUIOpenAttemptEvent>(OnGatewayOpenAttempt);
|
||||
SubscribeLocalEvent<GatewayComponent, BoundUIOpenedEvent>(UpdateUserInterface);
|
||||
@@ -48,11 +47,6 @@ public sealed class GatewaySystem : EntitySystem
|
||||
UpdateAllGateways();
|
||||
}
|
||||
|
||||
private void OnGatewayUnpaused(EntityUid uid, GatewayComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextReady += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, GatewayComponent comp, ComponentStartup args)
|
||||
{
|
||||
// no need to update ui since its just been created, just do portal
|
||||
|
||||
@@ -52,7 +52,6 @@ namespace Content.Server.Hands.Systems
|
||||
SubscribeLocalEvent<HandsComponent, BodyPartRemovedEvent>(HandleBodyPartRemoved);
|
||||
|
||||
SubscribeLocalEvent<HandsComponent, ComponentGetState>(GetComponentState);
|
||||
SubscribeLocalEvent<HandsComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
|
||||
SubscribeLocalEvent<HandsComponent, BeforeExplodeEvent>(OnExploded);
|
||||
|
||||
@@ -73,10 +72,6 @@ namespace Content.Server.Hands.Systems
|
||||
args.State = new HandsComponentState(hands);
|
||||
}
|
||||
|
||||
private void OnUnpaused(Entity<HandsComponent> ent, ref EntityUnpausedEvent args)
|
||||
{
|
||||
ent.Comp.NextThrowTime += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnExploded(Entity<HandsComponent> ent, ref BeforeExplodeEvent args)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Content.Server.Kitchen.Components;
|
||||
/// <summary>
|
||||
/// Attached to a microwave that is currently in the process of cooking
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
public sealed partial class ActiveMicrowaveComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
@@ -17,6 +17,7 @@ public sealed partial class ActiveMicrowaveComponent : Component
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan MalfunctionTime = TimeSpan.Zero;
|
||||
|
||||
[ViewVariables]
|
||||
|
||||
@@ -86,7 +86,6 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
|
||||
SubscribeLocalEvent<ActiveMicrowaveComponent, ComponentStartup>(OnCookStart);
|
||||
SubscribeLocalEvent<ActiveMicrowaveComponent, ComponentShutdown>(OnCookStop);
|
||||
SubscribeLocalEvent<ActiveMicrowaveComponent, EntityUnpausedEvent>(OnEntityUnpaused);
|
||||
SubscribeLocalEvent<ActiveMicrowaveComponent, EntInsertedIntoContainerMessage>(OnActiveMicrowaveInsert);
|
||||
SubscribeLocalEvent<ActiveMicrowaveComponent, EntRemovedFromContainerMessage>(OnActiveMicrowaveRemove);
|
||||
|
||||
@@ -112,11 +111,6 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
microwaveComponent.PlayingStream = _audio.Stop(microwaveComponent.PlayingStream);
|
||||
}
|
||||
|
||||
private void OnEntityUnpaused(Entity<ActiveMicrowaveComponent> ent, ref EntityUnpausedEvent args)
|
||||
{
|
||||
ent.Comp.MalfunctionTime += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnActiveMicrowaveInsert(Entity<ActiveMicrowaveComponent> ent, ref EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
AddComp<ActivelyMicrowavedComponent>(args.Entity);
|
||||
|
||||
@@ -36,7 +36,6 @@ public sealed class MagicSystem : EntitySystem
|
||||
[Dependency] private readonly IComponentFactory _compFact = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly DoorBoltSystem _boltsSystem = default!;
|
||||
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
|
||||
@@ -307,7 +306,7 @@ public sealed class MagicSystem : EntitySystem
|
||||
foreach (var entity in _lookup.GetEntitiesInRange(coords, args.Range))
|
||||
{
|
||||
if (TryComp<DoorBoltComponent>(entity, out var bolts))
|
||||
_boltsSystem.SetBoltsDown(entity, bolts, false);
|
||||
_doorSystem.SetBoltsDown((entity, bolts), false);
|
||||
|
||||
if (TryComp<DoorComponent>(entity, out var doorComp) && doorComp.State is not DoorState.Open)
|
||||
_doorSystem.StartOpening(entity);
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Content.Server.Medical.Components;
|
||||
/// <summary>
|
||||
/// After scanning, retrieves the target Uid to use with its related UI.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
[Access(typeof(HealthAnalyzerSystem), typeof(CryoPodSystem))]
|
||||
public sealed partial class HealthAnalyzerComponent : Component
|
||||
{
|
||||
@@ -14,6 +14,7 @@ public sealed partial class HealthAnalyzerComponent : Component
|
||||
/// When should the next update be sent for the patient
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextUpdate = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -50,21 +50,12 @@ public sealed class DefibrillatorSystem : EntitySystem
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<DefibrillatorComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<DefibrillatorComponent, UseInHandEvent>(OnUseInHand);
|
||||
SubscribeLocalEvent<DefibrillatorComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
|
||||
SubscribeLocalEvent<DefibrillatorComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<DefibrillatorComponent, DefibrillatorZapDoAfterEvent>(OnDoAfter);
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, DefibrillatorComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
if (component.NextZapTime == null)
|
||||
return;
|
||||
|
||||
component.NextZapTime = component.NextZapTime.Value + args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnUseInHand(EntityUid uid, DefibrillatorComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled || !TryComp(uid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((uid, useDelay)))
|
||||
|
||||
@@ -30,7 +30,6 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<HealthAnalyzerComponent, EntityUnpausedEvent>(OnEntityUnpaused);
|
||||
SubscribeLocalEvent<HealthAnalyzerComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<HealthAnalyzerComponent, HealthAnalyzerDoAfterEvent>(OnDoAfter);
|
||||
SubscribeLocalEvent<HealthAnalyzerComponent, EntGotInsertedIntoContainerMessage>(OnInsertedIntoContainer);
|
||||
@@ -65,11 +64,6 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEntityUnpaused(Entity<HealthAnalyzerComponent> ent, ref EntityUnpausedEvent args)
|
||||
{
|
||||
ent.Comp.NextUpdate += args.PausedTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger the doafter for scanning
|
||||
/// </summary>
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Content.Server.Medical.SuitSensors;
|
||||
/// Tracking device, embedded in almost all uniforms and jumpsuits.
|
||||
/// If enabled, will report to crew monitoring console owners position and status.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
[Access(typeof(SuitSensorSystem))]
|
||||
public sealed partial class SuitSensorComponent : Component
|
||||
{
|
||||
@@ -57,6 +57,7 @@ public sealed partial class SuitSensorComponent : Component
|
||||
/// Next time when sensor updated owners status
|
||||
/// </summary>
|
||||
[DataField("nextUpdate", customTypeSerializer:typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextUpdate = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -40,7 +40,6 @@ public sealed class SuitSensorSystem : EntitySystem
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnPlayerSpawn);
|
||||
SubscribeLocalEvent<SuitSensorComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<SuitSensorComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<SuitSensorComponent, GotEquippedEvent>(OnEquipped);
|
||||
SubscribeLocalEvent<SuitSensorComponent, GotUnequippedEvent>(OnUnequipped);
|
||||
SubscribeLocalEvent<SuitSensorComponent, ExaminedEvent>(OnExamine);
|
||||
@@ -51,11 +50,6 @@ public sealed class SuitSensorSystem : EntitySystem
|
||||
SubscribeLocalEvent<SuitSensorComponent, EmpDisabledRemoved>(OnEmpFinished);
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, SuitSensorComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextUpdate += args.PausedTime;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
@@ -3,7 +3,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.NPC.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
public sealed partial class NPCJukeComponent : Component
|
||||
{
|
||||
[DataField("jukeType")]
|
||||
@@ -13,6 +13,7 @@ public sealed partial class NPCJukeComponent : Component
|
||||
public float JukeDuration = 0.5f;
|
||||
|
||||
[DataField("nextJuke", customTypeSerializer:typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextJuke;
|
||||
|
||||
[DataField("targetTile")]
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Content.Server.NPC.Components;
|
||||
/// <summary>
|
||||
/// Added to NPCs that are moving.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
public sealed partial class NPCSteeringComponent : Component
|
||||
{
|
||||
#region Context Steering
|
||||
@@ -49,6 +49,7 @@ public sealed partial class NPCSteeringComponent : Component
|
||||
/// Next time we can change our steering direction.
|
||||
/// </summary>
|
||||
[DataField("nextSteer", customTypeSerializer:typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextSteer = TimeSpan.Zero;
|
||||
|
||||
[DataField("lastSteerIndex")]
|
||||
@@ -66,6 +67,7 @@ public sealed partial class NPCSteeringComponent : Component
|
||||
public EntityCoordinates LastStuckCoordinates;
|
||||
|
||||
[DataField("lastStuckTime", customTypeSerializer:typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan LastStuckTime;
|
||||
|
||||
public const float StuckDistance = 1f;
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Content.Server.NPC.Pathfinding;
|
||||
/// <summary>
|
||||
/// Stores the relevant pathfinding data for grids.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(PathfindingSystem))]
|
||||
[RegisterComponent, Access(typeof(PathfindingSystem)), AutoGenerateComponentPause]
|
||||
public sealed partial class GridPathfindingComponent : Component
|
||||
{
|
||||
[ViewVariables]
|
||||
@@ -15,6 +15,7 @@ public sealed partial class GridPathfindingComponent : Component
|
||||
/// Next time the graph is allowed to update.
|
||||
/// </summary>
|
||||
/// Removing this datafield is the lazy fix HOWEVER I want to purge this anyway and do pathfinding at runtime.
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextUpdate;
|
||||
|
||||
[ViewVariables]
|
||||
|
||||
@@ -44,7 +44,6 @@ public sealed partial class PathfindingSystem
|
||||
{
|
||||
SubscribeLocalEvent<GridInitializeEvent>(OnGridInit);
|
||||
SubscribeLocalEvent<GridRemovalEvent>(OnGridRemoved);
|
||||
SubscribeLocalEvent<GridPathfindingComponent, EntityUnpausedEvent>(OnGridPathPause);
|
||||
SubscribeLocalEvent<GridPathfindingComponent, ComponentShutdown>(OnGridPathShutdown);
|
||||
SubscribeLocalEvent<CollisionChangeEvent>(OnCollisionChange);
|
||||
SubscribeLocalEvent<CollisionLayerChangeEvent>(OnCollisionLayerChange);
|
||||
@@ -61,10 +60,6 @@ public sealed partial class PathfindingSystem
|
||||
DirtyChunk(ev.Entity, Comp<MapGridComponent>(ev.Entity).GridTileToLocal(ev.NewTile.GridIndices));
|
||||
}
|
||||
|
||||
private void OnGridPathPause(EntityUid uid, GridPathfindingComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextUpdate += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnGridPathShutdown(EntityUid uid, GridPathfindingComponent component, ComponentShutdown args)
|
||||
{
|
||||
|
||||
@@ -33,15 +33,9 @@ public sealed class NPCJukeSystem : EntitySystem
|
||||
_npcRangedQuery = GetEntityQuery<NPCRangedCombatComponent>();
|
||||
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
|
||||
SubscribeLocalEvent<NPCJukeComponent, EntityUnpausedEvent>(OnJukeUnpaused);
|
||||
SubscribeLocalEvent<NPCJukeComponent, NPCSteeringEvent>(OnJukeSteering);
|
||||
}
|
||||
|
||||
private void OnJukeUnpaused(EntityUid uid, NPCJukeComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextJuke += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnJukeSteering(EntityUid uid, NPCJukeComponent component, ref NPCSteeringEvent args)
|
||||
{
|
||||
if (component.JukeType == JukeType.AdjacentTile)
|
||||
|
||||
@@ -107,7 +107,6 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
||||
Subs.CVar(_configManager, CCVars.NPCPathfinding, SetNPCPathfinding, true);
|
||||
|
||||
SubscribeLocalEvent<NPCSteeringComponent, ComponentShutdown>(OnSteeringShutdown);
|
||||
SubscribeLocalEvent<NPCSteeringComponent, EntityUnpausedEvent>(OnSteeringUnpaused);
|
||||
SubscribeNetworkEvent<RequestNPCSteeringDebugEvent>(OnDebugRequest);
|
||||
}
|
||||
|
||||
@@ -158,12 +157,6 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
||||
component.PathfindToken = null;
|
||||
}
|
||||
|
||||
private void OnSteeringUnpaused(EntityUid uid, NPCSteeringComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.LastStuckTime += args.PausedTime;
|
||||
component.NextSteer += args.PausedTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the AI to the steering system to move towards a specific target
|
||||
/// </summary>
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Content.Server.Nutrition.Components;
|
||||
/// <summary>
|
||||
/// This is used for a machine that extracts hunger from entities and creates meat. Yum!
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(FatExtractorSystem))]
|
||||
[RegisterComponent, Access(typeof(FatExtractorSystem)), AutoGenerateComponentPause]
|
||||
public sealed partial class FatExtractorComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
@@ -48,6 +48,7 @@ public sealed partial class FatExtractorComponent : Component
|
||||
/// When the next update will occur
|
||||
/// </summary>
|
||||
[DataField("nextUpdate", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextUpdate;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -40,23 +40,11 @@ public sealed class AnimalHusbandrySystem : EntitySystem
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<ReproductiveComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<ReproductiveComponent, MindAddedMessage>(OnMindAdded);
|
||||
SubscribeLocalEvent<InfantComponent, EntityUnpausedEvent>(OnInfantUnpaused);
|
||||
SubscribeLocalEvent<InfantComponent, ComponentStartup>(OnInfantStartup);
|
||||
SubscribeLocalEvent<InfantComponent, ComponentShutdown>(OnInfantShutdown);
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, ReproductiveComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextBreedAttempt += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnInfantUnpaused(EntityUid uid, InfantComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.InfantEndTime += args.PausedTime;
|
||||
}
|
||||
|
||||
// we express EZ-pass terminate the pregnancy if a player takes the role
|
||||
private void OnMindAdded(EntityUid uid, ReproductiveComponent component, MindAddedMessage args)
|
||||
{
|
||||
|
||||
@@ -130,21 +130,18 @@ public sealed class DrinkSystem : EntitySystem
|
||||
|
||||
private void OnExamined(Entity<DrinkComponent> entity, ref ExaminedEvent args)
|
||||
{
|
||||
var hasOpenable = TryComp<OpenableComponent>(entity, out var openable);
|
||||
TryComp<OpenableComponent>(entity, out var openable);
|
||||
if (_openable.IsClosed(entity.Owner, null, openable) || !args.IsInDetailsRange || !entity.Comp.Examinable)
|
||||
return;
|
||||
|
||||
// put Empty / Xu after Opened, or start a new line
|
||||
args.AddMarkup(hasOpenable ? " - " : "\n");
|
||||
|
||||
var empty = IsEmpty(entity, entity.Comp);
|
||||
if (empty)
|
||||
{
|
||||
args.AddMarkup(Loc.GetString("drink-component-on-examine-is-empty"));
|
||||
args.PushMarkup(Loc.GetString("drink-component-on-examine-is-empty"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryComp<ExaminableSolutionComponent>(entity, out var comp))
|
||||
if (HasComp<ExaminableSolutionComponent>(entity))
|
||||
{
|
||||
//provide exact measurement for beakers
|
||||
args.PushText(Loc.GetString("drink-component-on-examine-exact-volume", ("amount", DrinkVolume(entity, entity.Comp))));
|
||||
@@ -159,7 +156,7 @@ public sealed class DrinkSystem : EntitySystem
|
||||
> 33 => HalfEmptyOrHalfFull(args),
|
||||
_ => "drink-component-on-examine-is-mostly-empty",
|
||||
};
|
||||
args.AddMarkup(Loc.GetString(remainingString));
|
||||
args.PushMarkup(Loc.GetString(remainingString));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,18 +27,12 @@ public sealed class FatExtractorSystem : EntitySystem
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<FatExtractorComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<FatExtractorComponent, GotEmaggedEvent>(OnGotEmagged);
|
||||
SubscribeLocalEvent<FatExtractorComponent, StorageAfterCloseEvent>(OnClosed);
|
||||
SubscribeLocalEvent<FatExtractorComponent, StorageAfterOpenEvent>(OnOpen);
|
||||
SubscribeLocalEvent<FatExtractorComponent, PowerChangedEvent>(OnPowerChanged);
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, FatExtractorComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextUpdate += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnGotEmagged(EntityUid uid, FatExtractorComponent component, ref GotEmaggedEvent args)
|
||||
{
|
||||
args.Handled = true;
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
using Content.Server.Chemistry.EntitySystems;
|
||||
using Content.Server.Fluids.EntitySystems;
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
using Content.Server.Nutrition.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Nutrition.EntitySystems;
|
||||
|
||||
@@ -19,43 +9,11 @@ namespace Content.Server.Nutrition.EntitySystems;
|
||||
/// </summary>
|
||||
public sealed class OpenableSystem : SharedOpenableSystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<OpenableComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<OpenableComponent, UseInHandEvent>(OnUse);
|
||||
SubscribeLocalEvent<OpenableComponent, ExaminedEvent>(OnExamined, after: new[] { typeof(PuddleSystem) });
|
||||
SubscribeLocalEvent<OpenableComponent, SolutionTransferAttemptEvent>(OnTransferAttempt);
|
||||
SubscribeLocalEvent<OpenableComponent, MeleeHitEvent>(HandleIfClosed);
|
||||
SubscribeLocalEvent<OpenableComponent, AfterInteractEvent>(HandleIfClosed);
|
||||
SubscribeLocalEvent<OpenableComponent, GetVerbsEvent<Verb>>(AddOpenCloseVerbs);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, OpenableComponent comp, ComponentInit args)
|
||||
{
|
||||
UpdateAppearance(uid, comp);
|
||||
}
|
||||
|
||||
private void OnUse(EntityUid uid, OpenableComponent comp, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled || !comp.OpenableByHand)
|
||||
return;
|
||||
|
||||
args.Handled = TryOpen(uid, comp);
|
||||
}
|
||||
|
||||
private void OnExamined(EntityUid uid, OpenableComponent comp, ExaminedEvent args)
|
||||
{
|
||||
if (!comp.Opened || !args.IsInDetailsRange)
|
||||
return;
|
||||
|
||||
var text = Loc.GetString(comp.ExamineText);
|
||||
args.PushMarkup(text);
|
||||
}
|
||||
|
||||
private void OnTransferAttempt(EntityUid uid, OpenableComponent comp, SolutionTransferAttemptEvent args)
|
||||
@@ -66,135 +24,4 @@ public sealed class OpenableSystem : SharedOpenableSystem
|
||||
args.Cancel(Loc.GetString("drink-component-try-use-drink-not-open", ("owner", uid)));
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleIfClosed(EntityUid uid, OpenableComponent comp, HandledEntityEventArgs args)
|
||||
{
|
||||
// prevent spilling/pouring/whatever drinks when closed
|
||||
args.Handled = !comp.Opened;
|
||||
}
|
||||
|
||||
private void AddOpenCloseVerbs(EntityUid uid, OpenableComponent comp, GetVerbsEvent<Verb> args)
|
||||
{
|
||||
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
Verb verb;
|
||||
if (comp.Opened)
|
||||
{
|
||||
if (!comp.Closeable)
|
||||
return;
|
||||
|
||||
verb = new()
|
||||
{
|
||||
Text = Loc.GetString(comp.CloseVerbText),
|
||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/close.svg.192dpi.png")),
|
||||
Act = () => TryClose(args.Target, comp)
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
verb = new()
|
||||
{
|
||||
Text = Loc.GetString(comp.OpenVerbText),
|
||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/open.svg.192dpi.png")),
|
||||
Act = () => TryOpen(args.Target, comp)
|
||||
};
|
||||
}
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the entity either does not have OpenableComponent or it is opened.
|
||||
/// Drinks that don't have OpenableComponent are automatically open, so it returns true.
|
||||
/// </summary>
|
||||
public bool IsOpen(EntityUid uid, OpenableComponent? comp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp, false))
|
||||
return true;
|
||||
|
||||
return comp.Opened;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the entity both has OpenableComponent and is not opened.
|
||||
/// Drinks that don't have OpenableComponent are automatically open, so it returns false.
|
||||
/// If user is not null a popup will be shown to them.
|
||||
/// </summary>
|
||||
public bool IsClosed(EntityUid uid, EntityUid? user = null, OpenableComponent? comp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp, false))
|
||||
return false;
|
||||
|
||||
if (comp.Opened)
|
||||
return false;
|
||||
|
||||
if (user != null)
|
||||
_popup.PopupEntity(Loc.GetString(comp.ClosedPopup, ("owner", uid)), user.Value, user.Value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update open visuals to the current value.
|
||||
/// </summary>
|
||||
public void UpdateAppearance(EntityUid uid, OpenableComponent? comp = null, AppearanceComponent? appearance = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp))
|
||||
return;
|
||||
|
||||
_appearance.SetData(uid, OpenableVisuals.Opened, comp.Opened, appearance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the opened field and updates open visuals.
|
||||
/// </summary>
|
||||
public void SetOpen(EntityUid uid, bool opened = true, OpenableComponent? comp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp, false) || opened == comp.Opened)
|
||||
return;
|
||||
|
||||
comp.Opened = opened;
|
||||
|
||||
if (opened)
|
||||
{
|
||||
var ev = new OpenableOpenedEvent();
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ev = new OpenableClosedEvent();
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
}
|
||||
|
||||
UpdateAppearance(uid, comp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If closed, opens it and plays the sound.
|
||||
/// </summary>
|
||||
/// <returns>Whether it got opened</returns>
|
||||
public bool TryOpen(EntityUid uid, OpenableComponent? comp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp, false) || comp.Opened)
|
||||
return false;
|
||||
|
||||
SetOpen(uid, true, comp);
|
||||
_audio.PlayPvs(comp.Sound, uid);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If opened, closes it and plays the close sound, if one is defined.
|
||||
/// </summary>
|
||||
/// <returns>Whether it got closed</returns>
|
||||
public bool TryClose(EntityUid uid, OpenableComponent? comp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp, false) || !comp.Opened || !comp.Closeable)
|
||||
return false;
|
||||
|
||||
SetOpen(uid, false, comp);
|
||||
if (comp.CloseSound != null)
|
||||
_audio.PlayPvs(comp.CloseSound, uid);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Threading;
|
||||
using Robust.Shared.Utility;
|
||||
using ChunkIndicesEnumerator = Robust.Shared.Map.Enumerators.ChunkIndicesEnumerator;
|
||||
|
||||
namespace Content.Server.Parallax;
|
||||
|
||||
|
||||
@@ -8,13 +8,14 @@ namespace Content.Server.Physics.Components;
|
||||
/// <summary>
|
||||
/// A component which makes its entity chasing entity with selected component.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(ChasingWalkSystem))]
|
||||
[RegisterComponent, Access(typeof(ChasingWalkSystem)), AutoGenerateComponentPause]
|
||||
public sealed partial class ChasingWalkComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The next moment in time when the entity is pushed toward its goal
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextImpulseTime;
|
||||
|
||||
/// <summary>
|
||||
@@ -57,6 +58,7 @@ public sealed partial class ChasingWalkComponent : Component
|
||||
/// The next change of direction time.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextChangeVectorTime;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -27,7 +27,6 @@ public sealed class ChasingWalkSystem : VirtualController
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ChasingWalkComponent, MapInitEvent>(OnChasingMapInit);
|
||||
SubscribeLocalEvent<ChasingWalkComponent, EntityUnpausedEvent>(OnChasingUnpaused);
|
||||
}
|
||||
|
||||
private void OnChasingMapInit(EntityUid uid, ChasingWalkComponent component, MapInitEvent args)
|
||||
@@ -36,12 +35,6 @@ public sealed class ChasingWalkSystem : VirtualController
|
||||
component.NextChangeVectorTime = _gameTiming.CurTime;
|
||||
}
|
||||
|
||||
private void OnChasingUnpaused(EntityUid uid, ChasingWalkComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextImpulseTime += args.PausedTime;
|
||||
component.NextChangeVectorTime += args.PausedTime;
|
||||
}
|
||||
|
||||
public override void UpdateBeforeSolve(bool prediction, float frameTime)
|
||||
{
|
||||
base.UpdateBeforeSolve(prediction, frameTime);
|
||||
|
||||
@@ -39,11 +39,6 @@ public sealed partial class PowerCellSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, PowerCellDrawComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextUpdateTime += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnDrawChargeChanged(EntityUid uid, PowerCellDrawComponent component, ref ChargeChangedEvent args)
|
||||
{
|
||||
// Update the bools for client prediction.
|
||||
|
||||
@@ -35,7 +35,6 @@ public sealed partial class PowerCellSystem : SharedPowerCellSystem
|
||||
SubscribeLocalEvent<PowerCellComponent, ExaminedEvent>(OnCellExamined);
|
||||
SubscribeLocalEvent<PowerCellComponent, EmpAttemptEvent>(OnCellEmpAttempt);
|
||||
|
||||
SubscribeLocalEvent<PowerCellDrawComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<PowerCellDrawComponent, ChargeChangedEvent>(OnDrawChargeChanged);
|
||||
SubscribeLocalEvent<PowerCellDrawComponent, PowerCellChangedEvent>(OnDrawCellChanged);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Content.Server.PowerSink
|
||||
/// <summary>
|
||||
/// Absorbs power up to its capacity when anchored then explodes.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
public sealed partial class PowerSinkComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
@@ -21,6 +21,7 @@ namespace Content.Server.PowerSink
|
||||
/// If explosion has been triggered, time at which to explode.
|
||||
/// </summary>
|
||||
[DataField("explosionTime", customTypeSerializer:typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public System.TimeSpan? ExplosionTime = null;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -37,7 +37,6 @@ namespace Content.Server.PowerSink
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PowerSinkComponent, ExaminedEvent>(OnExamine);
|
||||
SubscribeLocalEvent<PowerSinkComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
}
|
||||
|
||||
private void OnExamine(EntityUid uid, PowerSinkComponent component, ExaminedEvent args)
|
||||
@@ -54,14 +53,6 @@ namespace Content.Server.PowerSink
|
||||
);
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, PowerSinkComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
if (component.ExplosionTime == null)
|
||||
return;
|
||||
|
||||
component.ExplosionTime = component.ExplosionTime + args.PausedTime;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
var toRemove = new RemQueue<(EntityUid Entity, PowerSinkComponent Sink)>();
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace Content.Server.Preferences.Managers
|
||||
var sponsorPrototypes = _sponsors != null && _sponsors.TryGetPrototypes(message.MsgChannel.UserId, out var prototypes)
|
||||
? prototypes.ToArray()
|
||||
: new string[]{};
|
||||
profile.EnsureValid(sponsorPrototypes);
|
||||
profile.EnsureValid(_cfg, _protos, sponsorPrototypes);
|
||||
// Corvax-Sponsors-End
|
||||
var profiles = new Dictionary<int, ICharacterProfile>(curPrefs.Characters)
|
||||
{
|
||||
@@ -295,34 +295,7 @@ namespace Content.Server.Preferences.Managers
|
||||
|
||||
return new PlayerPreferences(prefs.Characters.Select(p =>
|
||||
{
|
||||
ICharacterProfile newProf;
|
||||
switch (p.Value)
|
||||
{
|
||||
case HumanoidCharacterProfile hp:
|
||||
{
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
var selectedSpecies = HumanoidAppearanceSystem.DefaultSpecies;
|
||||
|
||||
if (prototypeManager.TryIndex<SpeciesPrototype>(hp.Species, out var species) && species.RoundStart)
|
||||
{
|
||||
selectedSpecies = hp.Species;
|
||||
}
|
||||
|
||||
newProf = hp
|
||||
.WithJobPriorities(
|
||||
hp.JobPriorities.Where(job =>
|
||||
_protos.HasIndex<JobPrototype>(job.Key)))
|
||||
.WithAntagPreferences(
|
||||
hp.AntagPreferences.Where(antag =>
|
||||
_protos.HasIndex<AntagPrototype>(antag)))
|
||||
.WithSpecies(selectedSpecies);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
return new KeyValuePair<int, ICharacterProfile>(p.Key, newProf);
|
||||
return new KeyValuePair<int, ICharacterProfile>(p.Key, p.Value.Validated(_cfg, _protos));
|
||||
}), prefs.SelectedCharacterIndex, prefs.AdminOOCColor);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace Content.Server.Remotes
|
||||
public sealed class DoorRemoteSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly DoorBoltSystem _bolts = default!;
|
||||
[Dependency] private readonly AirlockSystem _airlock = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly DoorSystem _doorSystem = default!;
|
||||
@@ -105,7 +104,7 @@ namespace Content.Server.Remotes
|
||||
{
|
||||
if (!boltsComp.BoltWireCut)
|
||||
{
|
||||
_bolts.SetBoltsWithAudio(args.Target.Value, boltsComp, !boltsComp.BoltsDown);
|
||||
_doorSystem.SetBoltsDown((args.Target.Value, boltsComp), !boltsComp.BoltsDown, args.User);
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} used {ToPrettyString(args.Used)} on {ToPrettyString(args.Target.Value)} to {(boltsComp.BoltsDown ? "" : "un")}bolt it");
|
||||
}
|
||||
}
|
||||
|
||||
11
Content.Server/RequiresGrid/RequiresGridComponent.cs
Normal file
11
Content.Server/RequiresGrid/RequiresGridComponent.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Content.Server.RequiresGrid;
|
||||
|
||||
/// <summary>
|
||||
/// Destroys an entity when they no longer are part of a Grid
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(RequiresGridSystem))]
|
||||
public sealed partial class RequiresGridComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
29
Content.Server/RequiresGrid/RequiresGridSystem.cs
Normal file
29
Content.Server/RequiresGrid/RequiresGridSystem.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Content.Server.Destructible;
|
||||
|
||||
namespace Content.Server.RequiresGrid;
|
||||
|
||||
public sealed class RequiresGridSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DestructibleSystem _destructible = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RequiresGridComponent, EntParentChangedMessage>(OnEntParentChanged);
|
||||
}
|
||||
|
||||
private void OnEntParentChanged(EntityUid owner, RequiresGridComponent component, EntParentChangedMessage args)
|
||||
{
|
||||
if (args.OldParent == null)
|
||||
return;
|
||||
|
||||
if (args.Transform.GridUid != null)
|
||||
return;
|
||||
|
||||
if (TerminatingOrDeleted(owner))
|
||||
return;
|
||||
|
||||
_destructible.DestroyEntity(owner);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace Content.Server.Salvage.Expeditions;
|
||||
/// <summary>
|
||||
/// Designates this entity as holding a salvage expedition.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
public sealed partial class SalvageExpeditionComponent : SharedSalvageExpeditionComponent
|
||||
{
|
||||
public SalvageMissionParams MissionParams = default!;
|
||||
@@ -26,6 +26,7 @@ public sealed partial class SalvageExpeditionComponent : SharedSalvageExpedition
|
||||
/// When the expeditions ends.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("endTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan EndTime;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -31,10 +31,7 @@ public sealed partial class SalvageSystem
|
||||
SubscribeLocalEvent<SalvageExpeditionConsoleComponent, EntParentChangedMessage>(OnSalvageConsoleParent);
|
||||
SubscribeLocalEvent<SalvageExpeditionConsoleComponent, ClaimSalvageMessage>(OnSalvageClaimMessage);
|
||||
|
||||
SubscribeLocalEvent<SalvageExpeditionDataComponent, EntityUnpausedEvent>(OnDataUnpaused);
|
||||
|
||||
SubscribeLocalEvent<SalvageExpeditionComponent, ComponentShutdown>(OnExpeditionShutdown);
|
||||
SubscribeLocalEvent<SalvageExpeditionComponent, EntityUnpausedEvent>(OnExpeditionUnpaused);
|
||||
SubscribeLocalEvent<SalvageExpeditionComponent, ComponentGetState>(OnExpeditionGetState);
|
||||
|
||||
SubscribeLocalEvent<SalvageStructureComponent, ExaminedEvent>(OnStructureExamine);
|
||||
@@ -89,16 +86,6 @@ public sealed partial class SalvageSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDataUnpaused(EntityUid uid, SalvageExpeditionDataComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextOffer += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnExpeditionUnpaused(EntityUid uid, SalvageExpeditionComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.EndTime += args.PausedTime;
|
||||
}
|
||||
|
||||
private void UpdateExpeditions()
|
||||
{
|
||||
var currentTime = _timing.CurTime;
|
||||
|
||||
@@ -3,13 +3,14 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.Shuttles.Components;
|
||||
|
||||
[RegisterComponent, Access(typeof(ArrivalsSystem))]
|
||||
[RegisterComponent, Access(typeof(ArrivalsSystem)), AutoGenerateComponentPause]
|
||||
public sealed partial class ArrivalsShuttleComponent : Component
|
||||
{
|
||||
[DataField("station")]
|
||||
public EntityUid Station;
|
||||
|
||||
[DataField("nextTransfer", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextTransfer;
|
||||
|
||||
[DataField("nextArrivalsTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
|
||||
@@ -6,9 +6,10 @@ namespace Content.Server.Shuttles.Components;
|
||||
/// <summary>
|
||||
/// If added to a grid gets launched when the emergency shuttle launches.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(EmergencyShuttleSystem))]
|
||||
[RegisterComponent, Access(typeof(EmergencyShuttleSystem)), AutoGenerateComponentPause]
|
||||
public sealed partial class EscapePodComponent : Component
|
||||
{
|
||||
[DataField("launchTime", customTypeSerializer:typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan? LaunchTime;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user