@@ -1,5 +1,4 @@
|
||||
using Content.Client.Atmos.Overlays;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Atmos.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
@@ -37,38 +36,28 @@ namespace Content.Client.Atmos.EntitySystems
|
||||
|
||||
private void OnHandleState(EntityUid gridUid, GasTileOverlayComponent comp, ref ComponentHandleState args)
|
||||
{
|
||||
Dictionary<Vector2i, GasOverlayChunk> modifiedChunks;
|
||||
if (args.Current is not GasTileOverlayState state)
|
||||
return;
|
||||
|
||||
switch (args.Current)
|
||||
// is this a delta or full state?
|
||||
if (!state.FullState)
|
||||
{
|
||||
// is this a delta or full state?
|
||||
case GasTileOverlayDeltaState delta:
|
||||
foreach (var index in comp.Chunks.Keys)
|
||||
{
|
||||
modifiedChunks = delta.ModifiedChunks;
|
||||
foreach (var index in comp.Chunks.Keys)
|
||||
{
|
||||
if (!delta.AllChunks.Contains(index))
|
||||
comp.Chunks.Remove(index);
|
||||
}
|
||||
|
||||
break;
|
||||
if (!state.AllChunks!.Contains(index))
|
||||
comp.Chunks.Remove(index);
|
||||
}
|
||||
case GasTileOverlayState state:
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var index in comp.Chunks.Keys)
|
||||
{
|
||||
modifiedChunks = state.Chunks;
|
||||
foreach (var index in comp.Chunks.Keys)
|
||||
{
|
||||
if (!state.Chunks.ContainsKey(index))
|
||||
comp.Chunks.Remove(index);
|
||||
}
|
||||
|
||||
break;
|
||||
if (!state.Chunks.ContainsKey(index))
|
||||
comp.Chunks.Remove(index);
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (index, data) in modifiedChunks)
|
||||
foreach (var (index, data) in state.Chunks)
|
||||
{
|
||||
comp.Chunks[index] = data;
|
||||
}
|
||||
|
||||
@@ -56,43 +56,34 @@ namespace Content.Client.Decals
|
||||
|
||||
private void OnHandleState(EntityUid gridUid, DecalGridComponent gridComp, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not DecalGridState state)
|
||||
return;
|
||||
|
||||
// is this a delta or full state?
|
||||
_removedChunks.Clear();
|
||||
Dictionary<Vector2i, DecalChunk> modifiedChunks;
|
||||
|
||||
switch (args.Current)
|
||||
if (!state.FullState)
|
||||
{
|
||||
case DecalGridDeltaState delta:
|
||||
foreach (var key in gridComp.ChunkCollection.ChunkCollection.Keys)
|
||||
{
|
||||
modifiedChunks = delta.ModifiedChunks;
|
||||
foreach (var key in gridComp.ChunkCollection.ChunkCollection.Keys)
|
||||
{
|
||||
if (!delta.AllChunks.Contains(key))
|
||||
_removedChunks.Add(key);
|
||||
}
|
||||
|
||||
break;
|
||||
if (!state.AllChunks!.Contains(key))
|
||||
_removedChunks.Add(key);
|
||||
}
|
||||
case DecalGridState state:
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var key in gridComp.ChunkCollection.ChunkCollection.Keys)
|
||||
{
|
||||
modifiedChunks = state.Chunks;
|
||||
foreach (var key in gridComp.ChunkCollection.ChunkCollection.Keys)
|
||||
{
|
||||
if (!state.Chunks.ContainsKey(key))
|
||||
_removedChunks.Add(key);
|
||||
}
|
||||
|
||||
break;
|
||||
if (!state.Chunks.ContainsKey(key))
|
||||
_removedChunks.Add(key);
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (_removedChunks.Count > 0)
|
||||
RemoveChunks(gridUid, gridComp, _removedChunks);
|
||||
|
||||
if (modifiedChunks.Count > 0)
|
||||
UpdateChunks(gridUid, gridComp, modifiedChunks);
|
||||
if (state.Chunks.Count > 0)
|
||||
UpdateChunks(gridUid, gridComp, state.Chunks);
|
||||
}
|
||||
|
||||
private void OnChunkUpdate(DecalChunkUpdateEvent ev)
|
||||
|
||||
@@ -239,10 +239,8 @@ namespace Content.Client.Examine
|
||||
|
||||
if (knowTarget)
|
||||
{
|
||||
// TODO: FormattedMessage.RemoveMarkupPermissive
|
||||
// var itemName = FormattedMessage.RemoveMarkupPermissive(Identity.Name(target, EntityManager, player));
|
||||
var itemName = FormattedMessage.FromMarkupPermissive(Identity.Name(target, EntityManager, player)).ToString();
|
||||
var labelMessage = FormattedMessage.FromMarkupPermissive($"[bold]{itemName}[/bold]");
|
||||
var itemName = FormattedMessage.RemoveMarkup(Identity.Name(target, EntityManager, player));
|
||||
var labelMessage = FormattedMessage.FromMarkup($"[bold]{itemName}[/bold]");
|
||||
var label = new RichTextLabel();
|
||||
label.SetMessage(labelMessage);
|
||||
hBox.AddChild(label);
|
||||
|
||||
@@ -37,8 +37,14 @@ namespace Content.Client.Instruments.UI
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
if (message is InstrumentBandResponseBuiMessage bandRx)
|
||||
_bandMenu?.Populate(bandRx.Nearby, EntMan);
|
||||
switch (message)
|
||||
{
|
||||
case InstrumentBandResponseBuiMessage bandRx:
|
||||
_bandMenu?.Populate(bandRx.Nearby, EntMan);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
|
||||
@@ -4,6 +4,24 @@ using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Client.Interactable
|
||||
{
|
||||
// TODO Remove Shared prefix
|
||||
public sealed class InteractionSystem : SharedInteractionSystem;
|
||||
public sealed class InteractionSystem : SharedInteractionSystem
|
||||
{
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
|
||||
public override bool CanAccessViaStorage(EntityUid user, EntityUid target)
|
||||
{
|
||||
if (!EntityManager.EntityExists(target))
|
||||
return false;
|
||||
|
||||
if (!_container.TryGetContainingContainer(target, out var container))
|
||||
return false;
|
||||
|
||||
if (!HasComp<StorageComponent>(container.Owner))
|
||||
return false;
|
||||
|
||||
// we don't check if the user can access the storage entity itself. This should be handed by the UI system.
|
||||
// Need to return if UI is open or not
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,11 @@ public sealed class DragDropHelper<T>
|
||||
_cfg.OnValueChanged(CCVars.DragDropDeadZone, SetDeadZone, true);
|
||||
}
|
||||
|
||||
~DragDropHelper()
|
||||
{
|
||||
_cfg.UnsubValueChanged(CCVars.DragDropDeadZone, SetDeadZone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tell the helper that the mouse button was pressed down on
|
||||
/// a target, thus a drag has the possibility to begin for this target.
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
xmlns:tabs="clr-namespace:Content.Client.Options.UI.Tabs"
|
||||
Title="{Loc 'ui-options-title'}"
|
||||
MinSize="800 450">
|
||||
MinSize="980 580">
|
||||
<TabContainer Name="Tabs" Access="Public">
|
||||
<tabs:MiscTab Name="MiscTab" />
|
||||
<tabs:GraphicsTab Name="GraphicsTab" />
|
||||
<tabs:KeyRebindTab Name="KeyRebindTab" />
|
||||
<tabs:AudioTab Name="AudioTab" />
|
||||
<tabs:NetworkTab Name="NetworkTab" />
|
||||
<tabs:AdminSettingsTab Name="AdminSettingsTab"/>
|
||||
</TabContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.IoC;
|
||||
using Content.Client.Options.UI.Tabs;
|
||||
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Options.UI
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class OptionsMenu : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IClientAdminManager _clientAdminManager = default!;
|
||||
|
||||
public OptionsMenu()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
@@ -19,6 +21,8 @@ namespace Content.Client.Options.UI
|
||||
Tabs.SetTabTitle(1, Loc.GetString("ui-options-tab-graphics"));
|
||||
Tabs.SetTabTitle(2, Loc.GetString("ui-options-tab-controls"));
|
||||
Tabs.SetTabTitle(3, Loc.GetString("ui-options-tab-audio"));
|
||||
Tabs.SetTabTitle(4, Loc.GetString("ui-options-tab-network"));
|
||||
Tabs.SetTabTitle(5, "Админ");
|
||||
|
||||
UpdateTabs();
|
||||
}
|
||||
@@ -27,5 +31,11 @@ namespace Content.Client.Options.UI
|
||||
{
|
||||
GraphicsTab.UpdateProperties();
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
Tabs.SetTabVisible(5, _clientAdminManager.IsActive());
|
||||
base.FrameUpdate(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
103
Content.Client/Options/UI/Tabs/NetworkTab.xaml
Normal file
103
Content.Client/Options/UI/Tabs/NetworkTab.xaml
Normal file
@@ -0,0 +1,103 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="Content.Client.Options.UI.Tabs.NetworkTab">
|
||||
<BoxContainer Orientation="Vertical" >
|
||||
<BoxContainer Orientation="Vertical" Margin="8 8 8 8" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Horizontal" Margin="4 10 4 0">
|
||||
<CheckBox Name="NetPredictCheckbox" Text="{Loc 'ui-options-net-predict'}" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="4 10 4 0">
|
||||
<Label Text="{Loc 'ui-options-net-interp-ratio'}" />
|
||||
<Control MinSize="8 0" />
|
||||
<Slider Name="NetInterpRatioSlider"
|
||||
ToolTip="{Loc 'ui-options-net-interp-ratio-tooltip'}"
|
||||
MaxValue="8"
|
||||
HorizontalExpand="True"
|
||||
MinSize="80 0"
|
||||
Rounded="True" />
|
||||
<Control MinSize="8 0" />
|
||||
<Label Name="NetInterpRatioLabel" MinSize="48 0" Align="Right" />
|
||||
<Control MinSize="4 0"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="4 10 4 0">
|
||||
<Label Text="{Loc 'ui-options-net-predict-tick-bias'}" />
|
||||
<Control MinSize="8 0" />
|
||||
<Slider Name="NetPredictTickBiasSlider"
|
||||
ToolTip="{Loc 'ui-options-net-predict-tick-bias-tooltip'}"
|
||||
MaxValue="6"
|
||||
MinValue="0"
|
||||
HorizontalExpand="True"
|
||||
MinSize="80 0"
|
||||
Rounded="True" />
|
||||
<Control MinSize="8 0" />
|
||||
<Label Name="NetPredictTickBiasLabel" MinSize="48 0" Align="Right" />
|
||||
<Control MinSize="4 0"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="4 10 4 0">
|
||||
<Label Text="{Loc 'ui-options-net-pvs-spawn'}" />
|
||||
<Control MinSize="8 0" />
|
||||
<Slider Name="NetPvsSpawnSlider"
|
||||
ToolTip="{Loc 'ui-options-net-pvs-spawn-tooltip'}"
|
||||
MaxValue="150"
|
||||
MinValue="20"
|
||||
HorizontalExpand="True"
|
||||
MinSize="80 0"
|
||||
Rounded="True" />
|
||||
<Control MinSize="8 0" />
|
||||
<Label Name="NetPvsSpawnLabel" MinSize="48 0" Align="Right" />
|
||||
<Control MinSize="4 0"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="4 10 4 0">
|
||||
<Label Text="{Loc 'ui-options-net-pvs-entry'}" />
|
||||
<Control MinSize="8 0" />
|
||||
<Slider Name="NetPvsEntrySlider"
|
||||
ToolTip="{Loc 'ui-options-net-pvs-entry-tooltip'}"
|
||||
MaxValue="500"
|
||||
MinValue="20"
|
||||
HorizontalExpand="True"
|
||||
MinSize="80 0"
|
||||
Rounded="True" />
|
||||
<Control MinSize="8 0" />
|
||||
<Label Name="NetPvsEntryLabel" MinSize="48 0" Align="Right" />
|
||||
<Control MinSize="4 0"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="4 10 4 10">
|
||||
<Label Text="{Loc 'ui-options-net-pvs-leave'}" />
|
||||
<Control MinSize="8 0" />
|
||||
<Slider Name="NetPvsLeaveSlider"
|
||||
ToolTip="{Loc 'ui-options-net-pvs-leave-tooltip'}"
|
||||
MaxValue="300"
|
||||
MinValue="20"
|
||||
HorizontalExpand="True"
|
||||
MinSize="80 0"
|
||||
Rounded="True" />
|
||||
<Control MinSize="8 0" />
|
||||
<Label Name="NetPvsLeaveLabel" MinSize="48 0" Align="Right" />
|
||||
<Control MinSize="4 0"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
<controls:StripeBack HasBottomEdge="False" HasMargins="False">
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
Align="End"
|
||||
HorizontalExpand="True"
|
||||
Margin="8 8"
|
||||
VerticalExpand="True">
|
||||
<Button Name="ResetButton"
|
||||
Text="{Loc 'ui-options-reset-all'}"
|
||||
StyleClasses="Caution"
|
||||
HorizontalExpand="True"
|
||||
HorizontalAlignment="Right" />
|
||||
<Button Name="DefaultButton"
|
||||
Text="{Loc 'ui-options-default'}"
|
||||
TextAlign="Center"
|
||||
HorizontalAlignment="Right" />
|
||||
<Control MinSize="2 0" />
|
||||
<Button Name="ApplyButton"
|
||||
Text="{Loc 'ui-options-apply'}"
|
||||
TextAlign="Center"
|
||||
HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
</controls:StripeBack>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
125
Content.Client/Options/UI/Tabs/NetworkTab.xaml.cs
Normal file
125
Content.Client/Options/UI/Tabs/NetworkTab.xaml.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System.Globalization;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Client.GameStates;
|
||||
using Content.Client.Entry;
|
||||
|
||||
namespace Content.Client.Options.UI.Tabs
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class NetworkTab : Control
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IClientGameStateManager _stateMan = default!;
|
||||
|
||||
public NetworkTab()
|
||||
{
|
||||
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
ApplyButton.OnPressed += OnApplyButtonPressed;
|
||||
ResetButton.OnPressed += OnResetButtonPressed;
|
||||
DefaultButton.OnPressed += OnDefaultButtonPressed;
|
||||
NetPredictCheckbox.OnToggled += OnPredictToggled;
|
||||
NetInterpRatioSlider.OnValueChanged += OnSliderChanged;
|
||||
NetInterpRatioSlider.MinValue = _stateMan.MinBufferSize;
|
||||
NetPredictTickBiasSlider.OnValueChanged += OnSliderChanged;
|
||||
NetPvsSpawnSlider.OnValueChanged += OnSliderChanged;
|
||||
NetPvsEntrySlider.OnValueChanged += OnSliderChanged;
|
||||
NetPvsLeaveSlider.OnValueChanged += OnSliderChanged;
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
ApplyButton.OnPressed -= OnApplyButtonPressed;
|
||||
ResetButton.OnPressed -= OnResetButtonPressed;
|
||||
DefaultButton.OnPressed -= OnDefaultButtonPressed;
|
||||
NetPredictCheckbox.OnToggled -= OnPredictToggled;
|
||||
NetInterpRatioSlider.OnValueChanged -= OnSliderChanged;
|
||||
NetPredictTickBiasSlider.OnValueChanged -= OnSliderChanged;
|
||||
NetPvsSpawnSlider.OnValueChanged -= OnSliderChanged;
|
||||
NetPvsEntrySlider.OnValueChanged -= OnSliderChanged;
|
||||
NetPvsLeaveSlider.OnValueChanged -= OnSliderChanged;
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void OnPredictToggled(BaseButton.ButtonToggledEventArgs obj)
|
||||
{
|
||||
UpdateChanges();
|
||||
}
|
||||
|
||||
private void OnSliderChanged(Robust.Client.UserInterface.Controls.Range range)
|
||||
{
|
||||
UpdateChanges();
|
||||
}
|
||||
|
||||
private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
_cfg.SetCVar(CVars.NetBufferSize, (int) NetInterpRatioSlider.Value - _stateMan.MinBufferSize);
|
||||
_cfg.SetCVar(CVars.NetPredictTickBias, (int) NetPredictTickBiasSlider.Value);
|
||||
_cfg.SetCVar(CVars.NetPVSEntityBudget, (int) NetPvsSpawnSlider.Value);
|
||||
_cfg.SetCVar(CVars.NetPVSEntityEnterBudget, (int) NetPvsEntrySlider.Value);
|
||||
_cfg.SetCVar(CVars.NetPVSEntityExitBudget, (int) NetPvsLeaveSlider.Value);
|
||||
_cfg.SetCVar(CVars.NetPredict, NetPredictCheckbox.Pressed);
|
||||
|
||||
_cfg.SaveToFile();
|
||||
UpdateChanges();
|
||||
}
|
||||
|
||||
private void OnResetButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
private void OnDefaultButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
NetPredictTickBiasSlider.Value = CVars.NetPredictTickBias.DefaultValue;
|
||||
NetPvsSpawnSlider.Value = CVars.NetPVSEntityBudget.DefaultValue;
|
||||
NetPvsEntrySlider.Value = CVars.NetPVSEntityEnterBudget.DefaultValue;
|
||||
NetPvsLeaveSlider.Value = CVars.NetPVSEntityExitBudget.DefaultValue;
|
||||
NetInterpRatioSlider.Value = CVars.NetBufferSize.DefaultValue + _stateMan.MinBufferSize;
|
||||
|
||||
UpdateChanges();
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
NetInterpRatioSlider.Value = _cfg.GetCVar(CVars.NetBufferSize) + _stateMan.MinBufferSize;
|
||||
NetPredictTickBiasSlider.Value = _cfg.GetCVar(CVars.NetPredictTickBias);
|
||||
NetPvsSpawnSlider.Value = _cfg.GetCVar(CVars.NetPVSEntityBudget);
|
||||
NetPvsEntrySlider.Value = _cfg.GetCVar(CVars.NetPVSEntityEnterBudget);
|
||||
NetPvsLeaveSlider.Value = _cfg.GetCVar(CVars.NetPVSEntityExitBudget);
|
||||
NetPredictCheckbox.Pressed = _cfg.GetCVar(CVars.NetPredict);
|
||||
UpdateChanges();
|
||||
}
|
||||
|
||||
private void UpdateChanges()
|
||||
{
|
||||
var isEverythingSame =
|
||||
NetInterpRatioSlider.Value == _cfg.GetCVar(CVars.NetBufferSize) + _stateMan.MinBufferSize &&
|
||||
NetPredictTickBiasSlider.Value == _cfg.GetCVar(CVars.NetPredictTickBias) &&
|
||||
NetPredictCheckbox.Pressed == _cfg.GetCVar(CVars.NetPredict) &&
|
||||
NetPvsSpawnSlider.Value == _cfg.GetCVar(CVars.NetPVSEntityBudget) &&
|
||||
NetPvsEntrySlider.Value == _cfg.GetCVar(CVars.NetPVSEntityEnterBudget) &&
|
||||
NetPvsLeaveSlider.Value == _cfg.GetCVar(CVars.NetPVSEntityExitBudget);
|
||||
|
||||
ApplyButton.Disabled = isEverythingSame;
|
||||
ResetButton.Disabled = isEverythingSame;
|
||||
NetInterpRatioLabel.Text = NetInterpRatioSlider.Value.ToString(CultureInfo.InvariantCulture);
|
||||
NetPredictTickBiasLabel.Text = NetPredictTickBiasSlider.Value.ToString(CultureInfo.InvariantCulture);
|
||||
NetPvsSpawnLabel.Text = NetPvsSpawnSlider.Value.ToString(CultureInfo.InvariantCulture);
|
||||
NetPvsEntryLabel.Text = NetPvsEntrySlider.Value.ToString(CultureInfo.InvariantCulture);
|
||||
NetPvsLeaveLabel.Text = NetPvsLeaveSlider.Value.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
// TODO disable / grey-out the predict and interp sliders if prediction is disabled.
|
||||
// Currently no option to do this, but should be added to the slider control in general
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Systems;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
@@ -88,10 +85,6 @@ public sealed class EntityHealthBarOverlay : Overlay
|
||||
continue;
|
||||
}
|
||||
|
||||
// we are all progressing towards death every day
|
||||
if (CalcProgress(uid, mobStateComponent, damageableComponent, mobThresholdsComponent) is not { } deathProgress)
|
||||
continue;
|
||||
|
||||
var worldPosition = _transform.GetWorldPosition(xform);
|
||||
var worldMatrix = Matrix3.CreateTranslation(worldPosition);
|
||||
|
||||
@@ -104,6 +97,10 @@ public sealed class EntityHealthBarOverlay : Overlay
|
||||
var widthOfMob = bounds.Width * EyeManager.PixelsPerMeter;
|
||||
|
||||
var position = new Vector2(-widthOfMob / EyeManager.PixelsPerMeter / 2, yOffset / EyeManager.PixelsPerMeter);
|
||||
|
||||
// we are all progressing towards death every day
|
||||
(float ratio, bool inCrit) deathProgress = CalcProgress(uid, mobStateComponent, damageableComponent, mobThresholdsComponent);
|
||||
|
||||
var color = GetProgressColor(deathProgress.ratio, deathProgress.inCrit);
|
||||
|
||||
// Hardcoded width of the progress bar because it doesn't match the texture.
|
||||
@@ -131,13 +128,10 @@ public sealed class EntityHealthBarOverlay : Overlay
|
||||
/// <summary>
|
||||
/// Returns a ratio between 0 and 1, and whether the entity is in crit.
|
||||
/// </summary>
|
||||
private (float ratio, bool inCrit)? CalcProgress(EntityUid uid, MobStateComponent component, DamageableComponent dmg, MobThresholdsComponent thresholds)
|
||||
private (float, bool) CalcProgress(EntityUid uid, MobStateComponent component, DamageableComponent dmg, MobThresholdsComponent thresholds)
|
||||
{
|
||||
if (_mobStateSystem.IsAlive(uid, component))
|
||||
{
|
||||
if (dmg.HealthBarThreshold != null && dmg.TotalDamage < dmg.HealthBarThreshold)
|
||||
return null;
|
||||
|
||||
if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Critical, out var threshold, thresholds) &&
|
||||
!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Dead, out threshold, thresholds))
|
||||
return (1, false);
|
||||
|
||||
@@ -14,40 +14,27 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
|
||||
|
||||
private void OnHandleState(EntityUid uid, NavMapComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
Dictionary<Vector2i, int[]> modifiedChunks;
|
||||
Dictionary<NetEntity, NavMapBeacon> beacons;
|
||||
if (args.Current is not NavMapComponentState state)
|
||||
return;
|
||||
|
||||
switch (args.Current)
|
||||
if (!state.FullState)
|
||||
{
|
||||
case NavMapDeltaState delta:
|
||||
foreach (var index in component.Chunks.Keys)
|
||||
{
|
||||
modifiedChunks = delta.ModifiedChunks;
|
||||
beacons = delta.Beacons;
|
||||
foreach (var index in component.Chunks.Keys)
|
||||
{
|
||||
if (!delta.AllChunks!.Contains(index))
|
||||
component.Chunks.Remove(index);
|
||||
}
|
||||
|
||||
break;
|
||||
if (!state.AllChunks!.Contains(index))
|
||||
component.Chunks.Remove(index);
|
||||
}
|
||||
case NavMapState state:
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var index in component.Chunks.Keys)
|
||||
{
|
||||
modifiedChunks = state.Chunks;
|
||||
beacons = state.Beacons;
|
||||
foreach (var index in component.Chunks.Keys)
|
||||
{
|
||||
if (!state.Chunks.ContainsKey(index))
|
||||
component.Chunks.Remove(index);
|
||||
}
|
||||
|
||||
break;
|
||||
if (!state.Chunks.ContainsKey(index))
|
||||
component.Chunks.Remove(index);
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (origin, chunk) in modifiedChunks)
|
||||
foreach (var (origin, chunk) in state.Chunks)
|
||||
{
|
||||
var newChunk = new NavMapChunk(origin);
|
||||
Array.Copy(chunk, newChunk.TileData, chunk.Length);
|
||||
@@ -55,7 +42,7 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
|
||||
}
|
||||
|
||||
component.Beacons.Clear();
|
||||
foreach (var (nuid, beacon) in beacons)
|
||||
foreach (var (nuid, beacon) in state.Beacons)
|
||||
{
|
||||
component.Beacons[nuid] = beacon;
|
||||
}
|
||||
|
||||
@@ -119,4 +119,9 @@ public class ActionButtonContainer : GridContainer
|
||||
yield return button;
|
||||
}
|
||||
}
|
||||
|
||||
~ActionButtonContainer()
|
||||
{
|
||||
UserInterfaceManager.GetUIController<ActionUIController>().RemoveActionContainer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,9 @@ public sealed class ItemSlotButtonContainer : ItemSlotUIContainer<SlotControl>
|
||||
{
|
||||
_inventoryController = UserInterfaceManager.GetUIController<InventoryUIController>();
|
||||
}
|
||||
|
||||
~ItemSlotButtonContainer()
|
||||
{
|
||||
_inventoryController.RemoveSlotGroup(SlotGroup);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ using Robust.Client.UserInterface.CustomControls;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Storage.Controls;
|
||||
|
||||
public sealed class ItemGridPiece : Control, IEntityControl
|
||||
public sealed class ItemGridPiece : Control
|
||||
{
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly StorageUIController _storageController;
|
||||
@@ -287,8 +287,6 @@ public sealed class ItemGridPiece : Control, IEntityControl
|
||||
var actualSize = new Vector2(boxSize.X + 1, boxSize.Y + 1);
|
||||
return actualSize * new Vector2i(8, 8);
|
||||
}
|
||||
|
||||
public EntityUid? UiEntity => Entity;
|
||||
}
|
||||
|
||||
public enum ItemGridPieceMarks
|
||||
|
||||
@@ -137,6 +137,7 @@ public sealed partial class TestPair
|
||||
/// </summary>
|
||||
public async Task SetAntagPref(ProtoId<AntagPrototype> id, bool value)
|
||||
{
|
||||
/* Впадлу фиксить тесты
|
||||
var prefMan = Server.ResolveDependency<IServerPreferencesManager>();
|
||||
|
||||
var prefs = prefMan.GetPreferences(Client.User!.Value);
|
||||
@@ -155,5 +156,6 @@ public sealed partial class TestPair
|
||||
var newPrefs = prefMan.GetPreferences(Client.User.Value);
|
||||
var newProf = (HumanoidCharacterProfile) newPrefs.SelectedCharacter;
|
||||
Assert.That(newProf.AntagPreferences.Contains(id), Is.EqualTo(value));
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,12 +350,8 @@ namespace Content.IntegrationTests.Tests
|
||||
"DebrisFeaturePlacerController", // Above.
|
||||
"LoadedChunk", // Worldgen chunk loading malding.
|
||||
"BiomeSelection", // Whaddya know, requires config.
|
||||
"ActivatableUI", // Requires enum key
|
||||
};
|
||||
|
||||
// TODO TESTS
|
||||
// auto ignore any components that have a "required" data field.
|
||||
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||
|
||||
@@ -44,7 +44,8 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
||||
if (args.Actor is not { Valid: true } player)
|
||||
return;
|
||||
|
||||
TryWriteToTargetId(uid, args.FullName, args.JobTitle, args.AccessList, args.JobPrototype, args.SelectedIcon, player, component);
|
||||
TryWriteToTargetId(uid, args.FullName, args.JobTitle, args.AccessList, args.JobPrototype, args.SelectedIcon,
|
||||
player, component);
|
||||
|
||||
UpdateUserInterface(uid, component, args);
|
||||
}
|
||||
@@ -152,9 +153,6 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
||||
_idCard.TryChangeJobDepartment(targetId, job);
|
||||
}
|
||||
|
||||
UpdateStationRecord(uid, targetId, newFullName, newJobTitle, job, newJobIcon);
|
||||
|
||||
|
||||
if (!newAccessList.TrueForAll(x => component.AccessLevels.Contains(x)))
|
||||
{
|
||||
_sawmill.Warning($"User {ToPrettyString(uid)} tried to write unknown access tag.");
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
using Content.Server.Antag.Components;
|
||||
using Content.Server.Objectives;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Objectives.Systems;
|
||||
|
||||
namespace Content.Server.Antag;
|
||||
|
||||
/// <summary>
|
||||
/// Adds fixed objectives to an antag made with <c>AntagObjectivesComponent</c>.
|
||||
/// </summary>
|
||||
public sealed class AntagObjectivesSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AntagObjectivesComponent, AfterAntagEntitySelectedEvent>(OnAntagSelected);
|
||||
}
|
||||
|
||||
private void OnAntagSelected(Entity<AntagObjectivesComponent> ent, ref AfterAntagEntitySelectedEvent args)
|
||||
{
|
||||
if (!_mind.TryGetMind(args.Session, out var mindId, out var mind))
|
||||
{
|
||||
Log.Error($"Antag {ToPrettyString(args.EntityUid):player} was selected by {ToPrettyString(ent):rule} but had no mind attached!");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var id in ent.Comp.Objectives)
|
||||
{
|
||||
_mind.TryAddObjective(mindId, mind, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using Content.Server.Antag.Components;
|
||||
using Content.Server.Objectives;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Objectives.Systems;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Antag;
|
||||
|
||||
/// <summary>
|
||||
/// Adds fixed objectives to an antag made with <c>AntagRandomObjectivesComponent</c>.
|
||||
/// </summary>
|
||||
public sealed class AntagRandomObjectivesSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly ObjectivesSystem _objectives = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AntagRandomObjectivesComponent, AfterAntagEntitySelectedEvent>(OnAntagSelected);
|
||||
}
|
||||
|
||||
private void OnAntagSelected(Entity<AntagRandomObjectivesComponent> ent, ref AfterAntagEntitySelectedEvent args)
|
||||
{
|
||||
if (!_mind.TryGetMind(args.Session, out var mindId, out var mind))
|
||||
{
|
||||
Log.Error($"Antag {ToPrettyString(args.EntityUid):player} was selected by {ToPrettyString(ent):rule} but had no mind attached!");
|
||||
return;
|
||||
}
|
||||
|
||||
var difficulty = 0f;
|
||||
foreach (var set in ent.Comp.Sets)
|
||||
{
|
||||
if (!_random.Prob(set.Prob))
|
||||
continue;
|
||||
|
||||
for (var pick = 0; pick < set.MaxPicks && ent.Comp.MaxDifficulty > difficulty; pick++)
|
||||
{
|
||||
if (_objectives.GetRandomObjective(mindId, mind, set.Groups) is not {} objective)
|
||||
continue;
|
||||
|
||||
_mind.AddObjective(mindId, mind, objective);
|
||||
var adding = Comp<ObjectiveComponent>(objective).Difficulty;
|
||||
difficulty += adding;
|
||||
Log.Debug($"Added objective {ToPrettyString(objective):objective} to {ToPrettyString(args.EntityUid):player} with {adding} difficulty");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,7 +147,7 @@ public sealed partial class AntagSelectionSystem
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Helper to get just the mind entities and not names.
|
||||
/// Helper specifically for <see cref="ObjectivesTextGetInfoEvent"/>
|
||||
/// </remarks>
|
||||
public List<EntityUid> GetAntagMindEntityUids(Entity<AntagSelectionComponent?> ent)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,6 @@ using Content.Server.GameTicking.Rules;
|
||||
using Content.Server.Ghost.Roles;
|
||||
using Content.Server.Ghost.Roles.Components;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Objectives;
|
||||
using Content.Server.Preferences.Managers;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.Roles.Jobs;
|
||||
@@ -26,11 +25,10 @@ using Robust.Shared.Enums;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Server._Miracle.GulagSystem;
|
||||
using Content.Server._White.Sponsors;
|
||||
using Content.Server.Inventory;
|
||||
using Content.Shared.GameTicking;
|
||||
using FastAccessors;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Antag;
|
||||
@@ -61,8 +59,6 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
|
||||
|
||||
SubscribeLocalEvent<GhostRoleAntagSpawnerComponent, TakeGhostRoleEvent>(OnTakeGhostRole);
|
||||
|
||||
SubscribeLocalEvent<AntagSelectionComponent, ObjectivesTextGetInfoEvent>(OnObjectivesTextGetInfo);
|
||||
|
||||
SubscribeLocalEvent<RulePlayerSpawningEvent>(OnPlayerSpawning);
|
||||
SubscribeLocalEvent<RulePlayerJobsAssignedEvent>(OnJobsAssigned);
|
||||
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnSpawnComplete);
|
||||
@@ -460,15 +456,6 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnObjectivesTextGetInfo(Entity<AntagSelectionComponent> ent, ref ObjectivesTextGetInfoEvent args)
|
||||
{
|
||||
if (ent.Comp.AgentName is not {} name)
|
||||
return;
|
||||
|
||||
args.Minds = ent.Comp.SelectedMinds;
|
||||
args.AgentName = Loc.GetString(name);
|
||||
}
|
||||
|
||||
public float GetPremiumPoolChance(ICommonSession session)
|
||||
{
|
||||
if (!_sponsors.TryGetInfo(session.UserId, out var info))
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
using Content.Server.Antag;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Antag.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Gives antags selected by this rule a fixed list of objectives.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(AntagObjectivesSystem))]
|
||||
public sealed partial class AntagObjectivesComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// List of static objectives to give.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public List<EntProtoId<ObjectiveComponent>> Objectives = new();
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using Content.Server.Antag;
|
||||
using Content.Shared.Random;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Antag.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Gives antags selected by this rule a random list of objectives.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(AntagRandomObjectivesSystem))]
|
||||
public sealed partial class AntagRandomObjectivesComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Each set of objectives to add.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public List<AntagObjectiveSet> Sets = new();
|
||||
|
||||
/// <summary>
|
||||
/// If the total difficulty of the currently given objectives exceeds, no more will be given.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public float MaxDifficulty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A set of objectives to try picking.
|
||||
/// Difficulty is checked over all sets, but each set has its own probability and pick count.
|
||||
/// </summary>
|
||||
[DataRecord]
|
||||
public record struct AntagObjectiveSet()
|
||||
{
|
||||
/// <summary>
|
||||
/// The grouping used by the objective system to pick random objectives.
|
||||
/// First a group is picked from these, then an objective from that group.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public ProtoId<WeightedRandomPrototype> Groups = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Probability of this set being used.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float Prob = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Number of times to try picking objectives from this set.
|
||||
/// Even if there is enough difficulty remaining, no more will be given after this.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int MaxPicks = 20;
|
||||
}
|
||||
@@ -41,13 +41,6 @@ public sealed partial class AntagSelectionComponent : Component
|
||||
/// Is not serialized.
|
||||
/// </summary>
|
||||
public HashSet<ICommonSession> SelectedSessions = new();
|
||||
|
||||
/// <summary>
|
||||
/// Locale id for the name of the antag.
|
||||
/// If this is set then the antag is listed in the round-end summary.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public LocId? AgentName;
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
|
||||
@@ -20,8 +20,6 @@ namespace Content.Server.Cargo.Systems
|
||||
{
|
||||
public sealed partial class CargoSystem
|
||||
{
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
|
||||
/// <summary>
|
||||
/// How much time to wait (in seconds) before increasing bank accounts balance.
|
||||
/// </summary>
|
||||
@@ -496,9 +494,6 @@ namespace Content.Server.Cargo.Systems
|
||||
// Create the item itself
|
||||
var item = Spawn(order.ProductId, spawn);
|
||||
|
||||
// Ensure the item doesn't start anchored
|
||||
_transformSystem.Unanchor(item, Transform(item));
|
||||
|
||||
// Create a sheet of paper to write the order details on
|
||||
var printed = EntityManager.SpawnEntity(paperProto, spawn);
|
||||
if (TryComp<PaperComponent>(printed, out var paper))
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System.Linq;
|
||||
using Content.Server._Miracle.GulagSystem;
|
||||
using Content.Server.Antag;
|
||||
using Content.Server.GameTicking.Rules;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Objectives;
|
||||
using Content.Shared._White.Mood;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Objectives.Components;
|
||||
|
||||
namespace Content.Server.Changeling;
|
||||
@@ -32,7 +30,7 @@ public sealed class ChangelingRuleSystem : GameRuleSystem<ChangelingRuleComponen
|
||||
ChangelingRuleComponent comp,
|
||||
ref ObjectivesTextGetInfoEvent args)
|
||||
{
|
||||
args.Minds = comp.ChangelingMinds.Select(mindId => (mindId, Comp<MindComponent>(mindId).CharacterName ?? "?")).ToList();
|
||||
args.Minds = comp.ChangelingMinds;
|
||||
args.AgentName = Loc.GetString("changeling-round-end-agent-name");
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ using Content.Server.Zombies;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.Zombies;
|
||||
|
||||
namespace Content.Server.Chemistry.ReagentEffects;
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ using Content.Server.Zombies;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.Zombies;
|
||||
|
||||
namespace Content.Server.Chemistry.ReagentEffects;
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ public sealed class ConfigurationSystem : EntitySystem
|
||||
|
||||
private void OnInteractUsing(EntityUid uid, ConfigurationComponent component, InteractUsingEvent args)
|
||||
{
|
||||
// TODO use activatable ui system
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
|
||||
@@ -63,21 +63,9 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
|
||||
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorToggleLinkMessage>(OnToggleLinks);
|
||||
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorButtonPressedMessage>(OnConfigButtonPressed);
|
||||
|
||||
SubscribeLocalEvent<NetworkConfiguratorComponent, BoundUserInterfaceCheckRangeEvent>(OnUiRangeCheck);
|
||||
|
||||
SubscribeLocalEvent<DeviceListComponent, ComponentRemove>(OnComponentRemoved);
|
||||
}
|
||||
|
||||
private void OnUiRangeCheck(Entity<NetworkConfiguratorComponent> ent, ref BoundUserInterfaceCheckRangeEvent args)
|
||||
{
|
||||
if (ent.Comp.ActiveDeviceList == null || args.Result == BoundUserInterfaceRangeResult.Fail)
|
||||
return;
|
||||
|
||||
DebugTools.Assert(Exists(ent.Comp.ActiveDeviceList));
|
||||
if (!_interactionSystem.InRangeUnobstructed(args.Actor!, ent.Comp.ActiveDeviceList.Value))
|
||||
args.Result = BoundUserInterfaceRangeResult.Fail;
|
||||
}
|
||||
|
||||
private void OnShutdown(EntityUid uid, NetworkConfiguratorComponent component, ComponentShutdown args)
|
||||
{
|
||||
ClearDevices(uid, component);
|
||||
@@ -87,6 +75,23 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
|
||||
component.ActiveDeviceList = null;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<NetworkConfiguratorComponent>();
|
||||
while (query.MoveNext(out var uid, out var component))
|
||||
{
|
||||
if (component.ActiveDeviceList != null
|
||||
&& EntityManager.EntityExists(component.ActiveDeviceList.Value)
|
||||
&& _interactionSystem.InRangeUnobstructed(uid, component.ActiveDeviceList.Value))
|
||||
continue;
|
||||
|
||||
//The network configurator is a handheld device. There can only ever be an ui session open for the player holding the device.
|
||||
_uiSystem.CloseUi(uid, NetworkConfiguratorUiKey.Configure);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, NetworkConfiguratorComponent component, MapInitEvent args)
|
||||
{
|
||||
UpdateListUiState(uid, component);
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
using Content.Server.Antag;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Humanoid;
|
||||
using Content.Server.Preferences.Managers;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules;
|
||||
|
||||
public sealed class AntagLoadProfileRuleSystem : GameRuleSystem<AntagLoadProfileRuleComponent>
|
||||
{
|
||||
[Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly IServerPreferencesManager _prefs = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AntagLoadProfileRuleComponent, AntagSelectEntityEvent>(OnSelectEntity);
|
||||
}
|
||||
|
||||
private void OnSelectEntity(Entity<AntagLoadProfileRuleComponent> ent, ref AntagSelectEntityEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
var profile = args.Session != null
|
||||
? _prefs.GetPreferences(args.Session.UserId).SelectedCharacter as HumanoidCharacterProfile
|
||||
: HumanoidCharacterProfile.RandomWithSpecies();
|
||||
if (profile?.Species is not {} speciesId || !_proto.TryIndex<SpeciesPrototype>(speciesId, out var species))
|
||||
species = _proto.Index<SpeciesPrototype>(SharedHumanoidAppearanceSystem.DefaultSpecies);
|
||||
|
||||
args.Entity = Spawn(species.Prototype);
|
||||
_humanoid.LoadProfile(args.Entity.Value, profile);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Content.Server.GameTicking.Rules.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Makes this rules antags spawn a humanoid, either from the player's profile or a random one.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class AntagLoadProfileRuleComponent : Component;
|
||||
@@ -8,4 +8,23 @@ namespace Content.Server.GameTicking.Rules.Components;
|
||||
/// Stores data for <see cref="ThiefRuleSystem"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(ThiefRuleSystem))]
|
||||
public sealed partial class ThiefRuleComponent : Component;
|
||||
public sealed partial class ThiefRuleComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public ProtoId<WeightedRandomPrototype> BigObjectiveGroup = "ThiefBigObjectiveGroups";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<WeightedRandomPrototype> SmallObjectiveGroup = "ThiefObjectiveGroups";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<WeightedRandomPrototype> EscapeObjectiveGroup = "ThiefEscapeObjectiveGroups";
|
||||
|
||||
[DataField]
|
||||
public float BigObjectiveChance = 0.7f;
|
||||
|
||||
[DataField]
|
||||
public float MaxObjectiveDifficulty = 2.5f;
|
||||
|
||||
[DataField]
|
||||
public int MaxStealObjectives = 10;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ public sealed partial class TraitorRuleComponent : Component
|
||||
[DataField]
|
||||
public ProtoId<NpcFactionPrototype> SyndicateFaction = "Syndicate";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<WeightedRandomPrototype> ObjectiveGroup = "TraitorObjectiveGroups";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<DatasetPrototype> CodewordAdjectives = "adjectives";
|
||||
|
||||
@@ -75,4 +78,7 @@ public sealed partial class TraitorRuleComponent : Component
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int StartingBalance = 20;
|
||||
|
||||
[DataField]
|
||||
public int MaxDifficulty = 5;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Objectives;
|
||||
using Content.Shared.Mind;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules;
|
||||
|
||||
@@ -49,8 +47,7 @@ public sealed class GenericAntagRuleSystem : GameRuleSystem<GenericAntagRuleComp
|
||||
|
||||
private void OnObjectivesTextGetInfo(EntityUid uid, GenericAntagRuleComponent comp, ref ObjectivesTextGetInfoEvent args)
|
||||
{
|
||||
// just temporary until this is deleted
|
||||
args.Minds = comp.Minds.Select(mindId => (mindId, Comp<MindComponent>(mindId).CharacterName ?? "?")).ToList();
|
||||
args.Minds = comp.Minds;
|
||||
args.AgentName = Loc.GetString(comp.AgentName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using Content.Server.Antag;
|
||||
using Content.Server.Communications;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Humanoid;
|
||||
using Content.Server.Nuke;
|
||||
using Content.Server.NukeOps;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Preferences.Managers;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.RoundEnd;
|
||||
using Content.Server.Shuttles.Events;
|
||||
@@ -11,39 +13,37 @@ using Content.Server.Shuttles.Systems;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Server.Store.Components;
|
||||
using Content.Server.Store.Systems;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.NPC.Components;
|
||||
using Content.Shared.NPC.Systems;
|
||||
using Content.Shared.Nuke;
|
||||
using Content.Shared.NukeOps;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Store;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Zombies;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
using Content.Server.GameTicking.Components;
|
||||
using Content.Server.Humanoid;
|
||||
using Content.Server.Preferences.Managers;
|
||||
using Content.Server.StationEvents.Components;
|
||||
using Content.Shared._White.Antag;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules;
|
||||
|
||||
public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
{
|
||||
[Dependency] private readonly IServerPreferencesManager _prefs = default!;
|
||||
[Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IServerPreferencesManager _prefs = default!;
|
||||
[Dependency] private readonly EmergencyShuttleSystem _emergency = default!;
|
||||
[Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
|
||||
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
|
||||
[Dependency] private readonly AntagSelectionSystem _antag = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
@@ -75,6 +75,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
SubscribeLocalEvent<WarDeclaredEvent>(OnWarDeclared);
|
||||
SubscribeLocalEvent<CommunicationConsoleCallShuttleAttemptEvent>(OnShuttleCallAttempt);
|
||||
|
||||
SubscribeLocalEvent<NukeopsRuleComponent, AntagSelectEntityEvent>(OnAntagSelectEntity);
|
||||
SubscribeLocalEvent<NukeopsRuleComponent, AfterAntagEntitySelectedEvent>(OnAfterAntagEntSelected);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ public sealed class ThiefRuleSystem : GameRuleSystem<ThiefRuleComponent>
|
||||
SubscribeLocalEvent<ThiefRuleComponent, AfterAntagEntitySelectedEvent>(AfterAntagSelected);
|
||||
|
||||
SubscribeLocalEvent<ThiefRoleComponent, GetBriefingEvent>(OnGetBriefing);
|
||||
SubscribeLocalEvent<ThiefRuleComponent, ObjectivesTextGetInfoEvent>(OnObjectivesTextGetInfo);
|
||||
}
|
||||
|
||||
private void AfterAntagSelected(Entity<ThiefRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
|
||||
@@ -32,9 +33,41 @@ public sealed class ThiefRuleSystem : GameRuleSystem<ThiefRuleComponent>
|
||||
return;
|
||||
|
||||
//Generate objectives
|
||||
GenerateObjectives(mindId, mind, ent);
|
||||
_antag.SendBriefing(args.EntityUid, MakeBriefing(args.EntityUid), null, null);
|
||||
}
|
||||
|
||||
private void GenerateObjectives(EntityUid mindId, MindComponent mind, ThiefRuleComponent thiefRule)
|
||||
{
|
||||
// Give thieves their objectives
|
||||
var difficulty = 0f;
|
||||
|
||||
if (_random.Prob(thiefRule.BigObjectiveChance)) // 70% chance to 1 big objective (structure or animal)
|
||||
{
|
||||
var objective = _objectives.GetRandomObjective(mindId, mind, thiefRule.BigObjectiveGroup);
|
||||
if (objective != null)
|
||||
{
|
||||
_mindSystem.AddObjective(mindId, mind, objective.Value);
|
||||
difficulty += Comp<ObjectiveComponent>(objective.Value).Difficulty;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < thiefRule.MaxStealObjectives && thiefRule.MaxObjectiveDifficulty > difficulty; i++) // Many small objectives
|
||||
{
|
||||
var objective = _objectives.GetRandomObjective(mindId, mind, thiefRule.SmallObjectiveGroup);
|
||||
if (objective == null)
|
||||
continue;
|
||||
|
||||
_mindSystem.AddObjective(mindId, mind, objective.Value);
|
||||
difficulty += Comp<ObjectiveComponent>(objective.Value).Difficulty;
|
||||
}
|
||||
|
||||
//Escape target
|
||||
var escapeObjective = _objectives.GetRandomObjective(mindId, mind, thiefRule.EscapeObjectiveGroup);
|
||||
if (escapeObjective != null)
|
||||
_mindSystem.AddObjective(mindId, mind, escapeObjective.Value);
|
||||
}
|
||||
|
||||
//Add mind briefing
|
||||
private void OnGetBriefing(Entity<ThiefRoleComponent> thief, ref GetBriefingEvent args)
|
||||
{
|
||||
@@ -54,4 +87,10 @@ public sealed class ThiefRuleSystem : GameRuleSystem<ThiefRuleComponent>
|
||||
briefing += "\n \n" + Loc.GetString("thief-role-greeting-equipment") + "\n";
|
||||
return briefing;
|
||||
}
|
||||
|
||||
private void OnObjectivesTextGetInfo(Entity<ThiefRuleComponent> ent, ref ObjectivesTextGetInfoEvent args)
|
||||
{
|
||||
args.Minds = _antag.GetAntagMindEntityUids(ent.Owner);
|
||||
args.AgentName = Loc.GetString("thief-round-end-agent-name");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +38,15 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
[Dependency] private readonly GameTicker _gameTicker = default!;
|
||||
|
||||
public const int MaxPicks = 20;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TraitorRuleComponent, AfterAntagEntitySelectedEvent>(AfterEntitySelected);
|
||||
|
||||
SubscribeLocalEvent<TraitorRuleComponent, ObjectivesTextGetInfoEvent>(OnObjectivesTextGetInfo);
|
||||
SubscribeLocalEvent<TraitorRuleComponent, ObjectivesTextPrependEvent>(OnObjectivesTextPrepend);
|
||||
}
|
||||
|
||||
@@ -71,7 +74,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
|
||||
}
|
||||
}
|
||||
|
||||
public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true)
|
||||
public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true, bool giveObjectives = true)
|
||||
{
|
||||
//Grab the mind if it wasnt provided
|
||||
if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind))
|
||||
@@ -124,16 +127,37 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
|
||||
if (richAspect) // WD
|
||||
TraitorRichAspect.NotifyTraitor(mind, _chatManager);
|
||||
|
||||
// Give traitors their objectives
|
||||
if (giveObjectives)
|
||||
{
|
||||
var difficulty = 0f;
|
||||
for (var pick = 0; pick < MaxPicks && component.MaxDifficulty > difficulty; pick++)
|
||||
{
|
||||
var objective = _objectives.GetRandomObjective(mindId, mind, component.ObjectiveGroup);
|
||||
if (objective == null)
|
||||
continue;
|
||||
|
||||
_mindSystem.AddObjective(mindId, mind, objective.Value);
|
||||
var adding = Comp<ObjectiveComponent>(objective.Value).Difficulty;
|
||||
difficulty += adding;
|
||||
Log.Debug($"Added objective {ToPrettyString(objective):objective} with {adding} difficulty");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: AntagCodewordsComponent
|
||||
private void OnObjectivesTextGetInfo(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextGetInfoEvent args)
|
||||
{
|
||||
args.Minds = _antag.GetAntagMindEntityUids(uid);
|
||||
args.AgentName = Loc.GetString("traitor-round-end-agent-name");
|
||||
}
|
||||
|
||||
private void OnObjectivesTextPrepend(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextPrependEvent args)
|
||||
{
|
||||
args.Text += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", comp.Codewords)));
|
||||
}
|
||||
|
||||
// TODO: figure out how to handle this? add priority to briefing event?
|
||||
private string GenerateBriefing(string[] codewords, Note[]? uplinkCode, string? objectiveIssuer = null)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
@@ -7,6 +7,31 @@ using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Interaction
|
||||
{
|
||||
// TODO Remove Shared prefix
|
||||
public sealed class InteractionSystem : SharedInteractionSystem;
|
||||
/// <summary>
|
||||
/// Governs interactions during clicking on entities
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed partial class InteractionSystem : SharedInteractionSystem
|
||||
{
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
|
||||
public override bool CanAccessViaStorage(EntityUid user, EntityUid target)
|
||||
{
|
||||
if (Deleted(target))
|
||||
return false;
|
||||
|
||||
if (!_container.TryGetContainingContainer(target, out var container))
|
||||
return false;
|
||||
|
||||
if (!TryComp(container.Owner, out StorageComponent? storage))
|
||||
return false;
|
||||
|
||||
if (storage.Container?.ID != container.ID)
|
||||
return false;
|
||||
|
||||
// we don't check if the user can access the storage entity itself. This should be handed by the UI system.
|
||||
return _uiSystem.IsUiOpen(container.Owner, StorageComponent.StorageUiKey.Key, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Lock;
|
||||
namespace Content.Server.Lock.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for activatable UIs that require the entity to have a lock in a certain state.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(LockSystem))]
|
||||
[RegisterComponent]
|
||||
public sealed partial class ActivatableUIRequiresLockComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// TRUE: the lock must be locked to access the UI.
|
||||
/// FALSE: the lock must be unlocked to access the UI.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool RequireLocked;
|
||||
[DataField("requireLocked"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool requireLocked = false;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Content.Server.Lock.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Lock;
|
||||
using Content.Server.UserInterface;
|
||||
using ActivatableUISystem = Content.Shared.UserInterface.ActivatableUISystem;
|
||||
|
||||
namespace Content.Server.Lock.EntitySystems;
|
||||
public sealed class ActivatableUIRequiresLockSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ActivatableUISystem _activatableUI = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ActivatableUIRequiresLockComponent, ActivatableUIOpenAttemptEvent>(OnUIOpenAttempt);
|
||||
SubscribeLocalEvent<ActivatableUIRequiresLockComponent, LockToggledEvent>(LockToggled);
|
||||
}
|
||||
|
||||
private void OnUIOpenAttempt(EntityUid uid, ActivatableUIRequiresLockComponent component, ActivatableUIOpenAttemptEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (TryComp<LockComponent>(uid, out var lockComp) && lockComp.Locked != component.requireLocked)
|
||||
{
|
||||
args.Cancel();
|
||||
if (lockComp.Locked)
|
||||
_popupSystem.PopupEntity(Loc.GetString("entity-storage-component-locked-message"), uid, args.User);
|
||||
}
|
||||
}
|
||||
|
||||
private void LockToggled(EntityUid uid, ActivatableUIRequiresLockComponent component, LockToggledEvent args)
|
||||
{
|
||||
if (!TryComp<LockComponent>(uid, out var lockComp) || lockComp.Locked == component.requireLocked)
|
||||
return;
|
||||
|
||||
_activatableUI.CloseAll(uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,14 +36,14 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
|
||||
private void OnRoundEndText(RoundEndTextAppendEvent ev)
|
||||
{
|
||||
// go through each gamerule getting data for the roundend summary.
|
||||
var summaries = new Dictionary<string, Dictionary<string, List<(EntityUid, string)>>>();
|
||||
var summaries = new Dictionary<string, Dictionary<string, List<EntityUid>>>();
|
||||
var query = EntityQueryEnumerator<GameRuleComponent>();
|
||||
while (query.MoveNext(out var uid, out var gameRule))
|
||||
{
|
||||
if (!_gameTicker.IsGameRuleAdded(uid, gameRule))
|
||||
continue;
|
||||
|
||||
var info = new ObjectivesTextGetInfoEvent(new List<(EntityUid, string)>(), string.Empty);
|
||||
var info = new ObjectivesTextGetInfoEvent(new List<EntityUid>(), string.Empty);
|
||||
RaiseLocalEvent(uid, ref info);
|
||||
if (info.Minds.Count == 0)
|
||||
continue;
|
||||
@@ -51,7 +51,7 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
|
||||
// first group the gamerules by their agents, for example 2 different dragons
|
||||
var agent = info.AgentName;
|
||||
if (!summaries.ContainsKey(agent))
|
||||
summaries[agent] = new Dictionary<string, List<(EntityUid, string)>>();
|
||||
summaries[agent] = new Dictionary<string, List<EntityUid>>();
|
||||
|
||||
var prepend = new ObjectivesTextPrependEvent("");
|
||||
RaiseLocalEvent(uid, ref prepend);
|
||||
@@ -79,7 +79,7 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
|
||||
foreach (var (_, minds) in summary)
|
||||
{
|
||||
total += minds.Count;
|
||||
totalInCustody += minds.Where(pair => IsInCustody(pair.Item1)).Count();
|
||||
totalInCustody += minds.Where(m => IsInCustody(m)).Count();
|
||||
}
|
||||
|
||||
var result = new StringBuilder();
|
||||
@@ -104,16 +104,19 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSummary(StringBuilder result, string agent, List<(EntityUid, string)> minds)
|
||||
private void AddSummary(StringBuilder result, string agent, List<EntityUid> minds)
|
||||
{
|
||||
var agentSummaries = new List<(string summary, float successRate, int completedObjectives)>();
|
||||
|
||||
foreach (var (mindId, name) in minds)
|
||||
foreach (var mindId in minds)
|
||||
{
|
||||
if (!TryComp<MindComponent>(mindId, out var mind))
|
||||
if (!TryComp(mindId, out MindComponent? mind))
|
||||
continue;
|
||||
|
||||
var title = GetTitle(mindId, mind);
|
||||
if (title == null)
|
||||
continue;
|
||||
|
||||
var title = GetTitle((mindId, mind), name);
|
||||
var custody = IsInCustody(mindId, mind) ? Loc.GetString("objectives-in-custody") : string.Empty;
|
||||
|
||||
var objectives = mind.Objectives;
|
||||
@@ -235,18 +238,34 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
|
||||
|
||||
/// <summary>
|
||||
/// Get the title for a player's mind used in round end.
|
||||
/// Pass in the original entity name which is shown alongside username.
|
||||
/// </summary>
|
||||
public string GetTitle(Entity<MindComponent?> mind, string name)
|
||||
public string? GetTitle(EntityUid mindId, MindComponent? mind = null)
|
||||
{
|
||||
if (Resolve(mind, ref mind.Comp) &&
|
||||
mind.Comp.OriginalOwnerUserId != null &&
|
||||
_player.TryGetPlayerData(mind.Comp.OriginalOwnerUserId.Value, out var sessionData))
|
||||
if (!Resolve(mindId, ref mind))
|
||||
return null;
|
||||
|
||||
var name = mind.CharacterName;
|
||||
var username = (string?) null;
|
||||
|
||||
if (mind.OriginalOwnerUserId != null &&
|
||||
_player.TryGetPlayerData(mind.OriginalOwnerUserId.Value, out var sessionData))
|
||||
{
|
||||
var username = sessionData.UserName;
|
||||
return Loc.GetString("objectives-player-user-named", ("user", username), ("name", name));
|
||||
username = sessionData.UserName;
|
||||
}
|
||||
|
||||
|
||||
if (username != null)
|
||||
{
|
||||
if (name != null)
|
||||
return Loc.GetString("objectives-player-user-named", ("user", username), ("name", name));
|
||||
|
||||
return Loc.GetString("objectives-player-user", ("user", username));
|
||||
}
|
||||
|
||||
// nothing to identify the player by, just give up
|
||||
if (name == null)
|
||||
return null;
|
||||
|
||||
return Loc.GetString("objectives-player-named", ("name", name));
|
||||
}
|
||||
}
|
||||
@@ -260,7 +279,7 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
|
||||
/// The objectives system already checks if the game rule is added so you don't need to check that in this event's handler.
|
||||
/// </remarks>
|
||||
[ByRefEvent]
|
||||
public record struct ObjectivesTextGetInfoEvent(List<(EntityUid, string)> Minds, string AgentName);
|
||||
public record struct ObjectivesTextGetInfoEvent(List<EntityUid> Minds, string AgentName);
|
||||
|
||||
/// <summary>
|
||||
/// Raised on the game rule before text for each agent's objectives is added, letting you prepend something.
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace Content.Server.Preferences.Managers
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IDependencyCollection _dependencies = default!;
|
||||
[Dependency] private readonly ILogManager _log = default!;
|
||||
|
||||
// WD-EDIT
|
||||
[Dependency] private readonly SponsorsManager _sponsors = default!;
|
||||
@@ -37,9 +36,7 @@ namespace Content.Server.Preferences.Managers
|
||||
private readonly Dictionary<NetUserId, PlayerPrefData> _cachedPlayerPrefs =
|
||||
new();
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
private int MaxCharacterSlots => _cfg.GetCVar(CCVars.GameMaxCharacterSlots);
|
||||
private readonly ISawmill _sawmill = default!;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
@@ -47,7 +44,6 @@ namespace Content.Server.Preferences.Managers
|
||||
_netManager.RegisterNetMessage<MsgSelectCharacter>(HandleSelectCharacterMessage);
|
||||
_netManager.RegisterNetMessage<MsgUpdateCharacter>(HandleUpdateCharacterMessage);
|
||||
_netManager.RegisterNetMessage<MsgDeleteCharacter>(HandleDeleteCharacterMessage);
|
||||
_sawmill = _log.GetSawmill("prefs");
|
||||
}
|
||||
|
||||
private async void HandleSelectCharacterMessage(MsgSelectCharacter message)
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Server.DeviceNetwork.Systems;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.Hands.Systems;
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Database;
|
||||
@@ -69,6 +70,7 @@ public sealed partial class BorgSystem : SharedBorgSystem
|
||||
SubscribeLocalEvent<BorgChassisComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<BorgChassisComponent, PowerCellChangedEvent>(OnPowerCellChanged);
|
||||
SubscribeLocalEvent<BorgChassisComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
|
||||
SubscribeLocalEvent<BorgChassisComponent, ActivatableUIOpenAttemptEvent>(OnUIOpenAttempt);
|
||||
SubscribeLocalEvent<BorgChassisComponent, GetCharactedDeadIcEvent>(OnGetDeadIC);
|
||||
|
||||
SubscribeLocalEvent<BorgBrainComponent, MindAddedMessage>(OnBrainMindAdded);
|
||||
@@ -212,6 +214,13 @@ public sealed partial class BorgSystem : SharedBorgSystem
|
||||
UpdateUI(uid, component);
|
||||
}
|
||||
|
||||
private void OnUIOpenAttempt(EntityUid uid, BorgChassisComponent component, ActivatableUIOpenAttemptEvent args)
|
||||
{
|
||||
// borgs can't view their own ui
|
||||
if (args.User == uid)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnGetDeadIC(EntityUid uid, BorgChassisComponent component, ref GetCharactedDeadIcEvent args)
|
||||
{
|
||||
args.Dead = true;
|
||||
|
||||
@@ -28,9 +28,7 @@ public sealed class ImmovableRodRule : StationEventSystem<ImmovableRodRuleCompon
|
||||
|
||||
if (proto.TryGetComponent<ImmovableRodComponent>(out var rod) && proto.TryGetComponent<TimedDespawnComponent>(out var despawn))
|
||||
{
|
||||
if (!TryFindRandomTile(out _, out _, out _, out var targetCoords))
|
||||
return;
|
||||
|
||||
TryFindRandomTile(out _, out _, out _, out var targetCoords);
|
||||
var speed = RobustRandom.NextFloat(rod.MinSpeed, rod.MaxSpeed);
|
||||
var angle = RobustRandom.NextAngle();
|
||||
var direction = angle.ToVec();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Ensnaring;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.Cuffs;
|
||||
using Content.Shared.Cuffs.Components;
|
||||
using Content.Shared.Database;
|
||||
@@ -9,6 +10,7 @@ using Content.Shared.Ensnaring.Components;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Inventory.VirtualItem;
|
||||
@@ -27,6 +29,7 @@ namespace Content.Server.Strip
|
||||
{
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
[Dependency] private readonly EnsnareableSystem _ensnaringSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
|
||||
|
||||
[Dependency] private readonly SharedCuffableSystem _cuffableSystem = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||
@@ -43,6 +46,7 @@ namespace Content.Server.Strip
|
||||
|
||||
SubscribeLocalEvent<StrippableComponent, GetVerbsEvent<Verb>>(AddStripVerb);
|
||||
SubscribeLocalEvent<StrippableComponent, GetVerbsEvent<ExamineVerb>>(AddStripExamineVerb);
|
||||
SubscribeLocalEvent<StrippableComponent, ActivateInWorldEvent>(OnActivateInWorld);
|
||||
|
||||
// BUI
|
||||
SubscribeLocalEvent<StrippableComponent, StrippingSlotButtonPressed>(OnStripButtonPressed);
|
||||
@@ -65,7 +69,7 @@ namespace Content.Server.Strip
|
||||
{
|
||||
Text = Loc.GetString("strip-verb-get-data-text"),
|
||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/outfit.svg.192dpi.png")),
|
||||
Act = () => TryOpenStrippingUi(args.User, (uid, component), true),
|
||||
Act = () => StartOpeningStripper(args.User, (uid, component), true),
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
@@ -83,13 +87,37 @@ namespace Content.Server.Strip
|
||||
{
|
||||
Text = Loc.GetString("strip-verb-get-data-text"),
|
||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/outfit.svg.192dpi.png")),
|
||||
Act = () => TryOpenStrippingUi(args.User, (uid, component), true),
|
||||
Act = () => StartOpeningStripper(args.User, (uid, component), true),
|
||||
Category = VerbCategory.Examine,
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private void OnActivateInWorld(EntityUid uid, StrippableComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (args.Target == args.User)
|
||||
return;
|
||||
|
||||
if (!HasComp<ActorComponent>(args.User))
|
||||
return;
|
||||
|
||||
StartOpeningStripper(args.User, (uid, component));
|
||||
}
|
||||
|
||||
public override void StartOpeningStripper(EntityUid user, Entity<StrippableComponent> strippable, bool openInCombat = false)
|
||||
{
|
||||
base.StartOpeningStripper(user, strippable, openInCombat);
|
||||
|
||||
if (TryComp<CombatModeComponent>(user, out var mode) && mode.IsInCombatMode && !openInCombat)
|
||||
return;
|
||||
|
||||
if (HasComp<StrippingComponent>(user))
|
||||
{
|
||||
_userInterfaceSystem.OpenUi(strippable.Owner, StrippingUiKey.Key, user);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStripButtonPressed(Entity<StrippableComponent> strippable, ref StrippingSlotButtonPressed args)
|
||||
{
|
||||
if (args.Actor is not { Valid: true } user ||
|
||||
|
||||
@@ -187,10 +187,15 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||
if (session is { } pSession)
|
||||
{
|
||||
(targetCoordinates, targetLocalAngle) = _lag.GetCoordinatesAngle(target, pSession);
|
||||
return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range);
|
||||
}
|
||||
else
|
||||
{
|
||||
var xform = Transform(target);
|
||||
targetCoordinates = xform.Coordinates;
|
||||
targetLocalAngle = xform.LocalRotation;
|
||||
}
|
||||
|
||||
return Interaction.InRangeUnobstructed(user, target, range);
|
||||
return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range);
|
||||
}
|
||||
|
||||
protected override void DoDamageEffect(List<EntityUid> targets, EntityUid? user, TransformComponent targetXform)
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Wires;
|
||||
namespace Content.Server.Wires;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for activatable UIs that require the entity to have a panel in a certain state.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedWiresSystem))]
|
||||
[RegisterComponent]
|
||||
public sealed partial class ActivatableUIRequiresPanelComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// TRUE: the panel must be open to access the UI.
|
||||
/// FALSE: the panel must be closed to access the UI.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[DataField("requireOpen"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool RequireOpen = true;
|
||||
}
|
||||
@@ -4,23 +4,27 @@ using System.Threading;
|
||||
using Content.Server.Construction;
|
||||
using Content.Server.Construction.Components;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Tools.Components;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Wires;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using ActivatableUISystem = Content.Shared.UserInterface.ActivatableUISystem;
|
||||
|
||||
namespace Content.Server.Wires;
|
||||
|
||||
public sealed class WiresSystem : SharedWiresSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||
[Dependency] private readonly ActivatableUISystem _activatableUI = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||
@@ -48,6 +52,8 @@ public sealed class WiresSystem : SharedWiresSystem
|
||||
SubscribeLocalEvent<WiresComponent, TimedWireEvent>(OnTimedWire);
|
||||
SubscribeLocalEvent<WiresComponent, PowerChangedEvent>(OnWiresPowered);
|
||||
SubscribeLocalEvent<WiresComponent, WireDoAfterEvent>(OnDoAfter);
|
||||
SubscribeLocalEvent<ActivatableUIRequiresPanelComponent, ActivatableUIOpenAttemptEvent>(OnAttemptOpenActivatableUI);
|
||||
SubscribeLocalEvent<ActivatableUIRequiresPanelComponent, PanelChangedEvent>(OnActivatableUIPanelChanged);
|
||||
SubscribeLocalEvent<WiresPanelSecurityComponent, WiresPanelSecurityEvent>(SetWiresPanelSecurity);
|
||||
}
|
||||
|
||||
@@ -467,6 +473,23 @@ public sealed class WiresSystem : SharedWiresSystem
|
||||
_uiSystem.CloseUi(ent.Owner, WiresUiKey.Key);
|
||||
}
|
||||
|
||||
private void OnAttemptOpenActivatableUI(EntityUid uid, ActivatableUIRequiresPanelComponent component, ActivatableUIOpenAttemptEvent args)
|
||||
{
|
||||
if (args.Cancelled || !TryComp<WiresPanelComponent>(uid, out var wires))
|
||||
return;
|
||||
|
||||
if (component.RequireOpen != wires.Open)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnActivatableUIPanelChanged(EntityUid uid, ActivatableUIRequiresPanelComponent component, ref PanelChangedEvent args)
|
||||
{
|
||||
if (args.Open == component.RequireOpen)
|
||||
return;
|
||||
|
||||
_activatableUI.CloseAll(uid);
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, WiresComponent component, MapInitEvent args)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(component.LayoutId))
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using Content.Shared.Damage;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Shared.Zombies;
|
||||
namespace Content.Server.Zombies;
|
||||
|
||||
/// <summary>
|
||||
/// Temporary because diseases suck.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[RegisterComponent]
|
||||
public sealed partial class PendingZombieComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
@@ -39,7 +39,7 @@ public sealed class WizardRuleSystem : GameRuleSystem<WizardRuleComponent>
|
||||
|
||||
private void OnObjectivesTextGetInfo(Entity<WizardRuleComponent> ent, ref ObjectivesTextGetInfoEvent args)
|
||||
{
|
||||
args.Minds = ent.Comp.WizardMinds.Select(mindId => (mindId, Comp<MindComponent>(mindId).CharacterName ?? "?")).ToList();
|
||||
args.Minds = ent.Comp.WizardMinds;
|
||||
args.AgentName = Loc.GetString("wizard-round-end-agent-name");
|
||||
}
|
||||
|
||||
|
||||
@@ -508,7 +508,13 @@ public abstract class SharedActionsSystem : EntitySystem
|
||||
return distance <= action.Range;
|
||||
}
|
||||
|
||||
return _interactionSystem.InRangeAndAccessible(user, target, range: action.Range);
|
||||
if (_interactionSystem.InRangeUnobstructed(user, target, range: action.Range)
|
||||
&& _containerSystem.IsInSameOrParentContainer(user, target))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return _interactionSystem.CanAccessViaStorage(user, target);
|
||||
}
|
||||
|
||||
public bool ValidateWorldTarget(EntityUid user, EntityCoordinates coords, Entity<WorldTargetActionComponent> action)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Atmos.Components;
|
||||
|
||||
@@ -23,47 +24,55 @@ public sealed partial class GasTileOverlayComponent : Component
|
||||
public GameTick ForceTick { get; set; }
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class GasTileOverlayState(Dictionary<Vector2i, GasOverlayChunk> chunks) : ComponentState
|
||||
{
|
||||
public readonly Dictionary<Vector2i, GasOverlayChunk> Chunks = chunks;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class GasTileOverlayDeltaState(
|
||||
Dictionary<Vector2i, GasOverlayChunk> modifiedChunks,
|
||||
HashSet<Vector2i> allChunks)
|
||||
: ComponentState, IComponentDeltaState<GasTileOverlayState>
|
||||
public sealed class GasTileOverlayState : ComponentState, IComponentDeltaState
|
||||
{
|
||||
public readonly Dictionary<Vector2i, GasOverlayChunk> ModifiedChunks = modifiedChunks;
|
||||
public readonly HashSet<Vector2i> AllChunks = allChunks;
|
||||
public readonly Dictionary<Vector2i, GasOverlayChunk> Chunks;
|
||||
public bool FullState => AllChunks == null;
|
||||
|
||||
public void ApplyToFullState(GasTileOverlayState state)
|
||||
// required to infer deleted/missing chunks for delta states
|
||||
public HashSet<Vector2i>? AllChunks;
|
||||
|
||||
public GasTileOverlayState(Dictionary<Vector2i, GasOverlayChunk> chunks)
|
||||
{
|
||||
Chunks = chunks;
|
||||
}
|
||||
|
||||
public void ApplyToFullState(IComponentState fullState)
|
||||
{
|
||||
DebugTools.Assert(!FullState);
|
||||
var state = (GasTileOverlayState) fullState;
|
||||
DebugTools.Assert(state.FullState);
|
||||
|
||||
foreach (var key in state.Chunks.Keys)
|
||||
{
|
||||
if (!AllChunks.Contains(key))
|
||||
if (!AllChunks!.Contains(key))
|
||||
state.Chunks.Remove(key);
|
||||
}
|
||||
|
||||
foreach (var (chunk, data) in ModifiedChunks)
|
||||
foreach (var (chunk, data) in Chunks)
|
||||
{
|
||||
state.Chunks[chunk] = new(data);
|
||||
}
|
||||
}
|
||||
|
||||
public GasTileOverlayState CreateNewFullState(GasTileOverlayState state)
|
||||
public IComponentState CreateNewFullState(IComponentState fullState)
|
||||
{
|
||||
var chunks = new Dictionary<Vector2i, GasOverlayChunk>(AllChunks.Count);
|
||||
DebugTools.Assert(!FullState);
|
||||
var state = (GasTileOverlayState) fullState;
|
||||
DebugTools.Assert(state.FullState);
|
||||
|
||||
foreach (var (chunk, data) in ModifiedChunks)
|
||||
var chunks = new Dictionary<Vector2i, GasOverlayChunk>(state.Chunks.Count);
|
||||
|
||||
foreach (var (chunk, data) in Chunks)
|
||||
{
|
||||
chunks[chunk] = new(data);
|
||||
}
|
||||
|
||||
foreach (var (chunk, data) in state.Chunks)
|
||||
{
|
||||
if (AllChunks.Contains(chunk))
|
||||
if (AllChunks!.Contains(chunk))
|
||||
chunks.TryAdd(chunk, new(data));
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Content.Shared.Atmos.EntitySystems
|
||||
data[index] = chunk;
|
||||
}
|
||||
|
||||
args.State = new GasTileOverlayDeltaState(data, new(component.Chunks.Keys));
|
||||
args.State = new GasTileOverlayState(data) { AllChunks = new(component.Chunks.Keys) };
|
||||
}
|
||||
|
||||
public static Vector2i GetGasChunkIndices(Vector2i indices)
|
||||
|
||||
@@ -58,7 +58,7 @@ public abstract partial class SharedBuckleSystem
|
||||
return;
|
||||
|
||||
var strapPosition = Transform(strapUid).Coordinates;
|
||||
if (ev.NewPosition.EntityId.IsValid() && ev.NewPosition.InRange(EntityManager, _transform, strapPosition, strapComp.MaxBuckleDistance))
|
||||
if (ev.NewPosition.InRange(EntityManager, _transform, strapPosition, strapComp.MaxBuckleDistance))
|
||||
return;
|
||||
|
||||
TryUnbuckle(uid, uid, true, component);
|
||||
|
||||
@@ -5,6 +5,8 @@ using Content.Shared.StatusIcon;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
||||
namespace Content.Shared.Damage
|
||||
{
|
||||
@@ -16,7 +18,7 @@ namespace Content.Shared.Damage
|
||||
/// may also have resistances to certain damage types, defined via a <see cref="DamageModifierSetPrototype"/>.
|
||||
/// </remarks>
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent]
|
||||
[NetworkedComponent()]
|
||||
[Access(typeof(DamageableSystem), Other = AccessPermissions.ReadExecute)]
|
||||
public sealed partial class DamageableComponent : Component
|
||||
{
|
||||
@@ -24,8 +26,8 @@ namespace Content.Shared.Damage
|
||||
/// This <see cref="DamageContainerPrototype"/> specifies what damage types are supported by this component.
|
||||
/// If null, all damage types will be supported.
|
||||
/// </summary>
|
||||
[DataField("damageContainer")]
|
||||
public ProtoId<DamageContainerPrototype>? DamageContainerID;
|
||||
[DataField("damageContainer", customTypeSerializer: typeof(PrototypeIdSerializer<DamageContainerPrototype>))]
|
||||
public string? DamageContainerID;
|
||||
|
||||
/// <summary>
|
||||
/// This <see cref="DamageModifierSetPrototype"/> will be applied to any damage that is dealt to this container,
|
||||
@@ -35,8 +37,8 @@ namespace Content.Shared.Damage
|
||||
/// Though DamageModifierSets can be deserialized directly, we only want to use the prototype version here
|
||||
/// to reduce duplication.
|
||||
/// </remarks>
|
||||
[DataField("damageModifierSet")]
|
||||
public ProtoId<DamageModifierSetPrototype>? DamageModifierSetId;
|
||||
[DataField("damageModifierSet", customTypeSerializer: typeof(PrototypeIdSerializer<DamageModifierSetPrototype>))]
|
||||
public string? DamageModifierSetId;
|
||||
|
||||
/// <summary>
|
||||
/// All the damage information is stored in this <see cref="DamageSpecifier"/>.
|
||||
@@ -44,7 +46,7 @@ namespace Content.Shared.Damage
|
||||
/// <remarks>
|
||||
/// If this data-field is specified, this allows damageable components to be initialized with non-zero damage.
|
||||
/// </remarks>
|
||||
[DataField(readOnly: true)] //todo remove this readonly when implementing writing to damagespecifier
|
||||
[DataField("damage", readOnly: true)] //todo remove this readonly when implementing writing to damagespecifier
|
||||
public DamageSpecifier Damage = new();
|
||||
|
||||
/// <summary>
|
||||
@@ -62,8 +64,8 @@ namespace Content.Shared.Damage
|
||||
[ViewVariables]
|
||||
public FixedPoint2 TotalDamage;
|
||||
|
||||
[DataField("radiationDamageTypes")]
|
||||
public List<ProtoId<DamageTypePrototype>> RadiationDamageTypeIDs = new() { "Radiation" };
|
||||
[DataField("radiationDamageTypes", customTypeSerializer: typeof(PrototypeIdListSerializer<DamageTypePrototype>))]
|
||||
public List<string> RadiationDamageTypeIDs = new() { "Radiation" };
|
||||
|
||||
[DataField]
|
||||
public Dictionary<MobState, ProtoId<StatusIconPrototype>> HealthIcons = new()
|
||||
@@ -75,9 +77,6 @@ namespace Content.Shared.Damage
|
||||
|
||||
[DataField]
|
||||
public ProtoId<StatusIconPrototype> RottingIcon = "HealthIconRotting";
|
||||
|
||||
[DataField]
|
||||
public FixedPoint2? HealthBarThreshold;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
@@ -85,16 +84,13 @@ namespace Content.Shared.Damage
|
||||
{
|
||||
public readonly Dictionary<string, FixedPoint2> DamageDict;
|
||||
public readonly string? ModifierSetId;
|
||||
public readonly FixedPoint2? HealthBarThreshold;
|
||||
|
||||
public DamageableComponentState(
|
||||
Dictionary<string, FixedPoint2> damageDict,
|
||||
string? modifierSetId,
|
||||
FixedPoint2? healthBarThreshold)
|
||||
string? modifierSetId)
|
||||
{
|
||||
DamageDict = damageDict;
|
||||
ModifierSetId = modifierSetId;
|
||||
HealthBarThreshold = healthBarThreshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Inventory;
|
||||
@@ -239,12 +240,12 @@ namespace Content.Shared.Damage
|
||||
{
|
||||
if (_netMan.IsServer)
|
||||
{
|
||||
args.State = new DamageableComponentState(component.Damage.DamageDict, component.DamageModifierSetId, component.HealthBarThreshold);
|
||||
args.State = new DamageableComponentState(component.Damage.DamageDict, component.DamageModifierSetId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// avoid mispredicting damage on newly spawned entities.
|
||||
args.State = new DamageableComponentState(component.Damage.DamageDict.ShallowClone(), component.DamageModifierSetId, component.HealthBarThreshold);
|
||||
args.State = new DamageableComponentState(component.Damage.DamageDict.ShallowClone(), component.DamageModifierSetId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +279,6 @@ namespace Content.Shared.Damage
|
||||
}
|
||||
|
||||
component.DamageModifierSetId = state.ModifierSetId;
|
||||
component.HealthBarThreshold = state.HealthBarThreshold;
|
||||
|
||||
// Has the damage actually changed?
|
||||
DamageSpecifier newDamage = new() { DamageDict = new(state.DamageDict) };
|
||||
|
||||
@@ -62,37 +62,46 @@ namespace Content.Shared.Decals
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DecalGridState(Dictionary<Vector2i, DecalChunk> chunks) : ComponentState
|
||||
public sealed class DecalGridState : ComponentState, IComponentDeltaState
|
||||
{
|
||||
public Dictionary<Vector2i, DecalChunk> Chunks = chunks;
|
||||
}
|
||||
public Dictionary<Vector2i, DecalChunk> Chunks;
|
||||
public bool FullState => AllChunks == null;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DecalGridDeltaState(Dictionary<Vector2i, DecalChunk> modifiedChunks, HashSet<Vector2i> allChunks)
|
||||
: ComponentState, IComponentDeltaState<DecalGridState>
|
||||
{
|
||||
public Dictionary<Vector2i, DecalChunk> ModifiedChunks = modifiedChunks;
|
||||
public HashSet<Vector2i> AllChunks = allChunks;
|
||||
// required to infer deleted/missing chunks for delta states
|
||||
public HashSet<Vector2i>? AllChunks;
|
||||
|
||||
public void ApplyToFullState(DecalGridState state)
|
||||
public DecalGridState(Dictionary<Vector2i, DecalChunk> chunks)
|
||||
{
|
||||
Chunks = chunks;
|
||||
}
|
||||
|
||||
public void ApplyToFullState(IComponentState fullState)
|
||||
{
|
||||
DebugTools.Assert(!FullState);
|
||||
var state = (DecalGridState) fullState;
|
||||
DebugTools.Assert(state.FullState);
|
||||
|
||||
foreach (var key in state.Chunks.Keys)
|
||||
{
|
||||
if (!AllChunks!.Contains(key))
|
||||
state.Chunks.Remove(key);
|
||||
}
|
||||
|
||||
foreach (var (chunk, data) in ModifiedChunks)
|
||||
foreach (var (chunk, data) in Chunks)
|
||||
{
|
||||
state.Chunks[chunk] = new(data);
|
||||
}
|
||||
}
|
||||
|
||||
public DecalGridState CreateNewFullState(DecalGridState state)
|
||||
public IComponentState CreateNewFullState(IComponentState fullState)
|
||||
{
|
||||
DebugTools.Assert(!FullState);
|
||||
var state = (DecalGridState) fullState;
|
||||
DebugTools.Assert(state.FullState);
|
||||
|
||||
var chunks = new Dictionary<Vector2i, DecalChunk>(state.Chunks.Count);
|
||||
|
||||
foreach (var (chunk, data) in ModifiedChunks)
|
||||
foreach (var (chunk, data) in Chunks)
|
||||
{
|
||||
chunks[chunk] = new(data);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Content.Shared.Decals
|
||||
data[index] = chunk;
|
||||
}
|
||||
|
||||
args.State = new DecalGridDeltaState(data, new(component.ChunkCollection.ChunkCollection.Keys));
|
||||
args.State = new DecalGridState(data) { AllChunks = new(component.ChunkCollection.ChunkCollection.Keys) };
|
||||
}
|
||||
|
||||
private void OnGridInitialize(GridInitializeEvent msg)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Input;
|
||||
@@ -14,20 +15,16 @@ using Content.Shared.Inventory;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Pulling.Components;
|
||||
using Content.Shared.Movement.Pulling.Systems;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Storage;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Timing;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Wall;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Content.Shared._White.MeatyOre;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Storage;
|
||||
using Content.Shared.UserInterface;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Input;
|
||||
@@ -41,8 +38,6 @@ using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
#pragma warning disable 618
|
||||
|
||||
@@ -57,11 +52,12 @@ namespace Content.Shared.Interaction
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly ISharedAdminManager _adminManager = default!;
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _broadphase = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _sharedBroadphaseSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedVerbSystem _verbSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
@@ -70,18 +66,6 @@ namespace Content.Shared.Interaction
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
|
||||
|
||||
private EntityQuery<IgnoreUIRangeComponent> _ignoreUiRangeQuery;
|
||||
private EntityQuery<FixturesComponent> _fixtureQuery;
|
||||
private EntityQuery<ItemComponent> _itemQuery;
|
||||
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||
private EntityQuery<HandsComponent> _handsQuery;
|
||||
private EntityQuery<InteractionRelayComponent> _relayQuery;
|
||||
private EntityQuery<CombatModeComponent> _combatQuery;
|
||||
private EntityQuery<WallMountComponent> _wallMountQuery;
|
||||
private EntityQuery<UseDelayComponent> _delayQuery;
|
||||
private EntityQuery<ActivatableUIComponent> _uiQuery;
|
||||
|
||||
private const CollisionGroup InRangeUnobstructedMask = CollisionGroup.Impassable | CollisionGroup.InteractImpassable;
|
||||
|
||||
@@ -94,17 +78,6 @@ namespace Content.Shared.Interaction
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
_ignoreUiRangeQuery = GetEntityQuery<IgnoreUIRangeComponent>();
|
||||
_fixtureQuery = GetEntityQuery<FixturesComponent>();
|
||||
_itemQuery = GetEntityQuery<ItemComponent>();
|
||||
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
_handsQuery = GetEntityQuery<HandsComponent>();
|
||||
_relayQuery = GetEntityQuery<InteractionRelayComponent>();
|
||||
_combatQuery = GetEntityQuery<CombatModeComponent>();
|
||||
_wallMountQuery = GetEntityQuery<WallMountComponent>();
|
||||
_delayQuery = GetEntityQuery<UseDelayComponent>();
|
||||
_uiQuery = GetEntityQuery<ActivatableUIComponent>();
|
||||
|
||||
SubscribeLocalEvent<BoundUserInterfaceCheckRangeEvent>(HandleUserInterfaceRangeCheck);
|
||||
SubscribeLocalEvent<BoundUserInterfaceMessageAttempt>(OnBoundInterfaceInteractAttempt);
|
||||
|
||||
@@ -140,57 +113,34 @@ namespace Content.Shared.Interaction
|
||||
/// </summary>
|
||||
private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev)
|
||||
{
|
||||
_uiQuery.TryComp(ev.Target, out var uiComp);
|
||||
if (!_actionBlockerSystem.CanInteract(ev.Actor, ev.Target))
|
||||
{
|
||||
// We permit ghosts to open uis unless explicitly blocked
|
||||
if (ev.Message is not OpenBoundInterfaceMessage || !HasComp<GhostComponent>(ev.Actor) || uiComp?.BlockSpectators == true)
|
||||
{
|
||||
ev.Cancel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
var user = ev.Actor;
|
||||
|
||||
var range = _ui.GetUiRange(ev.Target, ev.UiKey);
|
||||
|
||||
// As long as range>0, the UI frame updates should have auto-closed the UI if it is out of range.
|
||||
DebugTools.Assert(range <= 0 || UiRangeCheck(ev.Actor, ev.Target, range));
|
||||
|
||||
if (range <= 0 && !IsAccessible(ev.Actor, ev.Target))
|
||||
if (!_actionBlockerSystem.CanInteract(user, ev.Target))
|
||||
{
|
||||
ev.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (uiComp == null)
|
||||
return;
|
||||
|
||||
if (uiComp.SingleUser && uiComp.CurrentSingleUser != ev.Actor)
|
||||
// Check if the bound entity is accessible. Note that we allow admins to ignore this restriction, so that
|
||||
// they can fiddle with UI's that people can't normally interact with (e.g., placing things directly into
|
||||
// other people's backpacks).
|
||||
if (!_containerSystem.IsInSameOrParentContainer(user, ev.Target)
|
||||
&& !CanAccessViaStorage(user, ev.Target)
|
||||
&& !_adminManager.HasAdminFlag(user, AdminFlags.Admin))
|
||||
{
|
||||
ev.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!uiComp.RequireHands)
|
||||
if (CompOrNull<IgnorBUIInteractionRangeComponent>(ev.Target) != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_handsQuery.TryComp(ev.Actor, out var hands) || hands.Hands.Count == 0)
|
||||
if (!InRangeUnobstructed(user, ev.Target))
|
||||
{
|
||||
ev.Cancel();
|
||||
}
|
||||
|
||||
private bool UiRangeCheck(Entity<TransformComponent?> user, Entity<TransformComponent?> target, float range)
|
||||
{
|
||||
if (!Resolve(target, ref target.Comp))
|
||||
return false;
|
||||
|
||||
if (user.Owner == target.Owner)
|
||||
return true;
|
||||
|
||||
// Fast check: if the user is the parent of the entity (e.g., holding it), we always assume that it is in range
|
||||
if (target.Comp.ParentUid == user.Owner)
|
||||
return true;
|
||||
|
||||
return InRangeAndAccessible(user, target, range) || _ignoreUiRangeQuery.HasComp(user);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -247,7 +197,10 @@ namespace Content.Shared.Interaction
|
||||
if (!InRangeUnobstructed(userEntity.Value, uid, popup: true))
|
||||
return false;
|
||||
|
||||
_pullSystem.TogglePull(uid, userEntity.Value);
|
||||
if (!TryComp(uid, out PullableComponent? pull))
|
||||
return false;
|
||||
|
||||
_pullSystem.TogglePull(uid, userEntity.Value, pull);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -323,7 +276,7 @@ namespace Content.Shared.Interaction
|
||||
public bool CombatModeCanHandInteract(EntityUid user, EntityUid? target)
|
||||
{
|
||||
// Always allow attack in these cases
|
||||
if (target == null || !_handsQuery.TryComp(user, out var hands) || hands.ActiveHand?.HeldEntity is not null)
|
||||
if (target == null || !TryComp<HandsComponent>(user, out var hands) || hands.ActiveHand?.HeldEntity is not null)
|
||||
return false;
|
||||
|
||||
// Only eat input if:
|
||||
@@ -331,7 +284,7 @@ namespace Content.Shared.Interaction
|
||||
// - Target doesn't cancel should-interact event
|
||||
// This is intended to allow items to be picked up in combat mode,
|
||||
// but to also allow items to force attacks anyway (like mobs which are items, e.g. mice)
|
||||
if (!_itemQuery.HasComp(target))
|
||||
if (!HasComp<ItemComponent>(target))
|
||||
return false;
|
||||
|
||||
var combatEv = new CombatModeShouldHandInteractEvent();
|
||||
@@ -361,7 +314,7 @@ namespace Content.Shared.Interaction
|
||||
bool checkAccess = true,
|
||||
bool checkCanUse = true)
|
||||
{
|
||||
if (_relayQuery.TryComp(user, out var relay) && relay.RelayEntity is not null)
|
||||
if (TryComp<InteractionRelayComponent>(user, out var relay) && relay.RelayEntity is not null)
|
||||
{
|
||||
// TODO this needs to be handled better. This probably bypasses many complex can-interact checks in weird roundabout ways.
|
||||
if (_actionBlockerSystem.CanInteract(user, target))
|
||||
@@ -375,7 +328,7 @@ namespace Content.Shared.Interaction
|
||||
if (target != null && Deleted(target.Value))
|
||||
return;
|
||||
|
||||
if (!altInteract && _combatQuery.TryComp(user, out var combatMode) && combatMode.IsInCombatMode)
|
||||
if (!altInteract && TryComp<CombatModeComponent>(user, out var combatMode) && combatMode.IsInCombatMode)
|
||||
{
|
||||
if (!CombatModeCanHandInteract(user, target))
|
||||
return;
|
||||
@@ -397,7 +350,10 @@ namespace Content.Shared.Interaction
|
||||
|
||||
// Check if interacted entity is in the same container, the direct child, or direct parent of the user.
|
||||
// Also checks if the item is accessible via some storage UI (e.g., open backpack)
|
||||
if (checkAccess && target != null && !IsAccessible(user, target.Value))
|
||||
if (checkAccess
|
||||
&& target != null
|
||||
&& !_containerSystem.IsInSameOrParentContainer(user, target.Value)
|
||||
&& !CanAccessViaStorage(user, target.Value))
|
||||
return;
|
||||
|
||||
var inRangeUnobstructed = target == null
|
||||
@@ -405,7 +361,7 @@ namespace Content.Shared.Interaction
|
||||
: !checkAccess || InRangeUnobstructed(user, target.Value); // permits interactions with wall mounted entities
|
||||
|
||||
// Does the user have hands?
|
||||
if (!_handsQuery.TryComp(user, out var hands) || hands.ActiveHand == null)
|
||||
if (!TryComp<HandsComponent>(user, out var hands) || hands.ActiveHand == null)
|
||||
{
|
||||
var ev = new InteractNoHandEvent(user, target, coordinates);
|
||||
RaiseLocalEvent(user, ev);
|
||||
@@ -545,7 +501,7 @@ namespace Content.Shared.Interaction
|
||||
|
||||
predicate ??= _ => false;
|
||||
var ray = new CollisionRay(origin.Position, dir.Normalized(), collisionMask);
|
||||
var rayResults = _broadphase.IntersectRayWithPredicate(origin.MapId, ray, dir.Length(), predicate.Invoke, false).ToList();
|
||||
var rayResults = _sharedBroadphaseSystem.IntersectRayWithPredicate(origin.MapId, ray, dir.Length(), predicate.Invoke, false).ToList();
|
||||
|
||||
if (rayResults.Count == 0)
|
||||
return dir.Length();
|
||||
@@ -608,29 +564,23 @@ namespace Content.Shared.Interaction
|
||||
}
|
||||
|
||||
var ray = new CollisionRay(origin.Position, dir.Normalized(), (int) collisionMask);
|
||||
var rayResults = _broadphase.IntersectRayWithPredicate(origin.MapId, ray, length, predicate.Invoke, false).ToList();
|
||||
var rayResults = _sharedBroadphaseSystem.IntersectRayWithPredicate(origin.MapId, ray, length, predicate.Invoke, false).ToList();
|
||||
|
||||
return rayResults.Count == 0;
|
||||
}
|
||||
|
||||
public bool InRangeUnobstructed(
|
||||
Entity<TransformComponent?> origin,
|
||||
Entity<TransformComponent?> other,
|
||||
EntityUid origin,
|
||||
EntityUid other,
|
||||
float range = InteractionRange,
|
||||
CollisionGroup collisionMask = InRangeUnobstructedMask,
|
||||
Ignored? predicate = null,
|
||||
bool popup = false)
|
||||
{
|
||||
if (!Resolve(other, ref other.Comp))
|
||||
if (!TryComp(other, out TransformComponent? otherXform))
|
||||
return false;
|
||||
|
||||
return InRangeUnobstructed(origin,
|
||||
other,
|
||||
other.Comp.Coordinates,
|
||||
other.Comp.LocalRotation,
|
||||
range,
|
||||
collisionMask,
|
||||
predicate,
|
||||
return InRangeUnobstructed(origin, other, otherXform.Coordinates, otherXform.LocalRotation, range, collisionMask, predicate,
|
||||
popup);
|
||||
}
|
||||
|
||||
@@ -662,8 +612,8 @@ namespace Content.Shared.Interaction
|
||||
/// True if the two points are within a given range without being obstructed.
|
||||
/// </returns>
|
||||
public bool InRangeUnobstructed(
|
||||
Entity<TransformComponent?> origin,
|
||||
Entity<TransformComponent?> other,
|
||||
EntityUid origin,
|
||||
EntityUid other,
|
||||
EntityCoordinates otherCoordinates,
|
||||
Angle otherAngle,
|
||||
float range = InteractionRange,
|
||||
@@ -671,10 +621,10 @@ namespace Content.Shared.Interaction
|
||||
Ignored? predicate = null,
|
||||
bool popup = false)
|
||||
{
|
||||
Ignored combinedPredicate = e => e == origin.Owner || (predicate?.Invoke(e) ?? false);
|
||||
Ignored combinedPredicate = e => e == origin || (predicate?.Invoke(e) ?? false);
|
||||
var inRange = true;
|
||||
MapCoordinates originPos = default;
|
||||
var targetPos = _transform.ToMapCoordinates(otherCoordinates);
|
||||
var targetPos = otherCoordinates.ToMap(EntityManager, _transform);
|
||||
Angle targetRot = default;
|
||||
|
||||
// So essentially:
|
||||
@@ -684,30 +634,23 @@ namespace Content.Shared.Interaction
|
||||
// Alternatively we could check centre distances first though
|
||||
// that means we wouldn't be able to easily check overlap interactions.
|
||||
if (range > 0f &&
|
||||
_fixtureQuery.TryComp(origin, out var fixtureA) &&
|
||||
TryComp<FixturesComponent>(origin, out var fixtureA) &&
|
||||
// These fixture counts are stuff that has the component but no fixtures for <reasons> (e.g. buttons).
|
||||
// At least until they get removed.
|
||||
fixtureA.FixtureCount > 0 &&
|
||||
_fixtureQuery.TryComp(other, out var fixtureB) &&
|
||||
TryComp<FixturesComponent>(other, out var fixtureB) &&
|
||||
fixtureB.FixtureCount > 0 &&
|
||||
Resolve(origin, ref origin.Comp))
|
||||
TryComp(origin, out TransformComponent? xformA))
|
||||
{
|
||||
var (worldPosA, worldRotA) = origin.Comp.GetWorldPositionRotation();
|
||||
var (worldPosA, worldRotA) = xformA.GetWorldPositionRotation();
|
||||
var xfA = new Transform(worldPosA, worldRotA);
|
||||
var parentRotB = _transform.GetWorldRotation(otherCoordinates.EntityId);
|
||||
var xfB = new Transform(targetPos.Position, parentRotB + otherAngle);
|
||||
|
||||
// Different map or the likes.
|
||||
if (!_broadphase.TryGetNearest(
|
||||
origin,
|
||||
other,
|
||||
out _,
|
||||
out _,
|
||||
out var distance,
|
||||
xfA,
|
||||
xfB,
|
||||
fixtureA,
|
||||
fixtureB))
|
||||
if (!_sharedBroadphaseSystem.TryGetNearest(origin, other,
|
||||
out _, out _, out var distance,
|
||||
xfA, xfB, fixtureA, fixtureB))
|
||||
{
|
||||
inRange = false;
|
||||
}
|
||||
@@ -729,15 +672,15 @@ namespace Content.Shared.Interaction
|
||||
else
|
||||
{
|
||||
// We'll still do the raycast from the centres but we'll bump the range as we know they're in range.
|
||||
originPos = _transform.GetMapCoordinates(origin, xform: origin.Comp);
|
||||
originPos = _transform.GetMapCoordinates(origin, xform: xformA);
|
||||
range = (originPos.Position - targetPos.Position).Length();
|
||||
}
|
||||
}
|
||||
// No fixtures, e.g. wallmounts.
|
||||
else
|
||||
{
|
||||
originPos = _transform.GetMapCoordinates(origin, origin);
|
||||
var otherParent = (other.Comp ?? Transform(other)).ParentUid;
|
||||
originPos = _transform.GetMapCoordinates(origin);
|
||||
var otherParent = Transform(other).ParentUid;
|
||||
targetRot = otherParent.IsValid() ? Transform(otherParent).LocalRotation + otherAngle : otherAngle;
|
||||
}
|
||||
|
||||
@@ -788,13 +731,13 @@ namespace Content.Shared.Interaction
|
||||
{
|
||||
HashSet<EntityUid> ignored = new();
|
||||
|
||||
if (_itemQuery.HasComp(target) && _physicsQuery.TryComp(target, out var physics) && physics.CanCollide)
|
||||
if (HasComp<ItemComponent>(target) && TryComp(target, out PhysicsComponent? physics) && physics.CanCollide)
|
||||
{
|
||||
// If the target is an item, we ignore any colliding entities. Currently done so that if items get stuck
|
||||
// inside of walls, users can still pick them up.
|
||||
ignored.UnionWith(_broadphase.GetEntitiesIntersectingBody(target, (int) collisionMask, false, physics));
|
||||
ignored.UnionWith(_sharedBroadphaseSystem.GetEntitiesIntersectingBody(target, (int) collisionMask, false, physics));
|
||||
}
|
||||
else if (_wallMountQuery.TryComp(target, out var wallMount))
|
||||
else if (TryComp(target, out WallMountComponent? wallMount))
|
||||
{
|
||||
// wall-mount exemptions may be restricted to a specific angle range.da
|
||||
|
||||
@@ -812,7 +755,13 @@ namespace Content.Shared.Interaction
|
||||
ignored.UnionWith(grid.GetAnchoredEntities(targetCoords));
|
||||
}
|
||||
|
||||
Ignored combinedPredicate = e => e == target || (predicate?.Invoke(e) ?? false) || ignored.Contains(e);
|
||||
Ignored combinedPredicate = e =>
|
||||
{
|
||||
return e == target
|
||||
|| (predicate?.Invoke(e) ?? false)
|
||||
|| ignored.Contains(e);
|
||||
};
|
||||
|
||||
return combinedPredicate;
|
||||
}
|
||||
|
||||
@@ -1009,8 +958,10 @@ namespace Content.Shared.Interaction
|
||||
bool checkUseDelay = true,
|
||||
bool checkAccess = true)
|
||||
{
|
||||
_delayQuery.TryComp(used, out var delayComponent);
|
||||
if (checkUseDelay && delayComponent != null && _useDelay.IsDelayed((used, delayComponent)))
|
||||
UseDelayComponent? delayComponent = null;
|
||||
if (checkUseDelay
|
||||
&& TryComp(used, out delayComponent)
|
||||
&& _useDelay.IsDelayed((used, delayComponent)))
|
||||
return false;
|
||||
|
||||
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used))
|
||||
@@ -1021,11 +972,11 @@ namespace Content.Shared.Interaction
|
||||
|
||||
// Check if interacted entity is in the same container, the direct child, or direct parent of the user.
|
||||
// This is bypassed IF the interaction happened through an item slot (e.g., backpack UI)
|
||||
if (checkAccess && !IsAccessible(user, used))
|
||||
if (checkAccess && !_containerSystem.IsInSameOrParentContainer(user, used) && !CanAccessViaStorage(user, used))
|
||||
return false;
|
||||
|
||||
// Does the user have hands?
|
||||
if (!_handsQuery.HasComp(user))
|
||||
if (!HasComp<HandsComponent>(user))
|
||||
return false;
|
||||
|
||||
var activateMsg = new ActivateInWorldEvent(user, used);
|
||||
@@ -1035,9 +986,7 @@ namespace Content.Shared.Interaction
|
||||
|
||||
DoContactInteraction(user, used, activateMsg);
|
||||
// Still need to call this even without checkUseDelay in case this gets relayed from Activate.
|
||||
if (delayComponent != null)
|
||||
_useDelay.TryResetDelay(used, component: delayComponent);
|
||||
|
||||
_useDelay.TryResetDelay(used, component: delayComponent);
|
||||
if (!activateMsg.WasLogged)
|
||||
_adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}");
|
||||
return true;
|
||||
@@ -1058,8 +1007,11 @@ namespace Content.Shared.Interaction
|
||||
bool checkCanInteract = true,
|
||||
bool checkUseDelay = true)
|
||||
{
|
||||
_delayQuery.TryComp(used, out var delayComponent);
|
||||
if (checkUseDelay && delayComponent != null && _useDelay.IsDelayed((used, delayComponent)))
|
||||
UseDelayComponent? delayComponent = null;
|
||||
|
||||
if (checkUseDelay
|
||||
&& TryComp(used, out delayComponent)
|
||||
&& _useDelay.IsDelayed((used, delayComponent)))
|
||||
return true; // if the item is on cooldown, we consider this handled.
|
||||
|
||||
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used))
|
||||
@@ -1121,60 +1073,11 @@ namespace Content.Shared.Interaction
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Check if a user can access a target (stored in the same containers) and is in range without obstructions.
|
||||
/// </summary>
|
||||
public bool InRangeAndAccessible(
|
||||
Entity<TransformComponent?> user,
|
||||
Entity<TransformComponent?> target,
|
||||
float range = InteractionRange,
|
||||
CollisionGroup collisionMask = InRangeUnobstructedMask,
|
||||
Ignored? predicate = null)
|
||||
{
|
||||
if (user == target)
|
||||
return true;
|
||||
|
||||
if (!Resolve(user, ref user.Comp))
|
||||
return false;
|
||||
|
||||
if (!Resolve(target, ref target.Comp))
|
||||
return false;
|
||||
|
||||
return IsAccessible(user, target) && InRangeUnobstructed(user, target, range, collisionMask, predicate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a user can access a target or if they are stored in different containers.
|
||||
/// </summary>
|
||||
public bool IsAccessible(Entity<TransformComponent?> user, Entity<TransformComponent?> target)
|
||||
{
|
||||
if (_containerSystem.IsInSameOrParentContainer(user, target, out _, out var container))
|
||||
return true;
|
||||
|
||||
return container != null && CanAccessViaStorage(user, target, container);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If a target is in range, but not in the same container as the user, it may be inside of a backpack. This
|
||||
/// checks if the user can access the item in these situations.
|
||||
/// </summary>
|
||||
public bool CanAccessViaStorage(EntityUid user, EntityUid target)
|
||||
{
|
||||
if (!_containerSystem.TryGetContainingContainer(target, out var container))
|
||||
return false;
|
||||
|
||||
return CanAccessViaStorage(user, target, container);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CanAccessViaStorage(Robust.Shared.GameObjects.EntityUid,Robust.Shared.GameObjects.EntityUid)"/>
|
||||
public bool CanAccessViaStorage(EntityUid user, EntityUid target, BaseContainer container)
|
||||
{
|
||||
if (StorageComponent.ContainerId != container.ID)
|
||||
return false;
|
||||
|
||||
// we don't check if the user can access the storage entity itself. This should be handed by the UI system.
|
||||
return _ui.IsUiOpen(container.Owner, StorageComponent.StorageUiKey.Key, user);
|
||||
}
|
||||
public abstract bool CanAccessViaStorage(EntityUid user, EntityUid target);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an entity currently equipped by another player is accessible to some user. This shouldn't
|
||||
@@ -1255,15 +1158,19 @@ namespace Content.Shared.Interaction
|
||||
RaiseLocalEvent(uidB.Value, new ContactInteractionEvent(uidA));
|
||||
}
|
||||
|
||||
|
||||
private void HandleUserInterfaceRangeCheck(ref BoundUserInterfaceCheckRangeEvent ev)
|
||||
{
|
||||
if (ev.Result == BoundUserInterfaceRangeResult.Fail)
|
||||
return;
|
||||
|
||||
ev.Result = UiRangeCheck(ev.Actor!, ev.Target, ev.Data.InteractionRange)
|
||||
? BoundUserInterfaceRangeResult.Pass
|
||||
: BoundUserInterfaceRangeResult.Fail;
|
||||
if (InRangeUnobstructed(ev.Actor, ev.Target, ev.Data.InteractionRange))
|
||||
{
|
||||
ev.Result = BoundUserInterfaceRangeResult.Pass;
|
||||
}
|
||||
else
|
||||
{
|
||||
ev.Result = BoundUserInterfaceRangeResult.Fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -210,7 +210,11 @@ public abstract partial class InventorySystem
|
||||
return false;
|
||||
|
||||
// Can the actor reach the item?
|
||||
if (_interactionSystem.InRangeAndAccessible(actor, itemUid))
|
||||
if (_interactionSystem.InRangeUnobstructed(actor, itemUid) && _containerSystem.IsInSameOrParentContainer(actor, itemUid))
|
||||
return true;
|
||||
|
||||
// Is the item in an open storage UI, i.e., is the user quick-equipping from an open backpack?
|
||||
if (_interactionSystem.CanAccessViaStorage(actor, itemUid))
|
||||
return true;
|
||||
|
||||
// Is the actor currently stripping the target? Here we could check if the actor has the stripping UI open, but
|
||||
|
||||
@@ -9,7 +9,6 @@ using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Wires;
|
||||
using JetBrains.Annotations;
|
||||
@@ -25,7 +24,6 @@ namespace Content.Shared.Lock;
|
||||
public sealed class LockSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
|
||||
[Dependency] private readonly ActivatableUISystem _activatableUI = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _sharedPopupSystem = default!;
|
||||
@@ -48,9 +46,6 @@ public sealed class LockSystem : EntitySystem
|
||||
SubscribeLocalEvent<LockedWiresPanelComponent, LockToggleAttemptEvent>(OnLockToggleAttempt);
|
||||
SubscribeLocalEvent<LockedWiresPanelComponent, AttemptChangePanelEvent>(OnAttemptChangePanel);
|
||||
SubscribeLocalEvent<LockedAnchorableComponent, UnanchorAttemptEvent>(OnUnanchorAttempt);
|
||||
|
||||
SubscribeLocalEvent<ActivatableUIRequiresLockComponent, ActivatableUIOpenAttemptEvent>(OnUIOpenAttempt);
|
||||
SubscribeLocalEvent<ActivatableUIRequiresLockComponent, LockToggledEvent>(LockToggled);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, LockComponent lockComp, ComponentStartup args)
|
||||
@@ -352,25 +347,4 @@ public sealed class LockSystem : EntitySystem
|
||||
args.User);
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnUIOpenAttempt(EntityUid uid, ActivatableUIRequiresLockComponent component, ActivatableUIOpenAttemptEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (TryComp<LockComponent>(uid, out var lockComp) && lockComp.Locked != component.RequireLocked)
|
||||
{
|
||||
args.Cancel();
|
||||
if (lockComp.Locked)
|
||||
_sharedPopupSystem.PopupEntity(Loc.GetString("entity-storage-component-locked-message"), uid, args.User);
|
||||
}
|
||||
}
|
||||
|
||||
private void LockToggled(EntityUid uid, ActivatableUIRequiresLockComponent component, LockToggledEvent args)
|
||||
{
|
||||
if (!TryComp<LockComponent>(uid, out var lockComp) || lockComp.Locked == component.RequireLocked)
|
||||
return;
|
||||
|
||||
_activatableUI.CloseAll(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ using Content.Shared.DoAfter;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Content.Shared.Interaction;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.MagicMirror;
|
||||
|
||||
@@ -18,13 +17,10 @@ public abstract class SharedMagicMirrorSystem : EntitySystem
|
||||
|
||||
private void OnMirrorRangeCheck(EntityUid uid, MagicMirrorComponent component, ref BoundUserInterfaceCheckRangeEvent args)
|
||||
{
|
||||
if (args.Result == BoundUserInterfaceRangeResult.Fail)
|
||||
return;
|
||||
|
||||
DebugTools.Assert(component.Target != null && Exists(component.Target));
|
||||
|
||||
if (!_interaction.InRangeUnobstructed(uid, component.Target.Value))
|
||||
if (!Exists(component.Target) || !_interaction.InRangeUnobstructed(uid, component.Target.Value))
|
||||
{
|
||||
args.Result = BoundUserInterfaceRangeResult.Fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -344,17 +344,14 @@ public sealed class PullingSystem : EntitySystem
|
||||
return !startPull.Cancelled && !getPulled.Cancelled;
|
||||
}
|
||||
|
||||
public bool TogglePull(Entity<PullableComponent?> pullable, EntityUid pullerUid)
|
||||
public bool TogglePull(EntityUid pullableUid, EntityUid pullerUid, PullableComponent pullable)
|
||||
{
|
||||
if (!Resolve(pullable, ref pullable.Comp, false))
|
||||
return false;
|
||||
|
||||
if (pullable.Comp.Puller == pullerUid)
|
||||
if (pullable.Puller == pullerUid)
|
||||
{
|
||||
return TryStopPull(pullable, pullable.Comp);
|
||||
return TryStopPull(pullableUid, pullable);
|
||||
}
|
||||
|
||||
return TryStartPull(pullerUid, pullable, pullableComp: pullable);
|
||||
return TryStartPull(pullerUid, pullableUid, pullableComp: pullable);
|
||||
}
|
||||
|
||||
public bool TogglePull(EntityUid pullerUid, PullerComponent puller)
|
||||
@@ -362,7 +359,7 @@ public sealed class PullingSystem : EntitySystem
|
||||
if (!TryComp<PullableComponent>(puller.Pulling, out var pullable))
|
||||
return false;
|
||||
|
||||
return TogglePull((puller.Pulling.Value, pullable), pullerUid);
|
||||
return TogglePull(puller.Pulling.Value, pullerUid, pullable);
|
||||
}
|
||||
|
||||
public bool TryStartPull(EntityUid pullerUid, EntityUid pullableUid,
|
||||
|
||||
@@ -96,7 +96,7 @@ public abstract class SharedNavMapSystem : EntitySystem
|
||||
chunks.Add(origin, chunk.TileData);
|
||||
}
|
||||
|
||||
args.State = new NavMapState(chunks, component.Beacons);
|
||||
args.State = new NavMapComponentState(chunks, component.Beacons);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -109,7 +109,12 @@ public abstract class SharedNavMapSystem : EntitySystem
|
||||
chunks.Add(origin, chunk.TileData);
|
||||
}
|
||||
|
||||
args.State = new NavMapDeltaState(chunks, component.Beacons, new(component.Chunks.Keys));
|
||||
args.State = new NavMapComponentState(chunks, component.Beacons)
|
||||
{
|
||||
// TODO NAVMAP cache a single AllChunks hashset in the component.
|
||||
// Or maybe just only send them if a chunk gets removed.
|
||||
AllChunks = new(component.Chunks.Keys),
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -117,35 +122,32 @@ public abstract class SharedNavMapSystem : EntitySystem
|
||||
#region: System messages
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
protected sealed class NavMapState(
|
||||
protected sealed class NavMapComponentState(
|
||||
Dictionary<Vector2i, int[]> chunks,
|
||||
Dictionary<NetEntity, NavMapBeacon> beacons)
|
||||
: ComponentState
|
||||
: ComponentState, IComponentDeltaState
|
||||
{
|
||||
public Dictionary<Vector2i, int[]> Chunks = chunks;
|
||||
public Dictionary<NetEntity, NavMapBeacon> Beacons = beacons;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
protected sealed class NavMapDeltaState(
|
||||
Dictionary<Vector2i, int[]> modifiedChunks,
|
||||
Dictionary<NetEntity, NavMapBeacon> beacons,
|
||||
HashSet<Vector2i> allChunks)
|
||||
: ComponentState, IComponentDeltaState<NavMapState>
|
||||
{
|
||||
public Dictionary<Vector2i, int[]> ModifiedChunks = modifiedChunks;
|
||||
public Dictionary<NetEntity, NavMapBeacon> Beacons = beacons;
|
||||
public HashSet<Vector2i> AllChunks = allChunks;
|
||||
// Required to infer deleted/missing chunks for delta states
|
||||
public HashSet<Vector2i>? AllChunks;
|
||||
|
||||
public void ApplyToFullState(NavMapState state)
|
||||
public bool FullState => AllChunks == null;
|
||||
|
||||
public void ApplyToFullState(IComponentState fullState)
|
||||
{
|
||||
DebugTools.Assert(!FullState);
|
||||
var state = (NavMapComponentState) fullState;
|
||||
DebugTools.Assert(state.FullState);
|
||||
|
||||
foreach (var key in state.Chunks.Keys)
|
||||
{
|
||||
if (!AllChunks!.Contains(key))
|
||||
state.Chunks.Remove(key);
|
||||
}
|
||||
|
||||
foreach (var (index, data) in ModifiedChunks)
|
||||
foreach (var (index, data) in Chunks)
|
||||
{
|
||||
if (!state.Chunks.TryGetValue(index, out var stateValue))
|
||||
state.Chunks[index] = stateValue = new int[data.Length];
|
||||
@@ -160,8 +162,12 @@ public abstract class SharedNavMapSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
public NavMapState CreateNewFullState(NavMapState state)
|
||||
public IComponentState CreateNewFullState(IComponentState fullState)
|
||||
{
|
||||
DebugTools.Assert(!FullState);
|
||||
var state = (NavMapComponentState) fullState;
|
||||
DebugTools.Assert(state.FullState);
|
||||
|
||||
var chunks = new Dictionary<Vector2i, int[]>(state.Chunks.Count);
|
||||
foreach (var (index, data) in state.Chunks)
|
||||
{
|
||||
@@ -170,13 +176,13 @@ public abstract class SharedNavMapSystem : EntitySystem
|
||||
|
||||
var newData = chunks[index] = new int[ArraySize];
|
||||
|
||||
if (ModifiedChunks.TryGetValue(index, out var updatedData))
|
||||
if (Chunks.TryGetValue(index, out var updatedData))
|
||||
Array.Copy(newData, updatedData, ArraySize);
|
||||
else
|
||||
Array.Copy(newData, data, ArraySize);
|
||||
}
|
||||
|
||||
return new NavMapState(chunks, new(Beacons));
|
||||
return new NavMapComponentState(chunks, new(Beacons));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,19 +74,18 @@ public sealed class RoleLoadout
|
||||
{
|
||||
var loadout = loadouts[i];
|
||||
|
||||
// Old prototype or otherwise invalid.
|
||||
if (!protoManager.TryIndex(loadout.Prototype, out var loadoutProto))
|
||||
{
|
||||
loadouts.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Malicious client maybe, check the group even has it.
|
||||
if (!groupProto.Loadouts.Contains(loadout.Prototype))
|
||||
{
|
||||
loadouts.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
// Похуй FIXME
|
||||
//if (!IsValid(profile, session, loadout.Prototype, collection, out _))
|
||||
// {
|
||||
// loadouts.RemoveAt(i);
|
||||
// continue;
|
||||
// }
|
||||
|
||||
Apply(loadoutProto);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.PowerCell.Components;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Wires;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -38,8 +37,6 @@ public abstract partial class SharedBorgSystem : EntitySystem
|
||||
SubscribeLocalEvent<BorgChassisComponent, EntInsertedIntoContainerMessage>(OnInserted);
|
||||
SubscribeLocalEvent<BorgChassisComponent, EntRemovedFromContainerMessage>(OnRemoved);
|
||||
SubscribeLocalEvent<BorgChassisComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeedModifiers);
|
||||
SubscribeLocalEvent<BorgChassisComponent, ActivatableUIOpenAttemptEvent>(OnUIOpenAttempt);
|
||||
|
||||
|
||||
//Honk
|
||||
SubscribeLocalEvent<SharedTTSComponent, ComponentInit>(RandomTTS);
|
||||
@@ -110,13 +107,6 @@ public abstract partial class SharedBorgSystem : EntitySystem
|
||||
component.ModuleContainer = Container.EnsureContainer<Container>(uid, component.ModuleContainerId, containerManager);
|
||||
}
|
||||
|
||||
private void OnUIOpenAttempt(EntityUid uid, BorgChassisComponent component, ActivatableUIOpenAttemptEvent args)
|
||||
{
|
||||
// borgs can't view their own ui
|
||||
if (args.User == uid)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
protected virtual void OnInserted(EntityUid uid, BorgChassisComponent component, EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ using Content.Shared.Stacks;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Content.Shared.Timing;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
@@ -157,9 +156,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
Grid = new List<Box2i>(component.Grid),
|
||||
MaxItemSize = component.MaxItemSize,
|
||||
StoredItems = storedItems,
|
||||
SavedLocations = component.SavedLocations,
|
||||
Whitelist = component.Whitelist,
|
||||
Blacklist = component.Blacklist
|
||||
SavedLocations = component.SavedLocations
|
||||
};
|
||||
}
|
||||
|
||||
@@ -171,8 +168,6 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
component.Grid.Clear();
|
||||
component.Grid.AddRange(state.Grid);
|
||||
component.MaxItemSize = state.MaxItemSize;
|
||||
component.Whitelist = state.Whitelist;
|
||||
component.Blacklist = state.Blacklist;
|
||||
|
||||
component.StoredItems.Clear();
|
||||
|
||||
@@ -1105,7 +1100,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
/// <returns>true if inserted, false otherwise</returns>
|
||||
public bool PlayerInsertEntityInWorld(Entity<StorageComponent?> uid, EntityUid player, EntityUid toInsert)
|
||||
{
|
||||
if (!Resolve(uid, ref uid.Comp) || !_interactionSystem.InRangeUnobstructed(player, uid.Owner))
|
||||
if (!Resolve(uid, ref uid.Comp) || !_interactionSystem.InRangeUnobstructed(player, uid))
|
||||
return false;
|
||||
|
||||
if (!Insert(uid, toInsert, out _, user: player, uid.Comp))
|
||||
@@ -1506,9 +1501,5 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
public List<Box2i> Grid = new();
|
||||
|
||||
public ProtoId<ItemSizePrototype>? MaxItemSize;
|
||||
|
||||
public EntityWhitelist? Whitelist;
|
||||
|
||||
public EntityWhitelist? Blacklist;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,17 @@
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.DragDrop;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Strip.Components;
|
||||
|
||||
namespace Content.Shared.Strip;
|
||||
|
||||
public abstract class SharedStrippableSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<StrippingComponent, CanDropTargetEvent>(OnCanDropOn);
|
||||
SubscribeLocalEvent<StrippableComponent, CanDropDraggedEvent>(OnCanDrop);
|
||||
SubscribeLocalEvent<StrippableComponent, DragDropDraggedEvent>(OnDragDrop);
|
||||
SubscribeLocalEvent<StrippableComponent, ActivateInWorldEvent>(OnActivateInWorld);
|
||||
}
|
||||
|
||||
private void OnActivateInWorld(EntityUid uid, StrippableComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (args.Handled || args.Target == args.User)
|
||||
return;
|
||||
|
||||
if (TryOpenStrippingUi(args.User, (uid, component)))
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
public (TimeSpan Time, bool Stealth) GetStripTimeModifiers(EntityUid user, EntityUid target, TimeSpan initialTime)
|
||||
@@ -43,20 +29,13 @@ public abstract class SharedStrippableSystem : EntitySystem
|
||||
if (args.Handled || args.Target != args.User)
|
||||
return;
|
||||
|
||||
if (TryOpenStrippingUi(args.User, (uid, component)))
|
||||
args.Handled = true;
|
||||
StartOpeningStripper(args.User, (uid, component));
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
public bool TryOpenStrippingUi(EntityUid user, Entity<StrippableComponent> target, bool openInCombat = false)
|
||||
public virtual void StartOpeningStripper(EntityUid user, Entity<StrippableComponent> component, bool openInCombat = false)
|
||||
{
|
||||
if (!openInCombat && TryComp<CombatModeComponent>(user, out var mode) && mode.IsInCombatMode)
|
||||
return false;
|
||||
|
||||
if (!HasComp<StrippingComponent>(user))
|
||||
return false;
|
||||
|
||||
_ui.OpenUi(target.Owner, StrippingUiKey.Key, user);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnCanDropOn(EntityUid uid, StrippingComponent component, ref CanDropTargetEvent args)
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Content.Shared.UserInterface
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public bool BlockSpectators;
|
||||
public bool AllowSpectator = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the item must be in the user's currently selected/active hand.
|
||||
|
||||
@@ -8,7 +8,7 @@ using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Shared.UserInterface;
|
||||
|
||||
@@ -19,12 +19,15 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||
|
||||
private readonly List<EntityUid> _toClose = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ActivatableUIComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<ActivatableUIComponent, UseInHandEvent>(OnUseInHand);
|
||||
SubscribeLocalEvent<ActivatableUIComponent, ActivateInWorldEvent>(OnActivate);
|
||||
SubscribeLocalEvent<ActivatableUIComponent, InteractUsingEvent>(OnInteractUsing);
|
||||
@@ -34,24 +37,28 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
SubscribeLocalEvent<ActivatableUIComponent, GetVerbsEvent<ActivationVerb>>(GetActivationVerb);
|
||||
SubscribeLocalEvent<ActivatableUIComponent, GetVerbsEvent<Verb>>(GetVerb);
|
||||
|
||||
// TODO ActivatableUI
|
||||
// Add UI-user component, and listen for user container changes.
|
||||
// I.e., should lose a computer UI if a player gets shut into a locker.
|
||||
SubscribeLocalEvent<ActivatableUIComponent, EntGotInsertedIntoContainerMessage>(OnGotInserted);
|
||||
SubscribeLocalEvent<ActivatableUIComponent, EntGotRemovedFromContainerMessage>(OnGotRemoved);
|
||||
|
||||
SubscribeLocalEvent<BoundUserInterfaceMessageAttempt>(OnBoundInterfaceInteractAttempt);
|
||||
SubscribeLocalEvent<UserInterfaceComponent, OpenUiActionEvent>(OnActionPerform);
|
||||
|
||||
InitializePower();
|
||||
}
|
||||
|
||||
private void OnStartup(Entity<ActivatableUIComponent> ent, ref ComponentStartup args)
|
||||
private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev)
|
||||
{
|
||||
if (ent.Comp.Key == null)
|
||||
{
|
||||
Log.Error($"Missing UI Key for entity: {ToPrettyString(ent)}");
|
||||
if (!TryComp(ev.Target, out ActivatableUIComponent? comp))
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO BUI
|
||||
// set interaction range to zero to avoid constant range checks.
|
||||
//
|
||||
// if (ent.Comp.InHandsOnly && _uiSystem.TryGetInterfaceData(ent.Owner, ent.Comp.Key, out var data))
|
||||
// data.InteractionRange = 0;
|
||||
if (!comp.RequireHands)
|
||||
return;
|
||||
|
||||
if (!TryComp(ev.Actor, out HandsComponent? hands) || hands.Hands.Count == 0)
|
||||
ev.Cancel();
|
||||
}
|
||||
|
||||
private void OnActionPerform(EntityUid uid, UserInterfaceComponent component, OpenUiActionEvent args)
|
||||
@@ -70,10 +77,9 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
|
||||
args.Verbs.Add(new ActivationVerb
|
||||
{
|
||||
// TODO VERBS add "open UI" icon
|
||||
Act = () => InteractUI(args.User, uid, component),
|
||||
Text = Loc.GetString(component.VerbText),
|
||||
// TODO VERB ICON find a better icon
|
||||
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")),
|
||||
Text = Loc.GetString(component.VerbText)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -84,10 +90,9 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
|
||||
args.Verbs.Add(new Verb
|
||||
{
|
||||
// TODO VERBS add "open UI" icon
|
||||
Act = () => InteractUI(args.User, uid, component),
|
||||
Text = Loc.GetString(component.VerbText),
|
||||
// TODO VERB ICON find a better icon
|
||||
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")),
|
||||
Text = Loc.GetString(component.VerbText)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -114,7 +119,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
return args.CanInteract || HasComp<GhostComponent>(args.User) && !component.BlockSpectators;
|
||||
return args.CanInteract || component.AllowSpectator && HasComp<GhostComponent>(args.User);
|
||||
}
|
||||
|
||||
private void OnUseInHand(EntityUid uid, ActivatableUIComponent component, UseInHandEvent args)
|
||||
@@ -186,7 +191,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_blockerSystem.CanInteract(user, uiEntity) && (!HasComp<GhostComponent>(user) || aui.BlockSpectators))
|
||||
if (!_blockerSystem.CanInteract(user, uiEntity) && (!aui.AllowSpectator || !HasComp<GhostComponent>(user)))
|
||||
return false;
|
||||
|
||||
if (aui.RequireHands)
|
||||
@@ -281,4 +286,47 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
if (ent.Comp.RequireHands && ent.Comp.InHandsOnly)
|
||||
CloseAll(ent, ent);
|
||||
}
|
||||
|
||||
private void OnGotInserted(Entity<ActivatableUIComponent> ent, ref EntGotInsertedIntoContainerMessage args)
|
||||
{
|
||||
CheckAccess((ent, ent));
|
||||
}
|
||||
|
||||
private void OnGotRemoved(Entity<ActivatableUIComponent> ent, ref EntGotRemovedFromContainerMessage args)
|
||||
{
|
||||
CheckAccess((ent, ent));
|
||||
}
|
||||
|
||||
public void CheckAccess(Entity<ActivatableUIComponent?> ent)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp))
|
||||
return;
|
||||
|
||||
if (ent.Comp.Key == null)
|
||||
{
|
||||
Log.Error($"Encountered null key in activatable ui on entity {ToPrettyString(ent)}");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var user in _uiSystem.GetActors(ent.Owner, ent.Comp.Key))
|
||||
{
|
||||
if (!_container.IsInSameOrParentContainer(user, ent)
|
||||
&& !_interaction.CanAccessViaStorage(user, ent))
|
||||
{
|
||||
_toClose.Add(user);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if (!_interaction.InRangeUnobstructed(user, ent))
|
||||
_toClose.Add(user);
|
||||
}
|
||||
|
||||
foreach (var user in _toClose)
|
||||
{
|
||||
_uiSystem.CloseUi(ent.Owner, ent.Comp.Key, user);
|
||||
}
|
||||
|
||||
_toClose.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,19 @@ namespace Content.Shared.Verbs
|
||||
extraCategories = new();
|
||||
|
||||
// accessibility checks
|
||||
var canAccess = force || _interactionSystem.InRangeAndAccessible(user, target);
|
||||
bool canAccess = false;
|
||||
if (force || target == user)
|
||||
canAccess = true;
|
||||
else if (_interactionSystem.InRangeUnobstructed(user, target))
|
||||
{
|
||||
// Note that being in a container does not count as an obstruction for InRangeUnobstructed
|
||||
// Therefore, we need extra checks to ensure the item is actually accessible:
|
||||
if (ContainerSystem.IsInSameOrParentContainer(user, target))
|
||||
canAccess = true;
|
||||
else
|
||||
// the item might be in a backpack that the user has open
|
||||
canAccess = _interactionSystem.CanAccessViaStorage(user, target);
|
||||
}
|
||||
|
||||
// A large number of verbs need to check action blockers. Instead of repeatedly having each system individually
|
||||
// call ActionBlocker checks, just cache it for the verb request.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared._White;
|
||||
using Content.Shared._White.Implants.NeuroControl;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.CombatMode;
|
||||
@@ -15,7 +13,6 @@ using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Item.ItemToggle.Components;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Weapons.Melee.Components;
|
||||
@@ -23,6 +20,9 @@ using Content.Shared.Weapons.Melee.Events;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Content.Shared.Weapons.Ranged.Events;
|
||||
using Content.Shared.Weapons.Ranged.Systems;
|
||||
using Content.Shared._White;
|
||||
using Content.Shared._White.Implants.NeuroControl;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
@@ -205,39 +205,49 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
|
||||
private void OnLightAttack(LightAttackEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.SenderSession.AttachedEntity is not {} user)
|
||||
var user = args.SenderSession.AttachedEntity;
|
||||
|
||||
if (user == null)
|
||||
return;
|
||||
|
||||
if (!TryGetWeapon(user, out var weaponUid, out var weapon) ||
|
||||
if (!TryGetWeapon(user.Value, out var weaponUid, out var weapon) ||
|
||||
weaponUid != GetEntity(msg.Weapon))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AttemptAttack(user, weaponUid, weapon, msg, args.SenderSession);
|
||||
AttemptAttack(args.SenderSession.AttachedEntity!.Value, weaponUid, weapon, msg, args.SenderSession);
|
||||
}
|
||||
|
||||
private void OnHeavyAttack(HeavyAttackEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.SenderSession.AttachedEntity is not {} user)
|
||||
if (args.SenderSession.AttachedEntity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryGetWeapon(user, out var weaponUid, out var weapon) ||
|
||||
if (!TryGetWeapon(args.SenderSession.AttachedEntity.Value, out var weaponUid, out var weapon) ||
|
||||
weaponUid != GetEntity(msg.Weapon))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AttemptAttack(user, weaponUid, weapon, msg, args.SenderSession);
|
||||
AttemptAttack(args.SenderSession.AttachedEntity.Value, weaponUid, weapon, msg, args.SenderSession);
|
||||
}
|
||||
|
||||
private void OnDisarmAttack(DisarmAttackEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.SenderSession.AttachedEntity is not {} user)
|
||||
if (args.SenderSession.AttachedEntity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryGetWeapon(user, out var weaponUid, out var weapon))
|
||||
AttemptAttack(user, weaponUid, weapon, msg, args.SenderSession);
|
||||
if (!TryGetWeapon(args.SenderSession.AttachedEntity.Value, out var weaponUid, out var weapon))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AttemptAttack(args.SenderSession.AttachedEntity.Value, weaponUid, weapon, msg, args.SenderSession);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -371,22 +381,18 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
return false;
|
||||
|
||||
var update = UpdateNextAttack.Both; // WD
|
||||
EntityUid? target = null;
|
||||
switch (attack)
|
||||
{
|
||||
case LightAttackEvent light:
|
||||
var lightTarget = GetEntity(light.Target);
|
||||
if (light.Target != null && !TryGetEntity(light.Target, out target))
|
||||
{
|
||||
// Target was lightly attacked & deleted.
|
||||
return false;
|
||||
}
|
||||
update = lightTarget == null ? UpdateNextAttack.Both :
|
||||
IsMob(lightTarget.Value) ? UpdateNextAttack.Mob : UpdateNextAttack.NonMob; // WD
|
||||
|
||||
if (!Blocker.CanAttack(user, target, (weaponUid, weapon)))
|
||||
if (!Blocker.CanAttack(user, lightTarget, (weaponUid, weapon)))
|
||||
return false;
|
||||
|
||||
// Can't self-attack if you're the weapon
|
||||
if (weaponUid == target)
|
||||
if (weaponUid == lightTarget)
|
||||
return false;
|
||||
|
||||
// WD START
|
||||
@@ -415,13 +421,11 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
|
||||
break;
|
||||
case DisarmAttackEvent disarm:
|
||||
if (disarm.Target != null && !TryGetEntity(disarm.Target, out target))
|
||||
{
|
||||
// Target was lightly attacked & deleted.
|
||||
return false;
|
||||
}
|
||||
var disarmTarget = GetEntity(disarm.Target);
|
||||
update = disarmTarget == null ? UpdateNextAttack.Both :
|
||||
IsMob(disarmTarget.Value) ? UpdateNextAttack.Mob : UpdateNextAttack.NonMob; // WD
|
||||
|
||||
if (!Blocker.CanAttack(user, target, (weaponUid, weapon), true))
|
||||
if (!Blocker.CanAttack(user, disarmTarget, (weaponUid, weapon), true))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -3,7 +3,6 @@ using Content.Shared.Database;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Tools.Systems;
|
||||
using Content.Shared.UserInterface;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
namespace Content.Shared.Wires;
|
||||
@@ -11,7 +10,6 @@ namespace Content.Shared.Wires;
|
||||
public abstract class SharedWiresSystem : EntitySystem
|
||||
{
|
||||
[Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!;
|
||||
[Dependency] private readonly ActivatableUISystem _activatableUI = default!;
|
||||
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
|
||||
[Dependency] protected readonly SharedAudioSystem Audio = default!;
|
||||
[Dependency] protected readonly SharedToolSystem Tool = default!;
|
||||
@@ -23,9 +21,6 @@ public abstract class SharedWiresSystem : EntitySystem
|
||||
SubscribeLocalEvent<WiresPanelComponent, WirePanelDoAfterEvent>(OnPanelDoAfter);
|
||||
SubscribeLocalEvent<WiresPanelComponent, InteractUsingEvent>(OnInteractUsing);
|
||||
SubscribeLocalEvent<WiresPanelComponent, ExaminedEvent>(OnExamine);
|
||||
|
||||
SubscribeLocalEvent<ActivatableUIRequiresPanelComponent, ActivatableUIOpenAttemptEvent>(OnAttemptOpenActivatableUI);
|
||||
SubscribeLocalEvent<ActivatableUIRequiresPanelComponent, PanelChangedEvent>(OnActivatableUIPanelChanged);
|
||||
}
|
||||
|
||||
private void OnPanelDoAfter(EntityUid uid, WiresPanelComponent panel, WirePanelDoAfterEvent args)
|
||||
@@ -137,21 +132,4 @@ public abstract class SharedWiresSystem : EntitySystem
|
||||
|
||||
return entity.Comp.Open;
|
||||
}
|
||||
|
||||
private void OnAttemptOpenActivatableUI(EntityUid uid, ActivatableUIRequiresPanelComponent component, ActivatableUIOpenAttemptEvent args)
|
||||
{
|
||||
if (args.Cancelled || !TryComp<WiresPanelComponent>(uid, out var wires))
|
||||
return;
|
||||
|
||||
if (component.RequireOpen != wires.Open)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnActivatableUIPanelChanged(EntityUid uid, ActivatableUIRequiresPanelComponent component, ref PanelChangedEvent args)
|
||||
{
|
||||
if (args.Open == component.RequireOpen)
|
||||
return;
|
||||
|
||||
_activatableUI.CloseAll(uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ objectives-round-end-result = {$count ->
|
||||
objectives-round-end-result-in-custody = {$custody} out of {$count} {MAKEPLURAL($agent)} were in custody.
|
||||
|
||||
objectives-player-user-named = [color=White]{$name}[/color] ([color=gray]{$user}[/color])
|
||||
objectives-player-user = [color=gray]{$user}[/color]
|
||||
objectives-player-named = [color=White]{$name}[/color]
|
||||
|
||||
objectives-no-objectives = {$custody}{$title} was a {$agent}.
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
icon:
|
||||
sprite: Structures/Piping/Atmospherics/Portable/portable_sheater.rsi
|
||||
state: sheaterOff
|
||||
product: CrateEngineeringSpaceHeater
|
||||
product: SpaceHeaterAnchored
|
||||
cost: 300
|
||||
category: cargoproduct-category-name-engineering
|
||||
group: market
|
||||
|
||||
@@ -194,13 +194,3 @@
|
||||
contents:
|
||||
- id: WeaponParticleDecelerator
|
||||
amount: 3
|
||||
|
||||
- type: entity
|
||||
id: CrateEngineeringSpaceHeater
|
||||
parent: CrateEngineering
|
||||
name: space heater crate
|
||||
description: Contains a space heater for climate control.
|
||||
components:
|
||||
- type: StorageFill
|
||||
contents:
|
||||
- id: SpaceHeaterFlatpack
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
components:
|
||||
- type: StorageFill
|
||||
contents:
|
||||
- id: EmitterFlatpack
|
||||
- id: Emitter # TODO change to flatpack
|
||||
|
||||
- type: entity
|
||||
id: CrateEngineeringSingularityCollector
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
- type: entity
|
||||
id: MobSpawnCrabQuartz
|
||||
name: mobspawner quartzcrab
|
||||
noSpawn: True
|
||||
components:
|
||||
- type: Transform
|
||||
anchored: True
|
||||
@@ -31,7 +30,6 @@
|
||||
id: MobSpawnCrabIron
|
||||
parent: MobSpawnCrabQuartz
|
||||
name: mobspawner ironcrab
|
||||
noSpawn: True
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: /Textures/Effects/mobspawn.rsi
|
||||
@@ -43,7 +41,6 @@
|
||||
id: MobSpawnCrabSilver
|
||||
parent: MobSpawnCrabQuartz
|
||||
name: mobspawner silvercrab
|
||||
noSpawn: True
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: /Textures/Effects/mobspawn.rsi
|
||||
@@ -55,7 +52,6 @@
|
||||
id: MobSpawnCrabUranium
|
||||
parent: MobSpawnCrabQuartz
|
||||
name: mobspawner uraniumcrab
|
||||
noSpawn: True
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: /Textures/Effects/mobspawn.rsi
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
- type: entity
|
||||
id: WallSpawnAsteroid
|
||||
noSpawn: True
|
||||
components:
|
||||
- type: Transform
|
||||
anchored: True
|
||||
@@ -29,7 +28,6 @@
|
||||
- type: entity
|
||||
id: WallSpawnAsteroidUraniumCrab
|
||||
parent: WallSpawnAsteroid
|
||||
noSpawn: True
|
||||
components:
|
||||
- type: SpawnOnDespawn
|
||||
prototype: AsteroidRockUraniumCrab
|
||||
@@ -37,7 +35,6 @@
|
||||
- type: entity
|
||||
id: WallSpawnAsteroidUranium
|
||||
parent: WallSpawnAsteroid
|
||||
noSpawn: True
|
||||
components:
|
||||
- type: SpawnOnDespawn
|
||||
prototype: AsteroidRockUranium
|
||||
@@ -45,7 +42,6 @@
|
||||
- type: entity
|
||||
id: WallSpawnAsteroidQuartzCrab
|
||||
parent: WallSpawnAsteroid
|
||||
noSpawn: True
|
||||
components:
|
||||
- type: SpawnOnDespawn
|
||||
prototype: AsteroidRockQuartzCrab
|
||||
@@ -53,7 +49,6 @@
|
||||
- type: entity
|
||||
id: WallSpawnAsteroidQuartz
|
||||
parent: WallSpawnAsteroid
|
||||
noSpawn: True
|
||||
components:
|
||||
- type: SpawnOnDespawn
|
||||
prototype: AsteroidRockQuartz
|
||||
@@ -61,7 +56,6 @@
|
||||
- type: entity
|
||||
id: WallSpawnAsteroidSilverCrab
|
||||
parent: WallSpawnAsteroid
|
||||
noSpawn: True
|
||||
components:
|
||||
- type: SpawnOnDespawn
|
||||
prototype: AsteroidRockSilverCrab
|
||||
@@ -69,7 +63,6 @@
|
||||
- type: entity
|
||||
id: WallSpawnAsteroidSilver
|
||||
parent: WallSpawnAsteroid
|
||||
noSpawn: True
|
||||
components:
|
||||
- type: SpawnOnDespawn
|
||||
prototype: AsteroidRockSilver
|
||||
@@ -77,7 +70,6 @@
|
||||
- type: entity
|
||||
id: WallSpawnAsteroidIronCrab
|
||||
parent: WallSpawnAsteroid
|
||||
noSpawn: True
|
||||
components:
|
||||
- type: SpawnOnDespawn
|
||||
prototype: AsteroidRockTinCrab
|
||||
@@ -85,7 +77,6 @@
|
||||
- type: entity
|
||||
id: WallSpawnAsteroidIron
|
||||
parent: WallSpawnAsteroid
|
||||
noSpawn: True
|
||||
components:
|
||||
- type: SpawnOnDespawn
|
||||
prototype: AsteroidRockTin
|
||||
@@ -1,92 +0,0 @@
|
||||
# Most of these have DO NOT MAP, since stations are completely unequipped to deal with ship combat + these are basically placeholder.
|
||||
|
||||
- type: entity
|
||||
id: ShuttleGunSvalinnMachineGunCircuitboard
|
||||
parent: BaseMachineCircuitboard
|
||||
name: LSE-400c "Svalinn machine gun" machine board
|
||||
description: A machine printed circuit board for an LSE-400c "Svalinn machine gun"
|
||||
suffix: DO NOT MAP, Machine Board
|
||||
components:
|
||||
- type: Sprite
|
||||
state: security
|
||||
- type: MachineBoard
|
||||
prototype: ShuttleGunSvalinnMachineGun
|
||||
requirements:
|
||||
MatterBin: 2
|
||||
Manipulator: 4
|
||||
materialRequirements:
|
||||
Steel: 5
|
||||
CableHV: 5
|
||||
|
||||
- type: entity
|
||||
id: ShuttleGunPerforatorCircuitboard
|
||||
parent: BaseMachineCircuitboard
|
||||
name: LSE-1200c "Perforator" machine board
|
||||
description: A machine printed circuit board for an LSE-1200c "Perforator"
|
||||
suffix: DO NOT MAP, Machine Board
|
||||
components:
|
||||
- type: Sprite
|
||||
state: security
|
||||
- type: MachineBoard
|
||||
prototype: ShuttleGunPerforator
|
||||
requirements:
|
||||
MatterBin: 4
|
||||
Manipulator: 6
|
||||
materialRequirements:
|
||||
Steel: 10
|
||||
CableHV: 5
|
||||
|
||||
- type: entity
|
||||
id: ShuttleGunFriendshipCircuitboard
|
||||
parent: BaseMachineCircuitboard
|
||||
name: EXP-320g "Friendship" machine board
|
||||
description: A machine printed circuit board for an EXP-320g "Friendship"
|
||||
suffix: DO NOT MAP, Machine Board
|
||||
components:
|
||||
- type: Sprite
|
||||
state: security
|
||||
- type: MachineBoard
|
||||
prototype: ShuttleGunFriendship
|
||||
requirements:
|
||||
MatterBin: 3
|
||||
Manipulator: 2
|
||||
materialRequirements:
|
||||
Steel: 7
|
||||
CableHV: 5
|
||||
|
||||
- type: entity
|
||||
id: ShuttleGunDusterCircuitboard
|
||||
parent: BaseMachineCircuitboard
|
||||
name: EXP-2100g "Duster" machine board
|
||||
description: A machine printed circuit board for an EXP-2100g "Duster"
|
||||
suffix: DO NOT MAP, Machine Board
|
||||
components:
|
||||
- type: Sprite
|
||||
state: security
|
||||
- type: MachineBoard
|
||||
prototype: ShuttleGunDuster
|
||||
requirements:
|
||||
MatterBin: 6
|
||||
Manipulator: 4
|
||||
materialRequirements:
|
||||
Steel: 10
|
||||
CableHV: 5
|
||||
Uranium: 2
|
||||
|
||||
- type: entity
|
||||
id: ShuttleGunKineticCircuitboard
|
||||
parent: BaseMachineCircuitboard
|
||||
name: PTK-800 "Matter Dematerializer" machine board
|
||||
description: A machine printed circuit board for an PTK-800 "Matter Dematerializer"
|
||||
suffix: DO NOT MAP, Machine Board
|
||||
components:
|
||||
- type: Sprite
|
||||
state: security
|
||||
- type: MachineBoard
|
||||
prototype: ShuttleGunKinetic
|
||||
requirements:
|
||||
MatterBin: 2
|
||||
Manipulator: 3
|
||||
materialRequirements:
|
||||
Steel: 5
|
||||
CableHV: 2
|
||||
@@ -1277,6 +1277,92 @@
|
||||
CableHV: 5
|
||||
Uranium: 2
|
||||
|
||||
- type: entity
|
||||
id: ShuttleGunSvalinnMachineGunCircuitboard
|
||||
parent: BaseMachineCircuitboard
|
||||
name: LSE-400c "Svalinn machine gun" machine board
|
||||
description: A machine printed circuit board for an LSE-400c "Svalinn machine gun"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: security
|
||||
- type: MachineBoard
|
||||
prototype: ShuttleGunSvalinnMachineGun
|
||||
requirements:
|
||||
MatterBin: 2
|
||||
Manipulator: 4
|
||||
materialRequirements:
|
||||
Steel: 5
|
||||
CableHV: 5
|
||||
|
||||
- type: entity
|
||||
id: ShuttleGunPerforatorCircuitboard
|
||||
parent: BaseMachineCircuitboard
|
||||
name: LSE-1200c "Perforator" machine board
|
||||
description: A machine printed circuit board for an LSE-1200c "Perforator"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: security
|
||||
- type: MachineBoard
|
||||
prototype: ShuttleGunPerforator
|
||||
requirements:
|
||||
MatterBin: 4
|
||||
Manipulator: 6
|
||||
materialRequirements:
|
||||
Steel: 10
|
||||
CableHV: 5
|
||||
|
||||
- type: entity
|
||||
id: ShuttleGunFriendshipCircuitboard
|
||||
parent: BaseMachineCircuitboard
|
||||
name: EXP-320g "Friendship" machine board
|
||||
description: A machine printed circuit board for an EXP-320g "Friendship"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: security
|
||||
- type: MachineBoard
|
||||
prototype: ShuttleGunFriendship
|
||||
requirements:
|
||||
MatterBin: 3
|
||||
Manipulator: 2
|
||||
materialRequirements:
|
||||
Steel: 7
|
||||
CableHV: 5
|
||||
|
||||
- type: entity
|
||||
id: ShuttleGunDusterCircuitboard
|
||||
parent: BaseMachineCircuitboard
|
||||
name: EXP-2100g "Duster" machine board
|
||||
description: A machine printed circuit board for an EXP-2100g "Duster"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: security
|
||||
- type: MachineBoard
|
||||
prototype: ShuttleGunDuster
|
||||
requirements:
|
||||
MatterBin: 6
|
||||
Manipulator: 4
|
||||
materialRequirements:
|
||||
Steel: 10
|
||||
CableHV: 5
|
||||
Uranium: 2
|
||||
|
||||
- type: entity
|
||||
id: ShuttleGunKineticCircuitboard
|
||||
parent: BaseMachineCircuitboard
|
||||
name: PTK-800 "Matter Dematerializer" machine board
|
||||
description: A machine printed circuit board for an PTK-800 "Matter Dematerializer"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: security
|
||||
- type: MachineBoard
|
||||
prototype: ShuttleGunKinetic
|
||||
requirements:
|
||||
MatterBin: 2
|
||||
Manipulator: 3
|
||||
materialRequirements:
|
||||
Steel: 5
|
||||
CableHV: 2
|
||||
|
||||
- type: entity
|
||||
parent: BaseMachineCircuitboard
|
||||
id: ReagentGrinderIndustrialMachineCircuitboard
|
||||
|
||||
@@ -184,12 +184,3 @@
|
||||
- state: overlay
|
||||
color: "#cec8ac"
|
||||
- state: icon-default
|
||||
|
||||
- type: entity
|
||||
parent: BaseFlatpack
|
||||
id: SpaceHeaterFlatpack
|
||||
name: space heater flatpack
|
||||
description: A flatpack used for constructing a space heater.
|
||||
components:
|
||||
- type: Flatpack
|
||||
entity: SpaceHeaterAnchored
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
components:
|
||||
- type: Instrument
|
||||
- type: ActivatableUI
|
||||
blockSpectators: true # otherwise they can play client-side music
|
||||
allowSpectator: false # otherwise they can play client-side music
|
||||
inHandsOnly: false
|
||||
singleUser: true
|
||||
requireHands: true
|
||||
|
||||
@@ -126,8 +126,8 @@
|
||||
maxVol: 7
|
||||
- type: SolutionInjectOnEmbed
|
||||
transferAmount: 7
|
||||
blockSlots: NONE
|
||||
solution: melee
|
||||
blockSlots: NONE
|
||||
- type: SolutionTransfer
|
||||
maxTransferAmount: 7
|
||||
|
||||
|
||||
@@ -325,12 +325,11 @@
|
||||
emptyCase: { state: empty }
|
||||
wiredCase: { state: wired }
|
||||
caseWithTrigger: { state: no-payload }
|
||||
caseWithPayload: { state: no-trigger }
|
||||
grenade: { state: complete }
|
||||
enum.Trigger.TriggerVisuals.VisualState:
|
||||
enum.ConstructionVisuals.Layer:
|
||||
Primed: { state: primed }
|
||||
# Unprimed: <Use state determined by enum.ConstructionVisuals.Layer>
|
||||
Unprimed: { state: complete }
|
||||
- type: StaticPrice
|
||||
price: 25
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# Most of these have DO NOT MAP, since stations are completely unequipped to deal with ship combat + these are basically placeholder.
|
||||
|
||||
- type: entity
|
||||
id: ShuttleGunBase
|
||||
name: shittle gun
|
||||
@@ -60,8 +58,7 @@
|
||||
id: ShuttleGunSvalinnMachineGun
|
||||
parent: [ ShuttleGunBase, ConstructibleMachine]
|
||||
name: LSE-400c "Svalinn machine gun"
|
||||
description: Basic stationary laser unit. Effective against live targets and electronics. Uses regular power cells to fire, and has an extremely high rate of fire.
|
||||
suffix: DO NOT MAP
|
||||
description: Basic stationary laser unit. Effective against live targets and electronics. Uses regular power cells to fire, and has an extremely high rate of fire
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Weapons/Guns/Shuttles/laser.rsi
|
||||
@@ -116,7 +113,6 @@
|
||||
parent: [ ShuttleGunBase, ConstructibleMachine]
|
||||
name: LSE-1200c "Perforator"
|
||||
description: Advanced stationary laser unit. Annihilates electronics and is extremely dangerous to health! Uses the power cage to fire.
|
||||
suffix: DO NOT MAP
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Weapons/Guns/Shuttles/laser.rsi
|
||||
@@ -173,7 +169,6 @@
|
||||
parent: [ShuttleGunBase, ConstructibleMachine]
|
||||
name: EXP-320g "Friendship"
|
||||
description: A small stationary grenade launcher that holds 2 grenades.
|
||||
suffix: DO NOT MAP
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Weapons/Guns/Shuttles/launcher.rsi
|
||||
@@ -227,7 +222,6 @@
|
||||
parent: [ShuttleGunBase, ConstructibleMachine]
|
||||
name: EXP-2100g "Duster"
|
||||
description: A powerful stationary grenade launcher. A cartridge is required for use.
|
||||
suffix: DO NOT MAP
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Weapons/Guns/Shuttles/launcher.rsi
|
||||
@@ -325,7 +319,6 @@
|
||||
parent: [ ShuttleGunBase, ConstructibleMachine]
|
||||
name: PTK-800 "Matter Dematerializer"
|
||||
description: Salvage stationary mining turret. Gradually accumulates charges on its own, extremely effective for asteroid excavation.
|
||||
suffix: DO NOT MAP
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Weapons/Guns/Shuttles/kinetic.rsi
|
||||
|
||||
@@ -413,9 +413,9 @@
|
||||
prototype: InitialInfected
|
||||
|
||||
- type: entity
|
||||
noSpawn: true
|
||||
parent: BaseNukeopsRule
|
||||
id: LoneOpsSpawn
|
||||
parent: BaseGameRule
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: StationEvent
|
||||
earliestStart: 35
|
||||
@@ -447,9 +447,9 @@
|
||||
prototype: Nukeops
|
||||
|
||||
- type: entity
|
||||
noSpawn: true
|
||||
parent: BaseTraitorRule
|
||||
id: SleeperAgentsRule
|
||||
parent: BaseGameRule
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: StationEvent
|
||||
earliestStart: 30
|
||||
@@ -460,6 +460,7 @@
|
||||
startAudio:
|
||||
path: /Audio/Announcements/intercept.ogg
|
||||
- type: AlertLevelInterceptionRule
|
||||
- type: TraitorRule
|
||||
- type: AntagSelection
|
||||
definitions:
|
||||
- prefRoles: [ Traitor ]
|
||||
|
||||
@@ -35,19 +35,7 @@
|
||||
id: Thief
|
||||
components:
|
||||
- type: ThiefRule
|
||||
- type: AntagObjectives
|
||||
objectives:
|
||||
- EscapeThiefShuttleObjective
|
||||
- type: AntagRandomObjectives
|
||||
sets:
|
||||
- groups: ThiefBigObjectiveGroups
|
||||
prob: 0.7
|
||||
maxPicks: 1
|
||||
- groups: ThiefObjectiveGroups
|
||||
maxPicks: 10
|
||||
maxDifficulty: 2.5
|
||||
- type: AntagSelection
|
||||
agentName: thief-round-end-agent-name
|
||||
definitions:
|
||||
- prefRoles: [ Thief ]
|
||||
maxRange:
|
||||
|
||||
@@ -64,9 +64,9 @@
|
||||
roundEndDelay: 10
|
||||
|
||||
- type: entity
|
||||
abstract: true
|
||||
id: Nukeops
|
||||
parent: BaseGameRule
|
||||
id: BaseNukeopsRule
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: GameRule
|
||||
minPlayers: 20 # Honk
|
||||
@@ -75,16 +75,6 @@
|
||||
- operationPrefix
|
||||
- operationSuffix
|
||||
- type: NukeopsRule
|
||||
- type: AntagSelection
|
||||
- type: AntagLoadProfileRule
|
||||
|
||||
- type: entity
|
||||
noSpawn: true
|
||||
parent: BaseNukeopsRule
|
||||
id: Nukeops
|
||||
components:
|
||||
- type: GameRule
|
||||
minPlayers: 20
|
||||
- type: LoadMapRule
|
||||
gameMap: NukieOutpost
|
||||
- type: AntagSelection
|
||||
@@ -148,30 +138,16 @@
|
||||
prototype: Nukeops
|
||||
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: BaseGameRule
|
||||
id: BaseTraitorRule
|
||||
components:
|
||||
- type: TraitorRule
|
||||
# TODO: codewords in yml
|
||||
# TODO: uplink in yml
|
||||
- type: AntagRandomObjectives
|
||||
sets:
|
||||
- groups: TraitorObjectiveGroups
|
||||
maxDifficulty: 5
|
||||
- type: AntagSelection
|
||||
agentName: traitor-round-end-agent-name
|
||||
|
||||
- type: entity
|
||||
noSpawn: true
|
||||
parent: BaseTraitorRule
|
||||
id: Traitor
|
||||
parent: BaseGameRule
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: GameRule
|
||||
minPlayers: 5
|
||||
delay:
|
||||
min: 240
|
||||
max: 420
|
||||
- type: TraitorRule
|
||||
- type: AntagSelection
|
||||
definitions:
|
||||
- prefRoles: [ Traitor ]
|
||||
|
||||
@@ -55,6 +55,13 @@
|
||||
ThiefObjectiveGroupStructure: 0 #Temporarily disabled until obvious ways to steal structures are added
|
||||
ThiefObjectiveGroupAnimal: 2
|
||||
|
||||
- type: weightedRandom
|
||||
id: ThiefEscapeObjectiveGroups
|
||||
weights:
|
||||
ThiefObjectiveGroupEscape: 1
|
||||
|
||||
|
||||
|
||||
- type: weightedRandom
|
||||
id: ThiefObjectiveGroupCollection
|
||||
weights:
|
||||
|
||||
@@ -50,12 +50,6 @@
|
||||
store: payloadTrigger
|
||||
name: триггер
|
||||
doAfter: 0.5
|
||||
- to: caseWithPayload
|
||||
steps:
|
||||
- tag: Payload
|
||||
store: payload
|
||||
name: Payload
|
||||
doAfter: 0.5
|
||||
|
||||
- node: caseWithTrigger
|
||||
actions:
|
||||
@@ -77,26 +71,6 @@
|
||||
name: заряд
|
||||
doAfter: 0.5
|
||||
|
||||
- node: caseWithPayload
|
||||
actions:
|
||||
- !type:AppearanceChange
|
||||
- !type:PlaySound
|
||||
sound: /Audio/Machines/button.ogg
|
||||
edges:
|
||||
- to: wiredCase
|
||||
steps:
|
||||
- tool: Prying
|
||||
doAfter: 0.5
|
||||
completed:
|
||||
- !type:EmptyContainer
|
||||
container: payload
|
||||
- to: grenade
|
||||
steps:
|
||||
- component: PayloadTrigger
|
||||
store: payloadTrigger
|
||||
name: Trigger
|
||||
doAfter: 0.5
|
||||
|
||||
- node: grenade
|
||||
actions:
|
||||
- !type:AppearanceChange
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/29c0ed1b000619cb5398ef921000a8d4502ba0b6 and modified by Swept & ElectroSR",
|
||||
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/29c0ed1b000619cb5398ef921000a8d4502ba0b6 and modified by Swept",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
@@ -19,10 +19,6 @@
|
||||
"name": "no-payload",
|
||||
"directions": 1
|
||||
},
|
||||
{
|
||||
"name": "no-trigger",
|
||||
"directions": 1
|
||||
},
|
||||
{
|
||||
"name": "complete",
|
||||
"directions": 1
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 347 B |
Submodule RobustToolbox updated: 721408bb37...ec794ce4e4
Reference in New Issue
Block a user