Merge remote-tracking branch 'refs/remotes/wizards/master'

# Conflicts:
#	Content.Shared/MagicMirror/MagicMirrorComponent.cs
#	Resources/Prototypes/Catalog/Fills/Lockers/misc.yml
#	Resources/Prototypes/Entities/Stations/base.yml
#	Resources/Textures/Clothing/Head/Hats/beret_security.rsi/equipped-HELMET-hamster.png
#	Resources/Textures/Clothing/Head/Hats/beret_security.rsi/equipped-HELMET.png
#	Resources/Textures/Clothing/Head/Hats/beret_security.rsi/icon.png
#	Resources/Textures/Clothing/Head/Hats/beret_security.rsi/inhand-left.png
#	Resources/Textures/Clothing/Head/Hats/beret_security.rsi/inhand-right.png
#	Resources/Textures/Clothing/Head/Hats/beret_security.rsi/meta.json
#	Resources/Textures/Clothing/Head/Hoods/Bio/security.rsi/equipped-HELMET.png
#	Resources/Textures/Clothing/Head/Hoods/Bio/security.rsi/icon.png
#	Resources/Textures/Clothing/Head/Hoods/Bio/security.rsi/inhand-left.png
#	Resources/Textures/Clothing/Head/Hoods/Bio/security.rsi/inhand-right.png
#	Resources/Textures/Clothing/Head/Hoods/Bio/security.rsi/meta.json
#	Resources/Textures/Clothing/OuterClothing/Bio/security.rsi/equipped-OUTERCLOTHING.png
#	Resources/Textures/Clothing/OuterClothing/Bio/security.rsi/icon.png
#	Resources/Textures/Clothing/OuterClothing/Bio/security.rsi/inhand-left.png
#	Resources/Textures/Clothing/OuterClothing/Bio/security.rsi/inhand-right.png
#	Resources/Textures/Clothing/OuterClothing/Bio/security.rsi/meta.json
#	Resources/Textures/Clothing/OuterClothing/Coats/warden.rsi/equipped-OUTERCLOTHING.png
#	Resources/Textures/Clothing/OuterClothing/Coats/warden.rsi/icon.png
#	Resources/Textures/Clothing/OuterClothing/Coats/warden.rsi/inhand-left.png
#	Resources/Textures/Clothing/OuterClothing/Coats/warden.rsi/inhand-right.png
#	Resources/Textures/Clothing/OuterClothing/WinterCoats/coatwarden.rsi/meta.json
#	Resources/Textures/Clothing/Uniforms/Jumpskirt/hos.rsi/equipped-INNERCLOTHING.png
#	Resources/Textures/Clothing/Uniforms/Jumpskirt/hos.rsi/icon.png
#	Resources/Textures/Clothing/Uniforms/Jumpskirt/security.rsi/equipped-INNERCLOTHING.png
#	Resources/Textures/Clothing/Uniforms/Jumpskirt/security.rsi/icon.png
#	Resources/Textures/Clothing/Uniforms/Jumpskirt/warden.rsi/equipped-INNERCLOTHING.png
#	Resources/Textures/Clothing/Uniforms/Jumpskirt/warden.rsi/icon.png
#	Resources/Textures/Clothing/Uniforms/Jumpsuit/hos.rsi/equipped-INNERCLOTHING.png
#	Resources/Textures/Clothing/Uniforms/Jumpsuit/hos.rsi/icon.png
#	Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/equipped-INNERCLOTHING.png
#	Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/icon.png
#	Resources/Textures/Clothing/Uniforms/Jumpsuit/warden.rsi/equipped-INNERCLOTHING.png
#	Resources/Textures/Clothing/Uniforms/Jumpsuit/warden.rsi/icon.png
#	Resources/migration.yml
This commit is contained in:
VigersRay 2024-07-31 03:54:09 +03:00
commit 31b2d24e30
172 changed files with 17323 additions and 17369 deletions

4
.github/labeler.yml vendored
View file

@ -12,6 +12,10 @@
- changed-files:
- any-glob-to-any-file: '**/*.xaml*'
"Changes: Shaders":
- changed-files:
- any-glob-to-any-file: '**/*.swsl'
"No C#":
- changed-files:
# Equiv to any-glob-to-all as long as this has one matcher. If ALL changed files are not C# files, then apply label.

View file

@ -30,7 +30,11 @@ namespace Content.Client.Administration.UI.Bwoink
}
};
OnOpen += () => Bwoink.PopulateList();
OnOpen += () =>
{
Bwoink.ChannelSelector.StopFiltering();
Bwoink.PopulateList();
};
}
}
}

View file

@ -4,154 +4,155 @@ using Content.Client.UserInterface.Controls;
using Content.Client.Verbs.UI;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Input;
using Robust.Shared.Utility;
namespace Content.Client.Administration.UI.CustomControls
namespace Content.Client.Administration.UI.CustomControls;
[GenerateTypedNameReferences]
public sealed partial class PlayerListControl : BoxContainer
{
[GenerateTypedNameReferences]
public sealed partial class PlayerListControl : BoxContainer
private readonly AdminSystem _adminSystem;
private readonly IEntityManager _entManager;
private readonly IUserInterfaceManager _uiManager;
private PlayerInfo? _selectedPlayer;
private List<PlayerInfo> _playerList = new();
private readonly List<PlayerInfo> _sortedPlayerList = new();
public Comparison<PlayerInfo>? Comparison;
public Func<PlayerInfo, string, string>? OverrideText;
public PlayerListControl()
{
private readonly AdminSystem _adminSystem;
private List<PlayerInfo> _playerList = new();
private readonly List<PlayerInfo> _sortedPlayerList = new();
public event Action<PlayerInfo?>? OnSelectionChanged;
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList;
public Func<PlayerInfo, string, string>? OverrideText;
public Comparison<PlayerInfo>? Comparison;
private IEntityManager _entManager;
private IUserInterfaceManager _uiManager;
private PlayerInfo? _selectedPlayer;
public PlayerListControl()
{
_entManager = IoCManager.Resolve<IEntityManager>();
_uiManager = IoCManager.Resolve<IUserInterfaceManager>();
_adminSystem = _entManager.System<AdminSystem>();
RobustXamlLoader.Load(this);
// Fill the Option data
PlayerListContainer.ItemPressed += PlayerListItemPressed;
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
PlayerListContainer.GenerateItem += GenerateButton;
PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
PopulateList(_adminSystem.PlayerList);
FilterLineEdit.OnTextChanged += _ => FilterList();
_adminSystem.PlayerListChanged += PopulateList;
BackgroundPanel.PanelOverride = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 40)};
}
private void PlayerListNoItemSelected()
{
_selectedPlayer = null;
OnSelectionChanged?.Invoke(null);
}
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData {Info: var selectedPlayer})
return;
if (selectedPlayer == _selectedPlayer)
return;
if (args.Event.Function != EngineKeyFunctions.UIClick)
return;
OnSelectionChanged?.Invoke(selectedPlayer);
_selectedPlayer = selectedPlayer;
// update label text. Only required if there is some override (e.g. unread bwoink count).
if (OverrideText != null && args.Button.Children.FirstOrDefault()?.Children?.FirstOrDefault() is Label label)
label.Text = GetText(selectedPlayer);
}
private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
return;
if (args.Function != EngineKeyFunctions.UIRightClick || selectedPlayer.NetEntity == null)
return;
_uiManager.GetUIController<VerbMenuUIController>().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
args.Handle();
}
public void StopFiltering()
{
FilterLineEdit.Text = string.Empty;
}
private void FilterList()
{
_sortedPlayerList.Clear();
foreach (var info in _playerList)
{
var displayName = $"{info.CharacterName} ({info.Username})";
if (info.IdentityName != info.CharacterName)
displayName += $" [{info.IdentityName}]";
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
continue;
_sortedPlayerList.Add(info);
}
if (Comparison != null)
_sortedPlayerList.Sort((a, b) => Comparison(a, b));
PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList());
if (_selectedPlayer != null)
PlayerListContainer.Select(new PlayerListData(_selectedPlayer));
}
public void PopulateList(IReadOnlyList<PlayerInfo>? players = null)
{
players ??= _adminSystem.PlayerList;
_playerList = players.ToList();
if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer))
_selectedPlayer = null;
FilterList();
}
private string GetText(PlayerInfo info)
{
var text = $"{info.CharacterName} ({info.Username})";
if (OverrideText != null)
text = OverrideText.Invoke(info, text);
return text;
}
private void GenerateButton(ListData data, ListContainerButton button)
{
if (data is not PlayerListData { Info: var info })
return;
button.AddChild(new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
Children =
{
new Label
{
ClipText = true,
Text = GetText(info)
}
}
});
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
}
_entManager = IoCManager.Resolve<IEntityManager>();
_uiManager = IoCManager.Resolve<IUserInterfaceManager>();
_adminSystem = _entManager.System<AdminSystem>();
RobustXamlLoader.Load(this);
// Fill the Option data
PlayerListContainer.ItemPressed += PlayerListItemPressed;
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
PlayerListContainer.GenerateItem += GenerateButton;
PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
PopulateList(_adminSystem.PlayerList);
FilterLineEdit.OnTextChanged += _ => FilterList();
_adminSystem.PlayerListChanged += PopulateList;
BackgroundPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = new Color(32, 32, 40) };
}
public record PlayerListData(PlayerInfo Info) : ListData;
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList;
public event Action<PlayerInfo?>? OnSelectionChanged;
private void PlayerListNoItemSelected()
{
_selectedPlayer = null;
OnSelectionChanged?.Invoke(null);
}
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
return;
if (selectedPlayer == _selectedPlayer)
return;
if (args.Event.Function != EngineKeyFunctions.UIClick)
return;
OnSelectionChanged?.Invoke(selectedPlayer);
_selectedPlayer = selectedPlayer;
// update label text. Only required if there is some override (e.g. unread bwoink count).
if (OverrideText != null && args.Button.Children.FirstOrDefault()?.Children?.FirstOrDefault() is Label label)
label.Text = GetText(selectedPlayer);
}
private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
return;
if (args.Function != EngineKeyFunctions.UIRightClick || selectedPlayer.NetEntity == null)
return;
_uiManager.GetUIController<VerbMenuUIController>().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
args.Handle();
}
public void StopFiltering()
{
FilterLineEdit.Text = string.Empty;
}
private void FilterList()
{
_sortedPlayerList.Clear();
foreach (var info in _playerList)
{
var displayName = $"{info.CharacterName} ({info.Username})";
if (info.IdentityName != info.CharacterName)
displayName += $" [{info.IdentityName}]";
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
continue;
_sortedPlayerList.Add(info);
}
if (Comparison != null)
_sortedPlayerList.Sort((a, b) => Comparison(a, b));
// Ensure pinned players are always at the top
_sortedPlayerList.Sort((a, b) => a.IsPinned != b.IsPinned && a.IsPinned ? -1 : 1);
PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList());
if (_selectedPlayer != null)
PlayerListContainer.Select(new PlayerListData(_selectedPlayer));
}
public void PopulateList(IReadOnlyList<PlayerInfo>? players = null)
{
players ??= _adminSystem.PlayerList;
_playerList = players.ToList();
if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer))
_selectedPlayer = null;
FilterList();
}
private string GetText(PlayerInfo info)
{
var text = $"{info.CharacterName} ({info.Username})";
if (OverrideText != null)
text = OverrideText.Invoke(info, text);
return text;
}
private void GenerateButton(ListData data, ListContainerButton button)
{
if (data is not PlayerListData { Info: var info })
return;
var entry = new PlayerListEntry();
entry.Setup(info, OverrideText);
entry.OnPinStatusChanged += _ =>
{
FilterList();
};
button.AddChild(entry);
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
}
}
public record PlayerListData(PlayerInfo Info) : ListData;

View file

@ -0,0 +1,6 @@
<BoxContainer xmlns="https://spacestation14.io"
Orientation="Horizontal" HorizontalExpand="true">
<Label Name="PlayerEntryLabel" Text="" ClipText="True" HorizontalExpand="True" />
<TextureButton Name="PlayerEntryPinButton"
HorizontalAlignment="Right" />
</BoxContainer>

View file

@ -0,0 +1,58 @@
using Content.Client.Stylesheets;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
namespace Content.Client.Administration.UI.CustomControls;
[GenerateTypedNameReferences]
public sealed partial class PlayerListEntry : BoxContainer
{
public PlayerListEntry()
{
RobustXamlLoader.Load(this);
}
public event Action<PlayerInfo>? OnPinStatusChanged;
public void Setup(PlayerInfo info, Func<PlayerInfo, string, string>? overrideText)
{
Update(info, overrideText);
PlayerEntryPinButton.OnPressed += HandlePinButtonPressed(info);
}
private Action<BaseButton.ButtonEventArgs> HandlePinButtonPressed(PlayerInfo info)
{
return args =>
{
info.IsPinned = !info.IsPinned;
UpdatePinButtonTexture(info.IsPinned);
OnPinStatusChanged?.Invoke(info);
};
}
private void Update(PlayerInfo info, Func<PlayerInfo, string, string>? overrideText)
{
PlayerEntryLabel.Text = overrideText?.Invoke(info, $"{info.CharacterName} ({info.Username})") ??
$"{info.CharacterName} ({info.Username})";
UpdatePinButtonTexture(info.IsPinned);
}
private void UpdatePinButtonTexture(bool isPinned)
{
if (isPinned)
{
PlayerEntryPinButton?.RemoveStyleClass(StyleNano.StyleClassPinButtonUnpinned);
PlayerEntryPinButton?.AddStyleClass(StyleNano.StyleClassPinButtonPinned);
}
else
{
PlayerEntryPinButton?.RemoveStyleClass(StyleNano.StyleClassPinButtonPinned);
PlayerEntryPinButton?.AddStyleClass(StyleNano.StyleClassPinButtonUnpinned);
}
}
}

View file

@ -22,21 +22,29 @@ public sealed class LatheSystem : SharedLatheSystem
if (args.Sprite == null)
return;
// Lathe specific stuff
if (_appearance.TryGetData<bool>(uid, LatheVisuals.IsRunning, out var isRunning, args.Component))
{
if (args.Sprite.LayerMapTryGet(LatheVisualLayers.IsRunning, out var runningLayer) &&
component.RunningState != null &&
component.IdleState != null)
{
var state = isRunning ? component.RunningState : component.IdleState;
args.Sprite.LayerSetState(runningLayer, state);
}
}
if (_appearance.TryGetData<bool>(uid, PowerDeviceVisuals.Powered, out var powered, args.Component) &&
args.Sprite.LayerMapTryGet(PowerDeviceVisualLayers.Powered, out var powerLayer))
{
args.Sprite.LayerSetVisible(powerLayer, powered);
}
// Lathe specific stuff
if (_appearance.TryGetData<bool>(uid, LatheVisuals.IsRunning, out var isRunning, args.Component) &&
args.Sprite.LayerMapTryGet(LatheVisualLayers.IsRunning, out var runningLayer) &&
component.RunningState != null &&
component.IdleState != null)
{
var state = isRunning ? component.RunningState : component.IdleState;
args.Sprite.LayerSetAnimationTime(runningLayer, 0f);
args.Sprite.LayerSetState(runningLayer, state);
if (component.UnlitIdleState != null &&
component.UnlitRunningState != null)
{
var state = isRunning ? component.UnlitRunningState : component.UnlitIdleState;
args.Sprite.LayerSetState(powerLayer, state);
}
}
}

View file

@ -0,0 +1,35 @@
using Content.Shared.Paper;
using Robust.Client.GameObjects;
namespace Content.Client.Paper;
public sealed class EnvelopeSystem : VisualizerSystem<EnvelopeComponent>
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<EnvelopeComponent, AfterAutoHandleStateEvent>(OnAfterAutoHandleState);
}
private void OnAfterAutoHandleState(Entity<EnvelopeComponent> ent, ref AfterAutoHandleStateEvent args)
{
UpdateAppearance(ent);
}
private void UpdateAppearance(Entity<EnvelopeComponent> ent, SpriteComponent? sprite = null)
{
if (!Resolve(ent.Owner, ref sprite))
return;
sprite.LayerSetVisible(EnvelopeVisualLayers.Open, ent.Comp.State == EnvelopeComponent.EnvelopeState.Open);
sprite.LayerSetVisible(EnvelopeVisualLayers.Sealed, ent.Comp.State == EnvelopeComponent.EnvelopeState.Sealed);
sprite.LayerSetVisible(EnvelopeVisualLayers.Torn, ent.Comp.State == EnvelopeComponent.EnvelopeState.Torn);
}
public enum EnvelopeVisualLayers : byte
{
Open,
Sealed,
Torn
}
}

View file

@ -3,6 +3,7 @@ using Content.Client.Message;
using Content.Client.Resources;
using Content.Client.UserInterface.Controls;
using Content.Shared.Singularity.Components;
using Content.Shared.Access.Systems;
using Robust.Client.Animations;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
@ -13,6 +14,7 @@ using Robust.Client.UserInterface.XAML;
using Robust.Shared.Noise;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Client.Player;
namespace Content.Client.ParticleAccelerator.UI;
@ -21,6 +23,11 @@ public sealed partial class ParticleAcceleratorControlMenu : FancyWindow
{
[Dependency] private readonly IResourceCache _cache = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _player = default!;
private readonly AccessReaderSystem _accessReader;
private readonly FastNoiseLite _drawNoiseGenerator;
private readonly Animation _alarmControlAnimation;
@ -44,6 +51,7 @@ public sealed partial class ParticleAcceleratorControlMenu : FancyWindow
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_accessReader = _entityManager.System<AccessReaderSystem>();
_drawNoiseGenerator = new();
_drawNoiseGenerator.SetFractalType(FastNoiseLite.FractalType.FBm);
_drawNoiseGenerator.SetFrequency(0.5f);
@ -150,7 +158,7 @@ public sealed partial class ParticleAcceleratorControlMenu : FancyWindow
private bool StrengthSpinBoxValid(int n)
{
return n >= 0 && n <= _maxStrength ;
return n >= 0 && n <= _maxStrength;
}
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
@ -201,13 +209,16 @@ public sealed partial class ParticleAcceleratorControlMenu : FancyWindow
private void UpdateUI(bool assembled, bool blocked, bool enabled, bool powerBlock)
{
bool hasAccess = _player.LocalSession?.AttachedEntity is {} player
&& _accessReader.IsAllowed(player, _entity);
OnButton.Pressed = enabled;
OffButton.Pressed = !enabled;
var cantUse = !assembled || blocked || powerBlock;
var cantUse = !assembled || blocked || powerBlock || !hasAccess;
OnButton.Disabled = cantUse;
OffButton.Disabled = cantUse;
ScanButton.Disabled = blocked;
ScanButton.Disabled = blocked || !hasAccess;
var cantChangeLevel = !assembled || blocked || !enabled || cantUse;
StateSpinBox.SetButtonDisabled(cantChangeLevel);

View file

@ -1,6 +1,7 @@
using Content.Client.Power.Components;
using Content.Client.Power.Components;
using Content.Shared.Power.Components;
using Content.Shared.Power.EntitySystems;
using Content.Shared.Examine;
using Robust.Shared.GameStates;
namespace Content.Client.Power.EntitySystems;
@ -10,9 +11,15 @@ public sealed class PowerReceiverSystem : SharedPowerReceiverSystem
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ApcPowerReceiverComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<ApcPowerReceiverComponent, ComponentHandleState>(OnHandleState);
}
private void OnExamined(Entity<ApcPowerReceiverComponent> ent, ref ExaminedEvent args)
{
args.PushMarkup(GetExamineText(ent.Comp.Powered));
}
private void OnHandleState(EntityUid uid, ApcPowerReceiverComponent component, ref ComponentHandleState args)
{
if (args.Current is not ApcPowerReceiverComponentState state)

View file

@ -102,12 +102,21 @@ public sealed class SalvageExpeditionConsoleBoundUserInterface : BoundUserInterf
offering.AddContent(new Label
{
Text = faction,
Text = string.IsNullOrWhiteSpace(Loc.GetString(_protoManager.Index<SalvageFactionPrototype>(faction).Description))
? LogAndReturnDefaultFactionDescription(faction)
: Loc.GetString(_protoManager.Index<SalvageFactionPrototype>(faction).Description),
FontColorOverride = StyleNano.NanoGold,
HorizontalAlignment = Control.HAlignment.Left,
Margin = new Thickness(0f, 0f, 0f, 5f),
});
string LogAndReturnDefaultFactionDescription(string faction)
{
Logger.Error($"Description is null or white space for SalvageFactionPrototype: {faction}");
return Loc.GetString(_protoManager.Index<SalvageFactionPrototype>(faction).ID);
}
// Duration
offering.AddContent(new Label
{
@ -132,12 +141,20 @@ public sealed class SalvageExpeditionConsoleBoundUserInterface : BoundUserInterf
offering.AddContent(new Label
{
Text = Loc.GetString(_protoManager.Index<SalvageBiomeModPrototype>(biome).ID),
Text = string.IsNullOrWhiteSpace(Loc.GetString(_protoManager.Index<SalvageBiomeModPrototype>(biome).Description))
? LogAndReturnDefaultBiomDescription(biome)
: Loc.GetString(_protoManager.Index<SalvageBiomeModPrototype>(biome).Description),
FontColorOverride = StyleNano.NanoGold,
HorizontalAlignment = Control.HAlignment.Left,
Margin = new Thickness(0f, 0f, 0f, 5f),
});
string LogAndReturnDefaultBiomDescription(string biome)
{
Logger.Error($"Description is null or white space for SalvageBiomeModPrototype: {biome}");
return Loc.GetString(_protoManager.Index<SalvageBiomeModPrototype>(biome).ID);
}
// Modifiers
offering.AddContent(new Label
{

View file

@ -100,6 +100,8 @@ namespace Content.Client.Singularity
/// </summary>
private void OnProjectFromScreenToMap(ref PixelToMapEvent args)
{ // Mostly copypasta from the singularity shader.
if (args.Viewport.Eye == null)
return;
var maxDistance = MaxDistance * EyeManager.PixelsPerMeter;
var finalCoords = args.VisiblePosition;
@ -112,10 +114,11 @@ namespace Content.Client.Singularity
// and in local space 'Y' is measured in pixels from the top of the viewport.
// As a minor optimization the locations of the singularities are transformed into fragment space in BeforeDraw so the shader doesn't need to.
// We need to undo that here or this will transform the cursor position as if the singularities were mirrored vertically relative to the center of the viewport.
var localPosition = _positions[i];
localPosition.Y = args.Viewport.Size.Y - localPosition.Y;
var delta = args.VisiblePosition - localPosition;
var distance = (delta / args.Viewport.RenderScale).Length();
var distance = (delta / (args.Viewport.RenderScale * args.Viewport.Eye.Scale)).Length();
var deformation = _intensities[i] / MathF.Pow(distance, _falloffPowers[i]);

View file

@ -151,6 +151,11 @@ namespace Content.Client.Stylesheets
public static readonly Color ChatBackgroundColor = Color.FromHex("#25252ADD");
//Bwoink
public const string StyleClassPinButtonPinned = "pinButtonPinned";
public const string StyleClassPinButtonUnpinned = "pinButtonUnpinned";
public override Stylesheet Stylesheet { get; }
public StyleNano(IResourceCache resCache) : base(resCache)
@ -1608,6 +1613,21 @@ namespace Content.Client.Stylesheets
{
BackgroundColor = FancyTreeSelectedRowColor,
}),
// Pinned button style
new StyleRule(
new SelectorElement(typeof(TextureButton), new[] { StyleClassPinButtonPinned }, null, null),
new[]
{
new StyleProperty(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/Interface/Bwoink/pinned.png"))
}),
// Unpinned button style
new StyleRule(
new SelectorElement(typeof(TextureButton), new[] { StyleClassPinButtonUnpinned }, null, null),
new[]
{
new StyleProperty(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/Interface/Bwoink/un_pinned.png"))
})
}).ToList());
}
}

View file

@ -45,6 +45,10 @@ public sealed class NPCTest
var count = counts.GetOrNew(compound.ID);
count++;
// Compound tasks marked with AllowRecursion are only evaluated once
if (counts.ContainsKey(compound.ID) && compound.AllowRecursion)
continue;
Assert.That(count, Is.LessThan(50));
counts[compound.ID] = count;
Count(protoManager.Index<HTNCompoundPrototype>(compoundTask.Task), counts, htnSystem, protoManager);

View file

@ -25,7 +25,7 @@ public sealed partial class LogWireAction : ComponentWireAction<AccessReaderComp
return comp.LoggingDisabled ? StatusLightState.Off : StatusLightState.On;
}
public override object StatusKey => AccessWireActionKey.Status;
public override object StatusKey => LogWireActionKey.Status;
public override void Initialize()
{

View file

@ -361,9 +361,9 @@ public sealed partial class AdminVerbSystem
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Species/Human/organs.rsi"), "stomach"),
Act = () =>
{
foreach (var (component, _) in _bodySystem.GetBodyOrganComponents<StomachComponent>(args.Target, body))
foreach (var entity in _bodySystem.GetBodyOrganEntityComps<StomachComponent>((args.Target, body)))
{
QueueDel(component.Owner);
QueueDel(entity.Owner);
}
_popupSystem.PopupEntity(Loc.GetString("admin-smite-stomach-removal-self"), args.Target,
@ -381,9 +381,9 @@ public sealed partial class AdminVerbSystem
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Species/Human/organs.rsi"), "lung-r"),
Act = () =>
{
foreach (var (component, _) in _bodySystem.GetBodyOrganComponents<LungComponent>(args.Target, body))
foreach (var entity in _bodySystem.GetBodyOrganEntityComps<LungComponent>((args.Target, body)))
{
QueueDel(component.Owner);
QueueDel(entity.Owner);
}
_popupSystem.PopupEntity(Loc.GetString("admin-smite-lung-removal-self"), args.Target,

View file

@ -7,11 +7,13 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Content.Server.Administration.Managers;
using Content.Server.Afk;
using Content.Server.Database;
using Content.Server.Discord;
using Content.Server.GameTicking;
using Content.Server.Players.RateLimiting;
using Content.Shared.Administration;
using Content.Shared.CCVar;
using Content.Shared.GameTicking;
using Content.Shared.Mind;
using JetBrains.Annotations;
using Robust.Server.Player;
@ -39,6 +41,7 @@ namespace Content.Server.Administration.Systems
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly SharedMindSystem _minds = default!;
[Dependency] private readonly IAfkManager _afkManager = default!;
[Dependency] private readonly IServerDbManager _dbManager = default!;
[Dependency] private readonly PlayerRateLimitManager _rateLimit = default!;
private ISharedSponsorsManager? _sponsorsManager; // Sunrise-Sponsors
@ -52,7 +55,11 @@ namespace Content.Server.Administration.Systems
private string _footerIconUrl = string.Empty;
private string _avatarUrl = string.Empty;
private string _serverName = string.Empty;
private readonly Dictionary<NetUserId, (string? id, string username, string description, string? characterName, GameRunLevel lastRunLevel)> _relayMessages = new();
private readonly
Dictionary<NetUserId, (string? id, string username, string description, string? characterName, GameRunLevel
lastRunLevel)> _relayMessages = new();
private Dictionary<NetUserId, string> _oldMessageIds = new();
private readonly Dictionary<NetUserId, Queue<string>> _messageQueues = new();
private readonly HashSet<NetUserId> _processingChannels = new();
@ -71,6 +78,7 @@ namespace Content.Server.Administration.Systems
private const string TooLongText = "... **(too long)**";
private int _maxAdditionalChars;
private readonly Dictionary<NetUserId, DateTime> _activeConversations = new();
public override void Initialize()
{
@ -81,13 +89,22 @@ namespace Content.Server.Administration.Systems
Subs.CVar(_config, CVars.GameHostName, OnServerNameChanged, true);
Subs.CVar(_config, CCVars.AdminAhelpOverrideClientName, OnOverrideChanged, true);
_sawmill = IoCManager.Resolve<ILogManager>().GetSawmill("AHELP");
_maxAdditionalChars = GenerateAHelpMessage("", "", true, _gameTicker.RoundDuration().ToString("hh\\:mm\\:ss"), _gameTicker.RunLevel, playedSound: false).Length;
var defaultParams = new AHelpMessageParams(
string.Empty,
string.Empty,
true,
_gameTicker.RoundDuration().ToString("hh\\:mm\\:ss"),
_gameTicker.RunLevel,
playedSound: false
);
_maxAdditionalChars = GenerateAHelpMessage(defaultParams).Length;
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
SubscribeLocalEvent<GameRunLevelChangedEvent>(OnGameRunLevelChanged);
SubscribeNetworkEvent<BwoinkClientTypingUpdated>(OnClientTypingUpdated);
SubscribeLocalEvent<RoundRestartCleanupEvent>(_ => _activeConversations.Clear());
_rateLimit.Register(
_rateLimit.Register(
RateLimitKey,
new RateLimitRegistration
{
@ -111,14 +128,129 @@ namespace Content.Server.Administration.Systems
_overrideClientName = obj;
}
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
private async void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
{
if (e.NewStatus == SessionStatus.Disconnected)
{
if (_activeConversations.TryGetValue(e.Session.UserId, out var lastMessageTime))
{
var timeSinceLastMessage = DateTime.Now - lastMessageTime;
if (timeSinceLastMessage > TimeSpan.FromMinutes(5))
{
_activeConversations.Remove(e.Session.UserId);
return; // Do not send disconnect message if timeout exceeded
}
}
// Check if the user has been banned
var ban = await _dbManager.GetServerBanAsync(null, e.Session.UserId, null);
if (ban != null)
{
var banMessage = Loc.GetString("bwoink-system-player-banned", ("banReason", ban.Reason));
NotifyAdmins(e.Session, banMessage, PlayerStatusType.Banned);
_activeConversations.Remove(e.Session.UserId);
return;
}
}
// Notify all admins if a player disconnects or reconnects
var message = e.NewStatus switch
{
SessionStatus.Connected => Loc.GetString("bwoink-system-player-reconnecting"),
SessionStatus.Disconnected => Loc.GetString("bwoink-system-player-disconnecting"),
_ => null
};
if (message != null)
{
var statusType = e.NewStatus == SessionStatus.Connected
? PlayerStatusType.Connected
: PlayerStatusType.Disconnected;
NotifyAdmins(e.Session, message, statusType);
}
if (e.NewStatus != SessionStatus.InGame)
return;
RaiseNetworkEvent(new BwoinkDiscordRelayUpdated(!string.IsNullOrWhiteSpace(_webhookUrl)), e.Session);
}
private void NotifyAdmins(ICommonSession session, string message, PlayerStatusType statusType)
{
if (!_activeConversations.ContainsKey(session.UserId))
{
// If the user is not part of an active conversation, do not notify admins.
return;
}
// Get the current timestamp
var timestamp = DateTime.Now.ToString("HH:mm:ss");
var roundTime = _gameTicker.RoundDuration().ToString("hh\\:mm\\:ss");
// Determine the icon based on the status type
string icon = statusType switch
{
PlayerStatusType.Connected => ":green_circle:",
PlayerStatusType.Disconnected => ":red_circle:",
PlayerStatusType.Banned => ":no_entry:",
_ => ":question:"
};
// Create the message parameters for Discord
var messageParams = new AHelpMessageParams(
session.Name,
message,
true,
roundTime,
_gameTicker.RunLevel,
playedSound: true,
icon: icon
);
// Create the message for in-game with username
var color = statusType switch
{
PlayerStatusType.Connected => Color.Green.ToHex(),
PlayerStatusType.Disconnected => Color.Yellow.ToHex(),
PlayerStatusType.Banned => Color.Orange.ToHex(),
_ => Color.Gray.ToHex(),
};
var inGameMessage = $"[color={color}]{session.Name} {message}[/color]";
var bwoinkMessage = new BwoinkTextMessage(
userId: session.UserId,
trueSender: SystemUserId,
text: inGameMessage,
sentAt: DateTime.Now,
playSound: false
);
var admins = GetTargetAdmins();
foreach (var admin in admins)
{
RaiseNetworkEvent(bwoinkMessage, admin);
}
// Enqueue the message for Discord relay
if (_webhookUrl != string.Empty)
{
// if (!_messageQueues.ContainsKey(session.UserId))
// _messageQueues[session.UserId] = new Queue<string>();
//
// var escapedText = FormattedMessage.EscapeText(message);
// messageParams.Message = escapedText;
//
// var discordMessage = GenerateAHelpMessage(messageParams);
// _messageQueues[session.UserId].Enqueue(discordMessage);
var queue = _messageQueues.GetOrNew(session.UserId);
var escapedText = FormattedMessage.EscapeText(message);
messageParams.Message = escapedText;
var discordMessage = GenerateAHelpMessage(messageParams);
queue.Enqueue(discordMessage);
}
}
private void OnGameRunLevelChanged(GameRunLevelChangedEvent args)
{
// Don't make a new embed if we
@ -213,7 +345,8 @@ namespace Content.Server.Administration.Systems
var content = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
_sawmill.Log(LogLevel.Error, $"Discord returned bad status code when trying to get webhook data (perhaps the webhook URL is invalid?): {response.StatusCode}\nResponse: {content}");
_sawmill.Log(LogLevel.Error,
$"Discord returned bad status code when trying to get webhook data (perhaps the webhook URL is invalid?): {response.StatusCode}\nResponse: {content}");
return;
}
@ -237,7 +370,7 @@ namespace Content.Server.Administration.Systems
// Whether the message will become too long after adding these new messages
var tooLong = exists && messages.Sum(msg => Math.Min(msg.Length, MessageLengthCap) + "\n".Length)
+ existingEmbed.description.Length > DescriptionMax;
+ existingEmbed.description.Length > DescriptionMax;
// If there is no existing embed, or it is getting too long, we create a new embed
if (!exists || tooLong)
@ -246,7 +379,8 @@ namespace Content.Server.Administration.Systems
if (lookup == null)
{
_sawmill.Log(LogLevel.Error, $"Unable to find player for NetUserId {userId} when sending discord webhook.");
_sawmill.Log(LogLevel.Error,
$"Unable to find player for NetUserId {userId} when sending discord webhook.");
_relayMessages.Remove(userId);
return;
}
@ -258,11 +392,13 @@ namespace Content.Server.Administration.Systems
{
if (tooLong && existingEmbed.id != null)
{
linkToPrevious = $"**[Go to previous embed of this round](https://discord.com/channels/{guildId}/{channelId}/{existingEmbed.id})**\n";
linkToPrevious =
$"**[Go to previous embed of this round](https://discord.com/channels/{guildId}/{channelId}/{existingEmbed.id})**\n";
}
else if (_oldMessageIds.TryGetValue(userId, out var id) && !string.IsNullOrEmpty(id))
{
linkToPrevious = $"**[Go to last round's conversation with this player](https://discord.com/channels/{guildId}/{channelId}/{id})**\n";
linkToPrevious =
$"**[Go to last round's conversation with this player](https://discord.com/channels/{guildId}/{channelId}/{id})**\n";
}
}
@ -278,7 +414,8 @@ namespace Content.Server.Administration.Systems
GameRunLevel.PreRoundLobby => "\n\n:arrow_forward: _**Pre-round lobby started**_\n",
GameRunLevel.InRound => "\n\n:arrow_forward: _**Round started**_\n",
GameRunLevel.PostRound => "\n\n:stop_button: _**Post-round started**_\n",
_ => throw new ArgumentOutOfRangeException(nameof(_gameTicker.RunLevel), $"{_gameTicker.RunLevel} was not matched."),
_ => throw new ArgumentOutOfRangeException(nameof(_gameTicker.RunLevel),
$"{_gameTicker.RunLevel} was not matched."),
};
existingEmbed.lastRunLevel = _gameTicker.RunLevel;
@ -294,7 +431,9 @@ namespace Content.Server.Administration.Systems
existingEmbed.description += $"\n{message}";
}
var payload = GeneratePayload(existingEmbed.description, existingEmbed.username, existingEmbed.characterName);
var payload = GeneratePayload(existingEmbed.description,
existingEmbed.username,
existingEmbed.characterName);
// If there is no existing embed, create a new one
// Otherwise patch (edit) it
@ -306,7 +445,8 @@ namespace Content.Server.Administration.Systems
var content = await request.Content.ReadAsStringAsync();
if (!request.IsSuccessStatusCode)
{
_sawmill.Log(LogLevel.Error, $"Discord returned bad status code when posting message (perhaps the message is too long?): {request.StatusCode}\nResponse: {content}");
_sawmill.Log(LogLevel.Error,
$"Discord returned bad status code when posting message (perhaps the message is too long?): {request.StatusCode}\nResponse: {content}");
_relayMessages.Remove(userId);
return;
}
@ -314,7 +454,8 @@ namespace Content.Server.Administration.Systems
var id = JsonNode.Parse(content)?["id"];
if (id == null)
{
_sawmill.Log(LogLevel.Error, $"Could not find id in json-content returned from discord webhook: {content}");
_sawmill.Log(LogLevel.Error,
$"Could not find id in json-content returned from discord webhook: {content}");
_relayMessages.Remove(userId);
return;
}
@ -329,7 +470,8 @@ namespace Content.Server.Administration.Systems
if (!request.IsSuccessStatusCode)
{
var content = await request.Content.ReadAsStringAsync();
_sawmill.Log(LogLevel.Error, $"Discord returned bad status code when patching message (perhaps the message is too long?): {request.StatusCode}\nResponse: {content}");
_sawmill.Log(LogLevel.Error,
$"Discord returned bad status code when patching message (perhaps the message is too long?): {request.StatusCode}\nResponse: {content}");
_relayMessages.Remove(userId);
return;
}
@ -359,7 +501,8 @@ namespace Content.Server.Administration.Systems
: $"pre-round lobby for round {_gameTicker.RoundId + 1}",
GameRunLevel.InRound => $"round {_gameTicker.RoundId}",
GameRunLevel.PostRound => $"post-round {_gameTicker.RoundId}",
_ => throw new ArgumentOutOfRangeException(nameof(_gameTicker.RunLevel), $"{_gameTicker.RunLevel} was not matched."),
_ => throw new ArgumentOutOfRangeException(nameof(_gameTicker.RunLevel),
$"{_gameTicker.RunLevel} was not matched."),
};
return new WebhookPayload
@ -405,6 +548,7 @@ namespace Content.Server.Administration.Systems
protected override void OnBwoinkTextMessage(BwoinkTextMessage message, EntitySessionEventArgs eventArgs)
{
base.OnBwoinkTextMessage(message, eventArgs);
_activeConversations[message.UserId] = DateTime.Now;
var senderSession = eventArgs.SenderSession;
// TODO: Sanitize text?
@ -427,7 +571,9 @@ namespace Content.Server.Administration.Systems
// Sunrise-Sponsors-Start
string bwoinkText;
if (senderAdmin is not null && senderAdmin.Flags == AdminFlags.Adminhelp) // Mentor. Not full admin. That's why it's colored differently.
if (senderAdmin is not null &&
senderAdmin.Flags ==
AdminFlags.Adminhelp) // Mentor. Not full admin. That's why it's colored differently.
{
bwoinkText = $"[color=purple]\\[{senderAdmin.Title}\\]{senderSession.Name}[/color]";
}
@ -481,7 +627,9 @@ namespace Content.Server.Administration.Systems
{
string overrideMsgText;
// Doing the same thing as above, but with the override name. Theres probably a better way to do this.
if (senderAdmin is not null && senderAdmin.Flags == AdminFlags.Adminhelp) // Mentor. Not full admin. That's why it's colored differently.
if (senderAdmin is not null &&
senderAdmin.Flags ==
AdminFlags.Adminhelp) // Mentor. Not full admin. That's why it's colored differently.
{
overrideMsgText = $"[color=purple]{_overrideClientName}[/color]";
}
@ -496,7 +644,11 @@ namespace Content.Server.Administration.Systems
overrideMsgText = $"{(message.PlaySound ? "" : "(S) ")}{overrideMsgText}: {escapedText}";
RaiseNetworkEvent(new BwoinkTextMessage(message.UserId, senderSession.UserId, overrideMsgText, playSound: playSound), session.Channel);
RaiseNetworkEvent(new BwoinkTextMessage(message.UserId,
senderSession.UserId,
overrideMsgText,
playSound: playSound),
session.Channel);
}
else
RaiseNetworkEvent(msg, session.Channel);
@ -516,8 +668,18 @@ namespace Content.Server.Administration.Systems
{
str = str[..(DescriptionMax - _maxAdditionalChars - unameLength)];
}
var nonAfkAdmins = GetNonAfkAdmins();
_messageQueues[msg.UserId].Enqueue(GenerateAHelpMessage(senderSession.Name, str, !personalChannel, _gameTicker.RoundDuration().ToString("hh\\:mm\\:ss"), _gameTicker.RunLevel, playedSound: playSound, noReceivers: nonAfkAdmins.Count == 0));
var messageParams = new AHelpMessageParams(
senderSession.Name,
str,
!personalChannel,
_gameTicker.RoundDuration().ToString("hh\\:mm\\:ss"),
_gameTicker.RunLevel,
playedSound: playSound,
noReceivers: nonAfkAdmins.Count == 0
);
_messageQueues[msg.UserId].Enqueue(GenerateAHelpMessage(messageParams));
}
if (admins.Count != 0 || sendsWebhook)
@ -532,7 +694,8 @@ namespace Content.Server.Administration.Systems
private IList<INetChannel> GetNonAfkAdmins()
{
return _adminManager.ActiveAdmins
.Where(p => (_adminManager.GetAdminData(p)?.HasFlag(AdminFlags.Adminhelp) ?? false) && !_afkManager.IsAfk(p))
.Where(p => (_adminManager.GetAdminData(p)?.HasFlag(AdminFlags.Adminhelp) ?? false) &&
!_afkManager.IsAfk(p))
.Select(p => p.Channel)
.ToList();
}
@ -546,25 +709,69 @@ namespace Content.Server.Administration.Systems
.ToList();
}
private static string GenerateAHelpMessage(string username, string message, bool admin, string roundTime, GameRunLevel roundState, bool playedSound, bool noReceivers = false)
private static string GenerateAHelpMessage(AHelpMessageParams parameters)
{
var stringbuilder = new StringBuilder();
if (admin)
if (parameters.Icon != null)
stringbuilder.Append(parameters.Icon);
else if (parameters.IsAdmin)
stringbuilder.Append(":outbox_tray:");
else if (noReceivers)
else if (parameters.NoReceivers)
stringbuilder.Append(":sos:");
else
stringbuilder.Append(":inbox_tray:");
if(roundTime != string.Empty && roundState == GameRunLevel.InRound)
stringbuilder.Append($" **{roundTime}**");
if (!playedSound)
if (parameters.RoundTime != string.Empty && parameters.RoundState == GameRunLevel.InRound)
stringbuilder.Append($" **{parameters.RoundTime}**");
if (!parameters.PlayedSound)
stringbuilder.Append(" **(S)**");
stringbuilder.Append($" **{username}:** ");
stringbuilder.Append(message);
if (parameters.Icon == null)
stringbuilder.Append($" **{parameters.Username}:** ");
else
stringbuilder.Append($" **{parameters.Username}** ");
stringbuilder.Append(parameters.Message);
return stringbuilder.ToString();
}
}
}
public sealed class AHelpMessageParams
{
public string Username { get; set; }
public string Message { get; set; }
public bool IsAdmin { get; set; }
public string RoundTime { get; set; }
public GameRunLevel RoundState { get; set; }
public bool PlayedSound { get; set; }
public bool NoReceivers { get; set; }
public string? Icon { get; set; }
public AHelpMessageParams(
string username,
string message,
bool isAdmin,
string roundTime,
GameRunLevel roundState,
bool playedSound,
bool noReceivers = false,
string? icon = null)
{
Username = username;
Message = message;
IsAdmin = isAdmin;
RoundTime = roundTime;
RoundState = roundState;
PlayedSound = playedSound;
NoReceivers = noReceivers;
Icon = icon;
}
}
public enum PlayerStatusType
{
Connected,
Disconnected,
Banned,
}
}

View file

@ -390,7 +390,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
}
_mind.TransferTo(curMind.Value, antagEnt, ghostCheckOverride: true);
_role.MindAddRoles(curMind.Value, def.MindComponents);
_role.MindAddRoles(curMind.Value, def.MindComponents, null, true);
ent.Comp.SelectedMinds.Add((curMind.Value, Name(player)));
SendBriefing(session, def.Briefing);
}

View file

@ -177,6 +177,10 @@ namespace Content.Server.Cargo.Systems
RaiseLocalEvent(ref ev);
ev.FulfillmentEntity ??= station.Value;
_idCardSystem.TryFindIdCard(player, out var idCard);
// ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
order.SetApproverData(idCard.Comp?.FullName, idCard.Comp?.JobTitle);
if (!ev.Handled)
{
ev.FulfillmentEntity = TryFulfillOrder((station.Value, stationData), order, orderDatabase);
@ -189,18 +193,13 @@ namespace Content.Server.Cargo.Systems
}
}
_idCardSystem.TryFindIdCard(player, out var idCard);
// ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
order.SetApproverData(idCard.Comp?.FullName, idCard.Comp?.JobTitle);
order.Approved = true;
_audio.PlayPvs(component.ConfirmSound, uid);
var approverName = idCard.Comp?.FullName ?? Loc.GetString("access-reader-unknown-id");
var approverJob = idCard.Comp?.JobTitle ?? Loc.GetString("access-reader-unknown-id");
var message = Loc.GetString("cargo-console-unlock-approved-order-broadcast",
("productName", Loc.GetString(order.ProductName)),
("orderAmount", order.OrderQuantity),
("approverName", approverName),
("approverJob", approverJob),
("approver", order.Approver ?? string.Empty),
("cost", cost));
_radio.SendRadioMessage(uid, message, component.AnnouncementChannel, uid, escapeMarkup: false);
ConsolePopup(args.Actor, Loc.GetString("cargo-console-trade-station", ("destination", MetaData(ev.FulfillmentEntity.Value).EntityName)));
@ -421,6 +420,7 @@ namespace Content.Server.Cargo.Systems
// Approve it now
order.SetApproverData(dest, sender);
order.Approved = true;
// Log order addition
_adminLogger.Add(LogType.Action, LogImpact.Low,

View file

@ -25,7 +25,6 @@ using Content.Shared.Interaction;
using Content.Shared.Item;
using Content.Shared.Movement.Events;
using Content.Shared.Popups;
using Content.Shared.Throwing;
using Content.Shared.Verbs;
using Robust.Server.Audio;
using Robust.Server.GameObjects;
@ -35,7 +34,6 @@ using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Events;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Utility;
namespace Content.Server.Disposal.Unit.EntitySystems;
@ -331,12 +329,13 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
{
var currentTime = GameTiming.CurTime;
if (!_actionBlockerSystem.CanMove(args.Entity))
return;
if (!TryComp(args.Entity, out HandsComponent? hands) ||
hands.Count == 0 ||
currentTime < component.LastExitAttempt + ExitAttemptDelay)
{
return;
}
component.LastExitAttempt = currentTime;
Remove(uid, component, args.Entity);

View file

@ -4,8 +4,12 @@ using Content.Server.Humanoid;
using Content.Shared.DoAfter;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Inventory;
using Content.Shared.MagicMirror;
using Content.Shared.Popups;
using Content.Shared.Tag;
using Robust.Shared.Audio.Systems;
namespace Content.Server.MagicMirror;
@ -19,6 +23,9 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly MarkingManager _markings = default!;
[Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
public override void Initialize()
{
@ -46,9 +53,26 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
if (component.Target is not { } target)
return;
// Check if the target getting their hair altered has any clothes that hides their hair
if (CheckHeadSlotOrClothes(message.Actor, component.Target.Value))
{
_popup.PopupEntity(
component.Target == message.Actor
? Loc.GetString("magic-mirror-blocked-by-hat-self")
: Loc.GetString("magic-mirror-blocked-by-hat-self-target"),
message.Actor,
message.Actor,
PopupType.Medium);
return;
}
_doAfterSystem.Cancel(component.DoAfter);
component.DoAfter = null;
var doafterTime = component.SelectSlotTime;
if (component.Target == message.Actor)
doafterTime /= 3;
var doAfter = new MagicMirrorSelectDoAfterEvent()
{
Category = message.Category,
@ -56,7 +80,7 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
Marking = message.Marking,
};
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Actor, component.SelectSlotTime, doAfter, uid, target: target, used: uid)
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Actor, doafterTime, doAfter, uid, target: target, used: uid)
{
DistanceThreshold = SharedInteractionSystem.InteractionRange,
BreakOnDamage = true,
@ -66,6 +90,15 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
},
out var doAfterId);
if (component.Target == message.Actor)
{
_popup.PopupEntity(Loc.GetString("magic-mirror-change-slot-self"), component.Target.Value, component.Target.Value, PopupType.Medium);
}
else
{
_popup.PopupEntity(Loc.GetString("magic-mirror-change-slot-target", ("user", Identity.Name(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium);
}
component.DoAfter = doAfterId;
_audio.PlayPvs(component.ChangeHairSound, uid);
}
@ -102,9 +135,26 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
if (component.Target is not { } target)
return;
// Check if the target getting their hair altered has any clothes that hides their hair
if (CheckHeadSlotOrClothes(message.Actor, component.Target.Value))
{
_popup.PopupEntity(
component.Target == message.Actor
? Loc.GetString("magic-mirror-blocked-by-hat-self")
: Loc.GetString("magic-mirror-blocked-by-hat-self-target"),
message.Actor,
message.Actor,
PopupType.Medium);
return;
}
_doAfterSystem.Cancel(component.DoAfter);
component.DoAfter = null;
var doafterTime = component.ChangeSlotTime;
if (component.Target == message.Actor)
doafterTime /= 3;
var doAfter = new MagicMirrorChangeColorDoAfterEvent()
{
Category = message.Category,
@ -112,7 +162,7 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
Colors = message.Colors,
};
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Actor, component.ChangeSlotTime, doAfter, uid, target: target, used: uid)
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Actor, doafterTime, doAfter, uid, target: target, used: uid)
{
BreakOnDamage = true,
BreakOnMove = true,
@ -121,6 +171,15 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
},
out var doAfterId);
if (component.Target == message.Actor)
{
_popup.PopupEntity(Loc.GetString("magic-mirror-change-color-self"), component.Target.Value, component.Target.Value, PopupType.Medium);
}
else
{
_popup.PopupEntity(Loc.GetString("magic-mirror-change-color-target", ("user", Identity.Name(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium);
}
component.DoAfter = doAfterId;
}
private void OnChangeColorDoAfter(EntityUid uid, MagicMirrorComponent component, MagicMirrorChangeColorDoAfterEvent args)
@ -156,16 +215,33 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
if (component.Target is not { } target)
return;
// Check if the target getting their hair altered has any clothes that hides their hair
if (CheckHeadSlotOrClothes(message.Actor, component.Target.Value))
{
_popup.PopupEntity(
component.Target == message.Actor
? Loc.GetString("magic-mirror-blocked-by-hat-self")
: Loc.GetString("magic-mirror-blocked-by-hat-self-target"),
message.Actor,
message.Actor,
PopupType.Medium);
return;
}
_doAfterSystem.Cancel(component.DoAfter);
component.DoAfter = null;
var doafterTime = component.RemoveSlotTime;
if (component.Target == message.Actor)
doafterTime /= 3;
var doAfter = new MagicMirrorRemoveSlotDoAfterEvent()
{
Category = message.Category,
Slot = message.Slot,
};
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Actor, component.RemoveSlotTime, doAfter, uid, target: target, used: uid)
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Actor, doafterTime, doAfter, uid, target: target, used: uid)
{
DistanceThreshold = SharedInteractionSystem.InteractionRange,
BreakOnDamage = true,
@ -174,6 +250,15 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
},
out var doAfterId);
if (component.Target == message.Actor)
{
_popup.PopupEntity(Loc.GetString("magic-mirror-remove-slot-self"), component.Target.Value, component.Target.Value, PopupType.Medium);
}
else
{
_popup.PopupEntity(Loc.GetString("magic-mirror-remove-slot-target", ("user", Identity.Name(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium);
}
component.DoAfter = doAfterId;
_audio.PlayPvs(component.ChangeHairSound, uid);
}
@ -210,15 +295,32 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
if (component.Target == null)
return;
// Check if the target getting their hair altered has any clothes that hides their hair
if (CheckHeadSlotOrClothes(message.Actor, component.Target.Value))
{
_popup.PopupEntity(
component.Target == message.Actor
? Loc.GetString("magic-mirror-blocked-by-hat-self")
: Loc.GetString("magic-mirror-blocked-by-hat-self-target"),
message.Actor,
message.Actor,
PopupType.Medium);
return;
}
_doAfterSystem.Cancel(component.DoAfter);
component.DoAfter = null;
var doafterTime = component.AddSlotTime;
if (component.Target == message.Actor)
doafterTime /= 3;
var doAfter = new MagicMirrorAddSlotDoAfterEvent()
{
Category = message.Category,
};
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Actor, component.AddSlotTime, doAfter, uid, target: component.Target.Value, used: uid)
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Actor, doafterTime, doAfter, uid, target: component.Target.Value, used: uid)
{
BreakOnDamage = true,
BreakOnMove = true,
@ -227,6 +329,15 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
},
out var doAfterId);
if (component.Target == message.Actor)
{
_popup.PopupEntity(Loc.GetString("magic-mirror-add-slot-self"), component.Target.Value, component.Target.Value, PopupType.Medium);
}
else
{
_popup.PopupEntity(Loc.GetString("magic-mirror-add-slot-target", ("user", Identity.Name(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium);
}
component.DoAfter = doAfterId;
_audio.PlayPvs(component.ChangeHairSound, uid);
}
@ -265,4 +376,32 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
ent.Comp.Target = null;
Dirty(ent);
}
/// <summary>
/// Helper function that checks if the wearer has anything on their head
/// Or if they have any clothes that hides their hair
/// </summary>
private bool CheckHeadSlotOrClothes(EntityUid user, EntityUid target)
{
if (TryComp<InventoryComponent>(target, out var inventoryComp))
{
// any hat whatsoever will block haircutting
if (_inventory.TryGetSlotEntity(target, "head", out var hat, inventoryComp))
{
return true;
}
// maybe there's some kind of armor that has the HidesHair tag as well, so check every slot for it
var slots = _inventory.GetSlotEnumerator((target, inventoryComp), SlotFlags.WITHOUT_POCKET);
while (slots.MoveNext(out var slot))
{
if (slot.ContainedEntity != null && _tagSystem.HasTag(slot.ContainedEntity.Value, "HidesHair"))
{
return true;
}
}
}
return false;
}
}

View file

@ -12,4 +12,10 @@ public sealed partial class HTNCompoundPrototype : IPrototype
[DataField("branches", required: true)]
public List<HTNBranch> Branches = new();
/// <summary>
/// Exclude this compound task from the CompoundRecursion integration test.
/// </summary>
[DataField]
public bool AllowRecursion = false;
}

View file

@ -63,8 +63,13 @@ public sealed class HTNPlanJob : Job<HTNPlan>
// How many primitive tasks we've added since last record.
var primitiveCount = 0;
int tasksProcessed = 0;
while (tasksToProcess.TryDequeue(out var currentTask))
{
if (tasksProcessed++ > _rootTask.MaximumTasks)
throw new Exception("HTN Planner exceeded maximum tasks");
switch (currentTask)
{
case HTNCompoundTask compound:

View file

@ -3,4 +3,10 @@ namespace Content.Server.NPC.HTN;
[ImplicitDataDefinitionForInheritors]
public abstract partial class HTNTask
{
/// <summary>
/// Limit the amount of tasks the planner considers. Exceeding this value sleeps the NPC and throws an exception.
/// The expected way to hit this limit is with badly written recursive tasks.
/// </summary>
[DataField]
public int MaximumTasks = 1000;
}

View file

@ -46,6 +46,11 @@ namespace Content.Server.Power.EntitySystems
_provQuery = GetEntityQuery<ApcPowerProviderComponent>();
}
private void OnExamined(Entity<ApcPowerReceiverComponent> ent, ref ExaminedEvent args)
{
args.PushMarkup(GetExamineText(ent.Comp.Powered));
}
private void OnGetVerbs(EntityUid uid, ApcPowerReceiverComponent component, GetVerbsEvent<Verb> args)
{
if (!_adminManager.HasAdminFlag(args.User, AdminFlags.Admin))
@ -61,17 +66,6 @@ namespace Content.Server.Power.EntitySystems
});
}
///<summary>
///Adds some markup to the examine text of whatever object is using this component to tell you if it's powered or not, even if it doesn't have an icon state to do this for you.
///</summary>
private void OnExamined(EntityUid uid, ApcPowerReceiverComponent component, ExaminedEvent args)
{
args.PushMarkup(Loc.GetString("power-receiver-component-on-examine-main",
("stateText", Loc.GetString( component.Powered
? "power-receiver-component-on-examine-powered"
: "power-receiver-component-on-examine-unpowered"))));
}
private void OnProviderShutdown(EntityUid uid, ApcPowerProviderComponent component, ComponentShutdown args)
{
foreach (var receiver in component.LinkedReceivers)

View file

@ -275,7 +275,6 @@ namespace Content.Server.Preferences.Managers
/// <summary>
/// Retrieves preferences for the given username from storage.
/// Creates and saves default preferences if they are not found, then returns them.
/// </summary>
public PlayerPreferences GetPreferences(NetUserId userId)
{
@ -290,7 +289,6 @@ namespace Content.Server.Preferences.Managers
/// <summary>
/// Retrieves preferences for the given username from storage or returns null.
/// Creates and saves default preferences if they are not found, then returns them.
/// </summary>
public PlayerPreferences? GetPreferencesOrNull(NetUserId? userId)
{

View file

@ -8,6 +8,7 @@ using Content.Shared.Popups;
using Content.Shared.Resist;
using Content.Shared.Tools.Components;
using Content.Shared.Tools.Systems;
using Content.Shared.ActionBlocker;
namespace Content.Server.Resist;
@ -18,6 +19,7 @@ public sealed class ResistLockerSystem : EntitySystem
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly WeldableSystem _weldable = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
public override void Initialize()
{
@ -34,6 +36,9 @@ public sealed class ResistLockerSystem : EntitySystem
if (!TryComp(uid, out EntityStorageComponent? storageComponent))
return;
if (!_actionBlocker.CanMove(args.Entity))
return;
if (TryComp<LockComponent>(uid, out var lockComponent) && lockComponent.Locked || _weldable.IsWelded(uid))
{
AttemptResist(args.Entity, uid, storageComponent, component);

View file

@ -26,6 +26,11 @@ public interface IGridSpawnGroup
/// </summary>
public float MinimumDistance { get; }
/// <summary>
/// Maximum distance to spawn away from the station.
/// </summary>
public float MaximumDistance { get; }
/// <inheritdoc />
public ProtoId<DatasetPrototype>? NameDataset { get; }
@ -67,6 +72,8 @@ public sealed class DungeonSpawnGroup : IGridSpawnGroup
/// <inheritdoc />
public float MinimumDistance { get; }
public float MaximumDistance { get; }
/// <inheritdoc />
public ProtoId<DatasetPrototype>? NameDataset { get; }
@ -94,7 +101,11 @@ public sealed class GridSpawnGroup : IGridSpawnGroup
{
public List<ResPath> Paths = new();
/// <inheritdoc />
public float MinimumDistance { get; }
/// <inheritdoc />
public float MaximumDistance { get; }
public ProtoId<DatasetPrototype>? NameDataset { get; }
public int MinCount { get; set; } = 1;
public int MaxCount { get; set; } = 1;

View file

@ -281,24 +281,24 @@ namespace Content.Server.Shuttles.Systems
{
if (_doorSystem.TryOpen(dockAUid, doorA))
{
doorA.ChangeAirtight = false;
if (TryComp<DoorBoltComponent>(dockAUid, out var airlockA))
{
_doorSystem.SetBoltsDown((dockAUid, airlockA), true);
}
}
doorA.ChangeAirtight = false;
}
if (TryComp(dockBUid, out DoorComponent? doorB))
{
if (_doorSystem.TryOpen(dockBUid, doorB))
{
doorB.ChangeAirtight = false;
if (TryComp<DoorBoltComponent>(dockBUid, out var airlockB))
{
_doorSystem.SetBoltsDown((dockBUid, airlockB), true);
}
}
doorB.ChangeAirtight = false;
}
if (_pathfinding.TryCreatePortal(dockAXform.Coordinates, dockBXform.Coordinates, out var handle))

View file

@ -10,6 +10,7 @@ using Content.Shared.Shuttles.Components;
using Content.Shared.Station.Components;
using Robust.Shared.Collections;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Random;
using Robust.Shared.Utility;
@ -86,9 +87,15 @@ public sealed partial class ShuttleSystem
_mapManager.DeleteMap(mapId);
}
private bool TryDungeonSpawn(EntityUid targetGrid, EntityUid stationUid, MapId mapId, DungeonSpawnGroup group, out EntityUid spawned)
private bool TryDungeonSpawn(Entity<MapGridComponent?> targetGrid, EntityUid stationUid, MapId mapId, DungeonSpawnGroup group, out EntityUid spawned)
{
spawned = EntityUid.Invalid;
if (!_gridQuery.Resolve(targetGrid.Owner, ref targetGrid.Comp))
{
return false;
}
var dungeonProtoId = _random.Pick(group.Protos);
if (!_protoManager.TryIndex(dungeonProtoId, out var dungeonProto))
@ -96,11 +103,13 @@ public sealed partial class ShuttleSystem
return false;
}
var spawnCoords = new EntityCoordinates(targetGrid, Vector2.Zero);
var targetPhysics = _physicsQuery.Comp(targetGrid);
var spawnCoords = new EntityCoordinates(targetGrid, targetPhysics.LocalCenter);
if (group.MinimumDistance > 0f)
{
spawnCoords = spawnCoords.Offset(_random.NextVector2(group.MinimumDistance, group.MinimumDistance * 1.5f));
var distancePadding = MathF.Max(targetGrid.Comp.LocalAABB.Width, targetGrid.Comp.LocalAABB.Height);
spawnCoords = spawnCoords.Offset(_random.NextVector2(distancePadding + group.MinimumDistance, distancePadding + group.MaximumDistance));
}
var spawnMapCoords = _transform.ToMapCoordinates(spawnCoords);

View file

@ -58,12 +58,16 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem
[Dependency] private readonly ThrusterSystem _thruster = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
private EntityQuery<MapGridComponent> _gridQuery;
public const float TileMassMultiplier = 0.5f;
public override void Initialize()
{
base.Initialize();
_gridQuery = GetEntityQuery<MapGridComponent>();
InitializeFTL();
InitializeGridFills();
InitializeIFF();

View file

@ -1,6 +1,6 @@
using Content.Server.Temperature.Systems;
namespace Content.Server.Atmos.Components;
namespace Content.Server.Temperature.Components;
[RegisterComponent]
[Access(typeof(TemperatureSystem))]

View file

@ -1,6 +1,5 @@
using System.Linq;
using Content.Server.Administration.Logs;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Components;
using Content.Server.Temperature.Components;

View file

@ -10,3 +10,12 @@ public enum AccessWireActionKey : byte
Pulsed,
PulseCancel
}
[Serializable, NetSerializable]
public enum LogWireActionKey : byte
{
Key,
Status,
Pulsed,
PulseCancel
}

View file

@ -20,6 +20,8 @@ namespace Content.Shared.Administration
{
private string? _playtimeString;
public bool IsPinned { get; set; }
public string PlaytimeString => _playtimeString ??=
OverallPlaytime?.ToString("%d':'hh':'mm") ?? Loc.GetString("generic-unknown-title");

View file

@ -1,3 +1,4 @@
using Content.Shared.ActionBlocker;
using Content.Shared.Burial;
using Content.Shared.Burial.Components;
using Content.Shared.DoAfter;
@ -8,7 +9,6 @@ using Content.Shared.Popups;
using Content.Shared.Storage.Components;
using Content.Shared.Storage.EntitySystems;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
namespace Content.Server.Burial.Systems;
@ -18,6 +18,7 @@ public sealed class BurialSystem : EntitySystem
[Dependency] private readonly SharedEntityStorageSystem _storageSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
public override void Initialize()
{
@ -162,6 +163,9 @@ public sealed class BurialSystem : EntitySystem
if (component.HandDiggingDoAfter != null)
return;
if (!_actionBlocker.CanMove(args.Entity))
return;
var doAfterEventArgs = new DoAfterArgs(EntityManager, args.Entity, component.DigDelay / component.DigOutByHandModifier, new GraveDiggingDoAfterEvent(), uid, target: uid)
{
NeedHand = false,

View file

@ -48,7 +48,7 @@ namespace Content.Shared.Cargo
// public int RequesterId;
[DataField]
public string Reason { get; private set; }
public bool Approved => Approver is not null;
public bool Approved;
[DataField]
public string? Approver;

View file

@ -1,5 +1,4 @@
using Content.Shared.ActionBlocker;
using Content.Shared.Body.Systems;
using Content.Shared.Buckle.Components;
using Content.Shared.Climbing.Components;
using Content.Shared.Climbing.Events;
@ -44,6 +43,7 @@ public sealed partial class ClimbSystem : VirtualController
private const string ClimbingFixtureName = "climb";
private const int ClimbingCollisionGroup = (int) (CollisionGroup.TableLayer | CollisionGroup.LowImpassable);
private EntityQuery<ClimbableComponent> _climbableQuery;
private EntityQuery<FixturesComponent> _fixturesQuery;
private EntityQuery<TransformComponent> _xformQuery;
@ -51,6 +51,7 @@ public sealed partial class ClimbSystem : VirtualController
{
base.Initialize();
_climbableQuery = GetEntityQuery<ClimbableComponent>();
_fixturesQuery = GetEntityQuery<FixturesComponent>();
_xformQuery = GetEntityQuery<TransformComponent>();
@ -350,12 +351,39 @@ public sealed partial class ClimbSystem : VirtualController
{
if (args.OurFixtureId != ClimbingFixtureName
|| !component.IsClimbing
|| component.NextTransition != null
|| args.OurFixture.Contacts.Count > 1)
|| component.NextTransition != null)
{
return;
}
if (args.OurFixture.Contacts.Count > 1)
{
foreach (var contact in args.OurFixture.Contacts.Values)
{
if (!contact.IsTouching)
continue;
var otherEnt = contact.EntityA;
var otherFixture = contact.FixtureA;
var otherFixtureId = contact.FixtureAId;
if (uid == contact.EntityA)
{
otherEnt = contact.EntityB;
otherFixture = contact.FixtureB;
otherFixtureId = contact.FixtureBId;
}
if (args.OtherEntity == otherEnt && args.OtherFixtureId == otherFixtureId)
continue;
if (otherFixture is { Hard: true } &&
_climbableQuery.HasComp(otherEnt))
{
return;
}
}
}
foreach (var otherFixture in args.OurFixture.Contacts.Keys)
{
// If it's the other fixture then ignore em

View file

@ -1,4 +1,5 @@
using Content.Shared.Actions;
using Content.Shared.Mind;
using Content.Shared.MouseRotator;
using Content.Shared.Movement.Components;
using Content.Shared.Popups;
@ -13,6 +14,7 @@ public abstract class SharedCombatModeSystem : EntitySystem
[Dependency] private readonly INetManager _netMan = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
public override void Initialize()
{
@ -82,7 +84,7 @@ public abstract class SharedCombatModeSystem : EntitySystem
_actionsSystem.SetToggled(component.CombatToggleActionEntity, component.IsInCombatMode);
// Change mouse rotator comps if flag is set
if (!component.ToggleMouseRotator || IsNpc(entity))
if (!component.ToggleMouseRotator || IsNpc(entity) && !_mind.TryGetMind(entity, out _, out _))
return;
SetMouseRotatorComponents(entity, value);

View file

@ -21,6 +21,7 @@ using Robust.Shared.Physics.Systems;
using Robust.Shared.Timing;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Network;
using Robust.Shared.Map.Components;
namespace Content.Shared.Doors.Systems;
@ -40,6 +41,8 @@ public abstract partial class SharedDoorSystem : EntitySystem
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
[Dependency] private readonly PryingSystem _pryingSystem = default!;
[Dependency] protected readonly SharedPopupSystem Popup = default!;
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
[ValidatePrototypeId<TagPrototype>]
public const string DoorBumpTag = "DoorBumpOpener";
@ -555,29 +558,37 @@ public abstract partial class SharedDoorSystem : EntitySystem
if (!Resolve(uid, ref physics))
yield break;
var xform = Transform(uid);
// Getting the world bounds from the gridUid allows us to use the version of
// GetCollidingEntities that returns Entity<PhysicsComponent>
if (!TryComp<MapGridComponent>(xform.GridUid, out var mapGridComp))
yield break;
var tileRef = _mapSystem.GetTileRef(xform.GridUid.Value, mapGridComp, xform.Coordinates);
var doorWorldBounds = _entityLookup.GetWorldBounds(tileRef);
// TODO SLOTH fix electro's code.
// ReSharper disable once InconsistentNaming
var doorAABB = _entityLookup.GetWorldAABB(uid);
foreach (var otherPhysics in PhysicsSystem.GetCollidingEntities(Transform(uid).MapID, doorAABB))
foreach (var otherPhysics in PhysicsSystem.GetCollidingEntities(Transform(uid).MapID, doorWorldBounds))
{
if (otherPhysics == physics)
if (otherPhysics.Comp == physics)
continue;
//TODO: Make only shutters ignore these objects upon colliding instead of all airlocks
// Excludes Glasslayer for windows, GlassAirlockLayer for windoors, TableLayer for tables
if (!otherPhysics.CanCollide || otherPhysics.CollisionLayer == (int)CollisionGroup.GlassLayer || otherPhysics.CollisionLayer == (int)CollisionGroup.GlassAirlockLayer || otherPhysics.CollisionLayer == (int)CollisionGroup.TableLayer)
if (!otherPhysics.Comp.CanCollide || otherPhysics.Comp.CollisionLayer == (int) CollisionGroup.GlassLayer || otherPhysics.Comp.CollisionLayer == (int) CollisionGroup.GlassAirlockLayer || otherPhysics.Comp.CollisionLayer == (int) CollisionGroup.TableLayer)
continue;
//If the colliding entity is a slippable item ignore it by the airlock
if (otherPhysics.CollisionLayer == (int)CollisionGroup.SlipLayer && otherPhysics.CollisionMask == (int)CollisionGroup.ItemMask)
if (otherPhysics.Comp.CollisionLayer == (int) CollisionGroup.SlipLayer && otherPhysics.Comp.CollisionMask == (int) CollisionGroup.ItemMask)
continue;
//For when doors need to close over conveyor belts
if (otherPhysics.CollisionLayer == (int) CollisionGroup.ConveyorMask)
if (otherPhysics.Comp.CollisionLayer == (int) CollisionGroup.ConveyorMask)
continue;
if ((physics.CollisionMask & otherPhysics.CollisionLayer) == 0 && (otherPhysics.CollisionMask & physics.CollisionLayer) == 0)
if ((physics.CollisionMask & otherPhysics.Comp.CollisionLayer) == 0 && (otherPhysics.Comp.CollisionMask & physics.CollisionLayer) == 0)
continue;
if (_entityLookup.GetWorldAABB(otherPhysics.Owner).IntersectPercentage(doorAABB) < IntersectPercentage)

View file

@ -18,6 +18,7 @@ namespace Content.Shared.Examine
{
public abstract partial class ExamineSystemShared : EntitySystem
{
[Dependency] private readonly OccluderSystem _occluder = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
@ -182,12 +183,9 @@ namespace Content.Shared.Examine
length = MaxRaycastRange;
}
var occluderSystem = Get<OccluderSystem>();
IoCManager.Resolve(ref entMan);
var ray = new Ray(origin.Position, dir.Normalized());
var rayResults = occluderSystem
.IntersectRayWithPredicate(origin.MapId, ray, length, state, predicate, false).ToList();
var rayResults = _occluder
.IntersectRayWithPredicate(origin.MapId, ray, length, state, predicate, false);
if (rayResults.Count == 0) return true;
@ -195,13 +193,13 @@ namespace Content.Shared.Examine
foreach (var result in rayResults)
{
if (!entMan.TryGetComponent(result.HitEntity, out OccluderComponent? o))
if (!TryComp(result.HitEntity, out OccluderComponent? o))
{
continue;
}
var bBox = o.BoundingBox;
bBox = bBox.Translated(entMan.GetComponent<TransformComponent>(result.HitEntity).WorldPosition);
bBox = bBox.Translated(_transform.GetWorldPosition(result.HitEntity));
if (bBox.Contains(origin.Position) || bBox.Contains(other.Position))
{
@ -216,7 +214,6 @@ namespace Content.Shared.Examine
public bool InRangeUnOccluded(EntityUid origin, EntityUid other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true)
{
var entMan = IoCManager.Resolve<IEntityManager>();
var originPos = _transform.GetMapCoordinates(origin);
var otherPos = _transform.GetMapCoordinates(other);
@ -225,16 +222,14 @@ namespace Content.Shared.Examine
public bool InRangeUnOccluded(EntityUid origin, EntityCoordinates other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true)
{
var entMan = IoCManager.Resolve<IEntityManager>();
var originPos = _transform.GetMapCoordinates(origin);
var otherPos = other.ToMap(entMan, _transform);
var otherPos = _transform.ToMapCoordinates(other);
return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker);
}
public bool InRangeUnOccluded(EntityUid origin, MapCoordinates other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true)
{
var entMan = IoCManager.Resolve<IEntityManager>();
var originPos = _transform.GetMapCoordinates(origin);
return InRangeUnOccluded(originPos, other, range, predicate, ignoreInsideBlocker);
@ -250,11 +245,12 @@ namespace Content.Shared.Examine
}
var hasDescription = false;
var metadata = MetaData(entity);
//Add an entity description if one is declared
if (!string.IsNullOrEmpty(EntityManager.GetComponent<MetaDataComponent>(entity).EntityDescription))
if (!string.IsNullOrEmpty(metadata.EntityDescription))
{
message.AddText(EntityManager.GetComponent<MetaDataComponent>(entity).EntityDescription);
message.AddText(metadata.EntityDescription);
hasDescription = true;
}
@ -356,7 +352,7 @@ namespace Content.Shared.Examine
var totalMessage = new FormattedMessage(Message);
parts.Sort(Comparison);
if (_hasDescription)
if (_hasDescription && parts.Count > 0)
{
totalMessage.PushNewline();
}

View file

@ -953,7 +953,7 @@ namespace Content.Shared.Interaction
RaiseLocalEvent(target, interactUsingEvent, true);
DoContactInteraction(user, used, interactUsingEvent);
DoContactInteraction(user, target, interactUsingEvent);
DoContactInteraction(used, target, interactUsingEvent);
// Contact interactions are currently only used for forensics, so we don't raise used -> target
if (interactUsingEvent.Handled)
return;
@ -974,7 +974,7 @@ namespace Content.Shared.Interaction
if (canReach)
{
DoContactInteraction(user, target, afterInteractEvent);
DoContactInteraction(used, target, afterInteractEvent);
// Contact interactions are currently only used for forensics, so we don't raise used -> target
}
if (afterInteractEvent.Handled)
@ -990,7 +990,7 @@ namespace Content.Shared.Interaction
if (canReach)
{
DoContactInteraction(user, target, afterInteractUsingEvent);
DoContactInteraction(used, target, afterInteractUsingEvent);
// Contact interactions are currently only used for forensics, so we don't raise used -> target
}
}

View file

@ -39,6 +39,12 @@ namespace Content.Shared.Lathe
[DataField]
public string? RunningState;
[DataField]
public string? UnlitIdleState;
[DataField]
public string? UnlitRunningState;
#endregion
/// <summary>

View file

@ -20,25 +20,25 @@ public sealed partial class MagicMirrorComponent : Component
public EntityUid? Target;
/// <summary>
/// doafter time required to add a new slot
/// Do after time to add a new slot, adding hair to a person
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public TimeSpan AddSlotTime = TimeSpan.FromSeconds(10); // Sunrise-edit
/// <summary>
/// doafter time required to remove a existing slot
/// Do after time to remove a slot, removing hair from a person
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public TimeSpan RemoveSlotTime = TimeSpan.FromSeconds(8); // Sunrise-edit
/// <summary>
/// doafter time required to change slot
/// Do after time to change a person's hairstyle
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public TimeSpan SelectSlotTime = TimeSpan.FromSeconds(6); // Sunrise-edit
/// <summary>
/// doafter time required to recolor slot
/// Do after time to change a person's hair color
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public TimeSpan ChangeSlotTime = TimeSpan.FromSeconds(4); // Sunrise-edit

View file

@ -0,0 +1,63 @@
using Content.Shared.DoAfter;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Paper;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
public sealed partial class EnvelopeComponent : Component
{
/// <summary>
/// The current open/sealed/torn state of the envelope
/// </summary>
[ViewVariables, DataField, AutoNetworkedField]
public EnvelopeState State = EnvelopeState.Open;
[DataField, ViewVariables]
public string SlotId = "letter_slot";
/// <summary>
/// Stores the current sealing/tearing doafter of the envelope
/// to prevent doafter spam/prediction issues
/// </summary>
[DataField, ViewVariables]
public DoAfterId? EnvelopeDoAfter;
/// <summary>
/// How long it takes to seal the envelope closed
/// </summary>
[DataField, ViewVariables]
public TimeSpan SealDelay = TimeSpan.FromSeconds(1);
/// <summary>
/// How long it takes to tear open the envelope
/// </summary>
[DataField, ViewVariables]
public TimeSpan TearDelay = TimeSpan.FromSeconds(1);
/// <summary>
/// The sound to play when the envelope is sealed closed
/// </summary>
[DataField, ViewVariables]
public SoundPathSpecifier? SealSound = new SoundPathSpecifier("/Audio/Effects/packetrip.ogg");
/// <summary>
/// The sound to play when the envelope is torn open
/// </summary>
[DataField, ViewVariables]
public SoundPathSpecifier? TearSound = new SoundPathSpecifier("/Audio/Effects/poster_broken.ogg");
[Serializable, NetSerializable]
public enum EnvelopeState : byte
{
Open,
Sealed,
Torn
}
}
[Serializable, NetSerializable]
public sealed partial class EnvelopeDoAfterEvent : SimpleDoAfterEvent
{
}

View file

@ -0,0 +1,108 @@
using Content.Shared.DoAfter;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Verbs;
using Robust.Shared.Audio.Systems;
using Content.Shared.Examine;
namespace Content.Shared.Paper;
public sealed class EnvelopeSystem : EntitySystem
{
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<EnvelopeComponent, ItemSlotInsertAttemptEvent>(OnInsertAttempt);
SubscribeLocalEvent<EnvelopeComponent, ItemSlotEjectAttemptEvent>(OnEjectAttempt);
SubscribeLocalEvent<EnvelopeComponent, GetVerbsEvent<AlternativeVerb>>(OnGetAltVerbs);
SubscribeLocalEvent<EnvelopeComponent, EnvelopeDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<EnvelopeComponent, ExaminedEvent>(OnExamine);
}
private void OnExamine(Entity<EnvelopeComponent> ent, ref ExaminedEvent args)
{
if (ent.Comp.State == EnvelopeComponent.EnvelopeState.Sealed)
{
args.PushMarkup(Loc.GetString("envelope-sealed-examine", ("envelope", ent.Owner)));
}
else if (ent.Comp.State == EnvelopeComponent.EnvelopeState.Torn)
{
args.PushMarkup(Loc.GetString("envelope-torn-examine", ("envelope", ent.Owner)));
}
}
private void OnGetAltVerbs(Entity<EnvelopeComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanAccess || !args.CanInteract || args.Hands == null)
return;
if (ent.Comp.State == EnvelopeComponent.EnvelopeState.Torn)
return;
var user = args.User;
args.Verbs.Add(new AlternativeVerb()
{
Text = Loc.GetString(ent.Comp.State == EnvelopeComponent.EnvelopeState.Open ? "envelope-verb-seal" : "envelope-verb-tear"),
IconEntity = GetNetEntity(ent.Owner),
Act = () =>
{
TryStartDoAfter(ent, user, ent.Comp.State == EnvelopeComponent.EnvelopeState.Open ? ent.Comp.SealDelay : ent.Comp.TearDelay);
},
});
}
private void OnInsertAttempt(Entity<EnvelopeComponent> ent, ref ItemSlotInsertAttemptEvent args)
{
args.Cancelled |= ent.Comp.State != EnvelopeComponent.EnvelopeState.Open;
}
private void OnEjectAttempt(Entity<EnvelopeComponent> ent, ref ItemSlotEjectAttemptEvent args)
{
args.Cancelled |= ent.Comp.State == EnvelopeComponent.EnvelopeState.Sealed;
}
private void TryStartDoAfter(Entity<EnvelopeComponent> ent, EntityUid user, TimeSpan delay)
{
if (ent.Comp.EnvelopeDoAfter.HasValue)
return;
var doAfterEventArgs = new DoAfterArgs(EntityManager, user, delay, new EnvelopeDoAfterEvent(), ent.Owner, ent.Owner)
{
BreakOnDamage = true,
NeedHand = true,
BreakOnHandChange = true,
MovementThreshold = 0.01f,
DistanceThreshold = 1.0f,
};
if (_doAfterSystem.TryStartDoAfter(doAfterEventArgs, out var doAfterId))
ent.Comp.EnvelopeDoAfter = doAfterId;
}
private void OnDoAfter(Entity<EnvelopeComponent> ent, ref EnvelopeDoAfterEvent args)
{
ent.Comp.EnvelopeDoAfter = null;
if (args.Cancelled)
return;
if (ent.Comp.State == EnvelopeComponent.EnvelopeState.Open)
{
_audioSystem.PlayPredicted(ent.Comp.SealSound, ent.Owner, args.User);
ent.Comp.State = EnvelopeComponent.EnvelopeState.Sealed;
Dirty(ent.Owner, ent.Comp);
}
else if (ent.Comp.State == EnvelopeComponent.EnvelopeState.Sealed)
{
_audioSystem.PlayPredicted(ent.Comp.TearSound, ent.Owner, args.User);
ent.Comp.State = EnvelopeComponent.EnvelopeState.Torn;
Dirty(ent.Owner, ent.Comp);
if (_itemSlotsSystem.TryGetSlot(ent.Owner, ent.Comp.SlotId, out var slotComp))
_itemSlotsSystem.TryEjectToHands(ent.Owner, slotComp, args.User);
}
}
}

View file

@ -1,6 +1,15 @@
namespace Content.Shared.Power.EntitySystems;
using Content.Shared.Examine;
using Content.Shared.Power.Components;
namespace Content.Shared.Power.EntitySystems;
public abstract class SharedPowerReceiverSystem : EntitySystem
{
protected string GetExamineText(bool powered)
{
return Loc.GetString("power-receiver-component-on-examine-main",
("stateText", Loc.GetString(powered
? "power-receiver-component-on-examine-powered"
: "power-receiver-component-on-examine-unpowered")));
}
}

View file

@ -5,7 +5,7 @@ public interface ISalvageMod
/// <summary>
/// Player-friendly version describing this modifier.
/// </summary>
string Description { get; }
LocId Description { get; }
/// <summary>
/// Cost for difficulty modifiers.

View file

@ -17,7 +17,7 @@ public sealed partial class SalvageAirMod : IPrototype, IBiomeSpecificMod
/// <inheritdoc/>
[DataField("desc")]
public string Description { get; private set; } = string.Empty;
public LocId Description { get; private set; } = string.Empty;
/// <inheritdoc/>
[DataField("cost")]

View file

@ -12,7 +12,7 @@ public sealed partial class SalvageBiomeModPrototype : IPrototype, ISalvageMod
{
[IdDataField] public string ID { get; } = default!;
[DataField("desc")] public string Description { get; private set; } = string.Empty;
[DataField("desc")] public LocId Description { get; private set; } = string.Empty;
/// <summary>
/// Cost for difficulty modifiers.

View file

@ -10,7 +10,7 @@ public sealed partial class SalvageDungeonModPrototype : IPrototype, IBiomeSpeci
{
[IdDataField] public string ID { get; } = default!;
[DataField("desc")] public string Description { get; private set; } = string.Empty;
[DataField("desc")] public LocId Description { get; private set; } = string.Empty;
/// <inheridoc/>
[DataField("cost")]

View file

@ -8,7 +8,7 @@ public sealed partial class SalvageLightMod : IPrototype, IBiomeSpecificMod
{
[IdDataField] public string ID { get; } = default!;
[DataField("desc")] public string Description { get; private set; } = string.Empty;
[DataField("desc")] public LocId Description { get; private set; } = string.Empty;
/// <inheritdoc/>
[DataField("cost")]

View file

@ -10,7 +10,7 @@ public sealed partial class SalvageMod : IPrototype, ISalvageMod
{
[IdDataField] public string ID { get; } = default!;
[DataField("desc")] public string Description { get; private set; } = string.Empty;
[DataField("desc")] public LocId Description { get; private set; } = string.Empty;
/// <summary>
/// Cost for difficulty modifiers.

View file

@ -8,7 +8,7 @@ public sealed partial class SalvageTemperatureMod : IPrototype, IBiomeSpecificMo
{
[IdDataField] public string ID { get; } = default!;
[DataField("desc")] public string Description { get; private set; } = string.Empty;
[DataField("desc")] public LocId Description { get; private set; } = string.Empty;
/// <inheritdoc/>
[DataField("cost")]

View file

@ -10,7 +10,7 @@ public sealed partial class SalvageWeatherMod : IPrototype, IBiomeSpecificMod
{
[IdDataField] public string ID { get; } = default!;
[DataField("desc")] public string Description { get; private set; } = string.Empty;
[DataField("desc")] public LocId Description { get; private set; } = string.Empty;
/// <inheritdoc/>
[DataField("cost")]

View file

@ -7,7 +7,7 @@ public sealed partial class SalvageFactionPrototype : IPrototype
{
[IdDataField] public string ID { get; } = default!;
[DataField("desc")] public string Description { get; private set; } = string.Empty;
[DataField("desc")] public LocId Description { get; private set; } = string.Empty;
[ViewVariables(VVAccess.ReadWrite), DataField("entries", required: true)]
public List<SalvageMobEntry> MobGroups = new();

View file

@ -55,18 +55,18 @@ public abstract partial class SharedSalvageSystem : EntitySystem
if (air.Description != string.Empty)
{
mods.Add(air.Description);
mods.Add(Loc.GetString(air.Description));
}
// only show the description if there is an atmosphere since wont matter otherwise
if (temp.Description != string.Empty && !air.Space)
{
mods.Add(temp.Description);
mods.Add(Loc.GetString(temp.Description));
}
if (light.Description != string.Empty)
{
mods.Add(light.Description);
mods.Add(Loc.GetString(light.Description));
}
var duration = TimeSpan.FromSeconds(CfgManager.GetCVar(CCVars.SalvageExpeditionDuration));

View file

@ -1,7 +1,9 @@
namespace Content.Shared.Slippery
using Robust.Shared.GameStates;
namespace Content.Shared.Slippery;
[RegisterComponent, NetworkedComponent]
public sealed partial class NoSlipComponent : Component
{
[RegisterComponent]
public sealed partial class NoSlipComponent : Component
{
}
}

View file

@ -16,16 +16,14 @@ using Content.Shared.Tools.Systems;
using Content.Shared.Verbs;
using Content.Shared.Wall;
using Content.Shared.Whitelist;
using Robust.Shared.Audio;
using Content.Shared.ActionBlocker;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@ -47,6 +45,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
[Dependency] private readonly WeldableSystem _weldable = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
public const string ContainerName = "entity_storage";
@ -132,6 +131,9 @@ public abstract class SharedEntityStorageSystem : EntitySystem
if (!HasComp<HandsComponent>(args.Entity))
return;
if (!_actionBlocker.CanMove(args.Entity))
return;
if (_timing.CurTime < component.NextInternalOpenAttempt)
return;
@ -139,9 +141,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
Dirty(uid, component);
if (component.OpenOnMove)
{
TryOpenStorage(args.Entity, uid);
}
}
protected void OnFoldAttempt(EntityUid uid, SharedEntityStorageComponent component, ref FoldAttemptEvent args)

View file

@ -150,8 +150,11 @@ public sealed class SwapTeleporterSystem : EntitySystem
return;
}
var (teleEnt, cont) = GetTeleportingEntity((uid, xform));
var (otherTeleEnt, otherCont) = GetTeleportingEntity((linkedEnt, Transform(linkedEnt)));
var teleEnt = GetTeleportingEntity((uid, xform));
var otherTeleEnt = GetTeleportingEntity((linkedEnt, Transform(linkedEnt)));
_container.TryGetOuterContainer(teleEnt, Transform(teleEnt), out var cont);
_container.TryGetOuterContainer(otherTeleEnt, Transform(otherTeleEnt), out var otherCont);
if (otherCont != null && !_container.CanInsert(teleEnt, otherCont) ||
cont != null && !_container.CanInsert(otherTeleEnt, cont))
@ -195,20 +198,18 @@ public sealed class SwapTeleporterSystem : EntitySystem
DestroyLink(linked, user); // the linked one is shown globally
}
private (EntityUid, BaseContainer?) GetTeleportingEntity(Entity<TransformComponent> ent)
private EntityUid GetTeleportingEntity(Entity<TransformComponent> ent)
{
var parent = ent.Comp.ParentUid;
if (_container.TryGetOuterContainer(ent, ent, out var container))
parent = container.Owner;
if (HasComp<MapGridComponent>(parent) || HasComp<MapComponent>(parent))
return (ent, container);
return ent;
if (!_xformQuery.TryGetComponent(parent, out var parentXform) || parentXform.Anchored)
return (ent, container);
return ent;
if (!TryComp<PhysicsComponent>(parent, out var body) || body.BodyType == BodyType.Static)
return (ent, container);
return ent;
return GetTeleportingEntity((parent, parentXform));
}

View file

@ -485,7 +485,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
var weapon = GetEntity(ev.Weapon);
Interaction.DoContactInteraction(weapon, target);
// We skip weapon -> target interaction, as forensics system applies DNA on hit
Interaction.DoContactInteraction(user, weapon);
// If the user is using a long-range weapon, this probably shouldn't be happening? But I'll interpret melee as a
@ -616,7 +616,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
// For stuff that cares about it being attacked.
foreach (var target in targets)
{
Interaction.DoContactInteraction(weapon, target);
// We skip weapon -> target interaction, as forensics system applies DNA on hit
// If the user is using a long-range weapon, this probably shouldn't be happening? But I'll interpret melee as a
// somewhat messy scuffle. See also, light attacks.

View file

@ -31,8 +31,6 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
public const string GrapplingJoint = "grappling";
public const float ReelRate = 2.5f;
public override void Initialize()
{
base.Initialize();
@ -187,7 +185,7 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
}
// TODO: This should be on engine.
distance.MaxLength = MathF.Max(distance.MinLength, distance.MaxLength - ReelRate * frameTime);
distance.MaxLength = MathF.Max(distance.MinLength, distance.MaxLength - grappling.ReelRate * frameTime);
distance.Length = MathF.Min(distance.MaxLength, distance.Length);
_physics.WakeBody(joint.BodyAUid);

View file

@ -8,6 +8,12 @@ namespace Content.Shared.Weapons.Ranged.Components;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class GrapplingGunComponent : Component
{
/// <summary>
/// Hook's reeling force and speed - the higher the number, the faster the hook rewinds.
/// </summary>
[DataField, AutoNetworkedField]
public float ReelRate = 2.5f;
[DataField("jointId"), AutoNetworkedField]
public string Joint = string.Empty;

View file

@ -367,5 +367,16 @@ Entries:
id: 45
time: '2024-07-11T05:14:01.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29896
- author: Repo
changes:
- message: Added aHelp player pinning.
type: Add
- message: Added disconnection, reconnection, banning notice on relay and ahelp.
type: Add
- message: Fixed search clears on aHelp close.
type: Fix
id: 46
time: '2024-07-30T08:28:32.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/28639
Name: Admin
Order: 1

View file

@ -1,202 +1,4 @@
Entries:
- author: MilenVolf
changes:
- message: Borgs now have brand new voice and walking sounds.
type: Add
- message: Syndicate assault borgs now have new feature to scare - manic laugher.
type: Add
id: 6491
time: '2024-04-29T04:38:31.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27205
- author: KrasnoshchekovPavel
changes:
- message: Fixed formatting of floating point numbers during localization
type: Fix
id: 6492
time: '2024-04-29T04:52:35.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27441
- author: mirrorcult
changes:
- message: Random events should now occur much more frequently
type: Tweak
id: 6493
time: '2024-04-29T06:38:15.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27469
- author: Plykiya
changes:
- message: Latejoin players now have a chance to roll thief.
type: Add
id: 6494
time: '2024-04-29T06:38:38.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27466
- author: ElectroJr
changes:
- message: Fixed actions sometimes disappearing from the hotbar when double clicking
type: Fix
id: 6495
time: '2024-04-29T08:36:18.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27468
- author: DrSmugleaf
changes:
- message: Fixed bullets not going exactly where you click when moving.
type: Fix
id: 6496
time: '2024-04-29T13:12:30.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27484
- author: FungiFellow
changes:
- message: Syndi-Cats now have a Wideswing, 80% Explosion Resist, 6/6/15 Pierce/Slash/Structural
and step over glass shards with trained feline agility.
type: Tweak
id: 6497
time: '2024-04-29T17:09:30.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27408
- author: ShadowCommander
changes:
- message: Fixed microwave construction not creating a microwave on completion.
type: Fix
id: 6498
time: '2024-04-30T00:19:08.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27500
- author: lzk228
changes:
- message: Barozine is removed from hydroponics mutatuions.
type: Remove
id: 6499
time: '2024-04-30T03:01:38.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27512
- author: DogZeroX
changes:
- message: Changed the announcement of the immovable rod event.
type: Tweak
id: 6500
time: '2024-04-30T04:05:14.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27515
- author: DogZeroX
changes:
- message: Flares looped sound no longer have a massive range, and are much quieter.
type: Fix
id: 6501
time: '2024-04-30T06:49:35.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27521
- author: ERORR404V1
changes:
- message: Added chameleon projector in thief toolbox!
type: Add
id: 6502
time: '2024-04-30T12:14:06.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27491
- author: DamnFeds
changes:
- message: honkbot now uses a happy honk meal instead of box of hugs and clown's
rubber stamp. Honk!
type: Tweak
id: 6503
time: '2024-05-01T03:27:21.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27535
- author: Plykiya
changes:
- message: Inserting telecrystals no longer announces to everyone around you that
you inserted it.
type: Tweak
id: 6504
time: '2024-05-01T15:17:47.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27585
- author: Dezzzix
changes:
- message: Goldschlager empty bottle renamed to Gildlager empty bottle
type: Tweak
id: 6505
time: '2024-05-01T15:24:05.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27581
- author: FungiFellow
changes:
- message: Grilles now take Structural Damage
type: Tweak
id: 6506
time: '2024-05-01T22:23:42.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27596
- author: lzk228
changes:
- message: Immovable rod announce now happens at the end of event.
type: Tweak
id: 6507
time: '2024-05-01T22:26:22.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27587
- author: metalgearsloth
changes:
- message: Fix server performance dropping significantly.
type: Fix
id: 6508
time: '2024-05-02T00:18:38.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27528
- author: metalgearsloth
changes:
- message: Fix muzzle flash rotations.
type: Fix
- message: Fix large client performance drop.
type: Fix
id: 6509
time: '2024-05-02T02:40:07.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27533
- author: ElectroJr
changes:
- message: Fix gas analyzers not opening their UI when used in-hand.
type: Fix
id: 6510
time: '2024-05-02T06:00:01.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27610
- author: Plykiya
changes:
- message: Disarming a player now causes them to throw the disarmed item away from
them.
type: Tweak
id: 6511
time: '2024-05-02T12:32:47.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27589
- author: fujiwaranao
changes:
- message: Renault now has additional fox noises.
type: Add
id: 6512
time: '2024-05-02T12:35:11.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27578
- author: Doc-Michael
changes:
- message: CMO's Lab coat is now more resistant to chemical spills and minor cuts
type: Tweak
id: 6513
time: '2024-05-02T12:37:12.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27551
- author: Vasilis
changes:
- message: Removed airtight flaps from the construction menu.
type: Remove
id: 6514
time: '2024-05-02T14:49:54.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27619
- author: Lamrr
changes:
- message: Wine and beer bottles can now be inserted into the booze dispenser.
type: Fix
id: 6515
time: '2024-05-02T16:17:36.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27626
- author: deltanedas
changes:
- message: "Ducky slippers now make you waddle. \U0001F986\U0001F986"
type: Tweak
id: 6516
time: '2024-05-02T17:09:38.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27628
- author: ElectroJr
changes:
- message: Fixed various interactions not prioritizing opening a UI. No more trying
to eat mission critical faxes.
type: Fix
id: 6517
time: '2024-05-02T23:37:21.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27631
- author: Plykiya
changes:
- message: Blast grenades have had their manufacturing cost tripled.
@ -3781,3 +3583,205 @@
id: 6990
time: '2024-07-27T07:27:21.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30393
- author: BombasterDS
changes:
- message: Added new plant mutations for apple, sugarcane and galaxythistle
type: Add
id: 6991
time: '2024-07-27T15:08:49.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/28993
- author: Spessmann
changes:
- message: Thief objectives for figurines and stamps now require less items
type: Tweak
id: 6992
time: '2024-07-27T23:11:27.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30390
- author: metalgearsloth
changes:
- message: Moved VGRoid from 1,000m away to ~500m.
type: Tweak
id: 6993
time: '2024-07-28T03:14:18.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29943
- author: lzk228
changes:
- message: Fixed pancakes stacks. Before it, splitting not default pancakes stacks
would give you default pancakes.
type: Fix
id: 6994
time: '2024-07-28T03:49:06.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30270
- author: Plykiya
changes:
- message: Fixed the client mispredicting people slipping with their magboots turned
on
type: Fix
id: 6995
time: '2024-07-28T06:17:06.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30425
- author: Katzenminer
changes:
- message: Pun and similar pets are no longer firemune
type: Fix
id: 6996
time: '2024-07-28T08:32:27.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30424
- author: lzk228
changes:
- message: Fixed permanent absence of the approver string in cargo invoice.
type: Fix
id: 6997
time: '2024-07-29T06:19:43.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29690
- author: JIPDawg
changes:
- message: F9 is correctly bound to the Round End Summary window by default now.
type: Fix
id: 6998
time: '2024-07-29T06:49:28.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30438
- author: githubuser508
changes:
- message: Candles crate and the ability for Cargo to order it.
type: Add
id: 6999
time: '2024-07-29T08:29:27.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29736
- author: Blackern5000
changes:
- message: Emergency oxygen and fire lockers now generally contain more supplies
type: Tweak
id: 7000
time: '2024-07-29T09:57:04.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29230
- author: Moomoobeef
changes:
- message: Added the ability to wear lizard plushies on your head!
type: Add
id: 7001
time: '2024-07-29T12:52:40.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30400
- author: TurboTrackerss14
changes:
- message: Reduced Kobold ghostrole chance to mirror Monkey
type: Tweak
id: 7002
time: '2024-07-29T15:16:54.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30450
- author: Ian321
changes:
- message: The Courser now comes with a defibrillator.
type: Tweak
id: 7003
time: '2024-07-30T01:05:27.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30471
- author: slarticodefast
changes:
- message: Fixed puppy Ian not counting as a thief steal target.
type: Fix
id: 7004
time: '2024-07-30T01:22:17.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30474
- author: themias
changes:
- message: Added envelopes to the PTech and bureaucracy crate
type: Add
id: 7005
time: '2024-07-30T01:49:05.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30298
- author: TheKittehJesus
changes:
- message: The recipe for chow mein, egg-fried rice, and both brownies now use liquid
egg instead of a whole egg.
type: Tweak
- message: Cake batter now also requires 5u of milk
type: Tweak
id: 7006
time: '2024-07-30T02:14:11.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30262
- author: Plykiya
changes:
- message: Wearing something that covers your head will prevent your hair from being
cut.
type: Add
- message: You now see a popup when your hair is being altered.
type: Add
- message: The doafter for altering other people's hair now takes seven seconds.
type: Tweak
id: 7007
time: '2024-07-30T02:17:28.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30366
- author: Cojoke-dot
changes:
- message: Hamlet and other ghost rolls can now spin when they enter combat mode
type: Fix
id: 7008
time: '2024-07-30T02:48:28.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30478
- author: themias
changes:
- message: Fixed the ACC wire not appearing in vending machine wire layouts
type: Fix
id: 7009
time: '2024-07-30T03:04:17.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30453
- author: Blackern5000
changes:
- message: The defibrillator has been recolored slightly
type: Tweak
id: 7010
time: '2024-07-30T04:41:21.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29964
- author: themias
changes:
- message: Fixed victim's fingerprints transferring onto an attacker's weapon
type: Fix
id: 7011
time: '2024-07-30T08:35:30.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30257
- author: to4no_fix
changes:
- message: Now engineering access is needed to interact with the particle accelerator
type: Tweak
id: 7012
time: '2024-07-30T11:29:32.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30394
- author: Cojoke-dot
changes:
- message: You can no longer get out of a disposal chute or container while knocked
over by trying to walk
type: Fix
id: 7013
time: '2024-07-30T13:53:44.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30391
- author: Cojoke-dot
changes:
- message: QSI now swaps the top most valid container instead of QSI when placed
in an anchored container
type: Fix
id: 7014
time: '2024-07-30T14:07:35.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30241
- author: TheShuEd
changes:
- message: industrial ore processor can now process diamonds
type: Fix
id: 7015
time: '2024-07-30T14:41:15.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30499
- author: PJB3005
changes:
- message: CLF3 is now called "chlorine trifluoride"
type: Tweak
id: 7016
time: '2024-07-31T00:14:23.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30510
- author: slarticodefast
changes:
- message: Fixed the mouse position when it is over a singularity distortion effect
while zoomed in or out.
type: Fix
id: 7017
time: '2024-07-31T00:14:49.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30509

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
gera-transformation-popup = This action will transform you. Use it again to confirm.

View file

@ -16,3 +16,6 @@ admin-bwoink-play-sound = Bwoink?
bwoink-title-none-selected = None selected
bwoink-system-rate-limited = System: you are sending messages too quickly.
bwoink-system-player-disconnecting = has disconnected.
bwoink-system-player-reconnecting = has reconnected.
bwoink-system-player-banned = has been banned for: {$banReason}

View file

@ -30,7 +30,7 @@ cargo-console-snip-snip = Order trimmed to capacity
cargo-console-insufficient-funds = Insufficient funds (require {$cost})
cargo-console-unfulfilled = No room to fulfill order
cargo-console-trade-station = Sent to {$destination}
cargo-console-unlock-approved-order-broadcast = [bold]{$productName} x{$orderAmount}[/bold], which cost [bold]{$cost}[/bold], was approved by [bold]{$approverName}, {$approverJob}[/bold]
cargo-console-unlock-approved-order-broadcast = [bold]{$productName} x{$orderAmount}[/bold], which cost [bold]{$cost}[/bold], was approved by [bold]{$approver}[/bold]
cargo-console-paper-print-name = Order #{$orderNumber}
cargo-console-paper-print-text =

View file

@ -1,3 +1,15 @@
magic-mirror-component-activate-user-has-no-hair = You can't have any hair!
magic-mirror-window-title = Magic Mirror
magic-mirror-window-title = Magic Mirror
magic-mirror-add-slot-self = You're giving yourself some hair.
magic-mirror-remove-slot-self = You're removing some of your hair.
magic-mirror-change-slot-self = You're changing your hairstyle.
magic-mirror-change-color-self = You're changing your hair color.
magic-mirror-add-slot-target = Hair is being added to you by {$user}.
magic-mirror-remove-slot-target = Your hair is being cut off by {$user}.
magic-mirror-change-slot-target = Your hairstyle is being changed by {$user}.
magic-mirror-change-color-target = Your hair color is being changed by {$user}.
magic-mirror-blocked-by-hat-self = You need to take off your hat before changing your hair.
magic-mirror-blocked-by-hat-self-target = You try to change their hair but their clothes gets in the way.

View file

@ -6,6 +6,6 @@ examine-system-cant-see-entity = You can't make out whatever that is.
examine-verb-name = Basic
examinable-anchored = It is [color=darkgreen]anchored[/color] to the floor
examinable-anchored = It is [color=darkgreen]anchored[/color] to the floor.
examinable-unanchored = It is [color=darkred]unanchored[/color] from the floor
examinable-unanchored = It is [color=darkred]unanchored[/color] from the floor.

View file

@ -0,0 +1,11 @@
envelope-verb-seal = Seal
envelope-verb-tear = Tear
envelope-letter-slot = Letter
envelope-sealed-examine = [color=gray]{CAPITALIZE(THE($envelope))} is sealed.[/color]
envelope-torn-examine = [color=yellow]{CAPITALIZE(THE($envelope))} is torn and unusable![/color]
envelope-default-message = TO:
FROM:

View file

@ -32,3 +32,34 @@ salvage-expedition-announcement-countdown-seconds = {$duration} seconds remainin
salvage-expedition-announcement-dungeon = Dungeon is located {$direction}.
salvage-expedition-completed = Expedition is completed.
salvage-expedition-reward-description = Mission completion reward
# Salvage biome mod
salvage-biome-mod-caves = Caves
salvage-biome-mod-grasslands = Grasslands
salvage-biome-mod-snow = Snow
salvage-biome-mod-lava = Lava
# Salvage mods
salvage-light-mod-daylight = Daylight
salvage-light-mod-evening = Evening
salvage-light-mod-night = Night time
salvage-temperature-mod-room-temperature = Room temperature
salvage-temperature-mod-hot = Hot
salvage-temperature-mod-high-temperature = High temperature
salvage-temperature-mod-extreme-heat = Extreme heat
salvage-temperature-mod-cold = Cold
salvage-temperature-mod-low-temperature = Low temperature
salvage-temperature-mod-extreme-cold = Extreme cold
salvage-air-mod-no-atmosphere = No atmosphere
salvage-air-mod-breathable-atmosphere = Breathable atmosphere
salvage-air-mod-dangerous-atmosphere = Dangerous atmosphere
salvage-air-mod-toxic-atmosphere = Toxic atmosphere
salvage-air-mod-volatile-atmosphere = Volatile atmosphere
salvage-dungeon-mod-lava-brig = Lava Brig
salvage-dungeon-mod-snowy-labs = Snowy labs
salvage-dungeon-mod-experiment = Experiment
salvage-dungeon-mod-haunted = Haunted
salvage-dungeon-mod-mineshaft = Mineshaft

View file

@ -0,0 +1,2 @@
salvage-faction-xenos = Xenos
salvage-faction-carps = Carps

View file

@ -7,7 +7,7 @@ reagent-desc-napalm = It's just a little flammable.
reagent-name-phlogiston = phlogiston
reagent-desc-phlogiston = Catches you on fire and makes you ignite.
reagent-name-chlorine-trifluoride = CLF3
reagent-name-chlorine-trifluoride = chlorine trifluoride
reagent-desc-chlorine-trifluoride = You really, REALLY don't want to get this shit anywhere near you.
reagent-name-foaming-agent = foaming agent

View file

@ -32,6 +32,8 @@ seeds-potato-name = potato
seeds-potato-display-name = potatoes
seeds-sugarcane-name = sugarcane
seeds-sugarcane-display-name = sugarcanes
seeds-papercane-name = papercane
seeds-papercane-display-name = papercanes
seeds-towercap-name = tower cap
seeds-towercap-display-name = tower caps
seeds-steelcap-name = steel cap
@ -48,6 +50,8 @@ seeds-eggplant-name = eggplant
seeds-eggplant-display-name = eggplants
seeds-apple-name = apple
seeds-apple-display-name = apple tree
seeds-goldenapple-name = golden apple
seeds-goldenapple-display-name = golden apple tree
seeds-corn-name = corn
seeds-corn-display-name = ears of corn
seeds-onion-name = onion
@ -88,6 +92,8 @@ seeds-ambrosiadeus-name = ambrosia deus
seeds-ambrosiadeus-display-name = ambrosia deus
seeds-galaxythistle-name = galaxythistle
seeds-galaxythistle-display-name = galaxythistle
seeds-glasstle-name = glasstle
seeds-glasstle-display-name = glasstle
seeds-flyamanita-name = fly amanita
seeds-flyamanita-display-name = fly amanita
seeds-gatfruit-name = gatfruit

View file

@ -362,85 +362,96 @@ entities:
version: 2
data:
tiles:
-2,-1:
0: 61166
-1,-1:
0: 65535
0,-1:
0: 65535
-2,-3:
0: 60620
0: 8192
1: 34944
-2,-2:
0: 61102
1: 64
1: 52936
-2,-1:
1: 52430
-2,-4:
0: 51328
-1,-4:
0: 57343
1: 8192
-1,-3:
0: 65535
-1,-2:
0: 49151
1: 16384
0,-4:
0: 65535
0,-3:
0: 65535
0,-2:
0: 65535
1,-4:
0: 12560
1,-3:
0: 29491
1,-2:
0: 30551
1: 32
1,-1:
0: 30583
0: 16512
-2,0:
0: 61166
-2,1:
0: 61102
1: 64
-2,2:
0: 52300
1: 128
-2,3:
0: 136
-1,0:
0: 65535
-1,1:
0: 49151
1: 16384
-1,2:
0: 65535
-1,3:
0: 61439
0,0:
0: 65535
0,1:
0: 57343
1: 8192
0,2:
0: 65535
0,3:
0: 32767
1,0:
0: 30583
1,1:
0: 30551
1: 32
1,2:
0: 13091
1: 16
1,3:
0: 17
1: 52940
-1,-4:
1: 65260
-1,-3:
1: 65372
-1,-2:
1: 57297
-1,-1:
1: 65489
-1,-5:
0: 61440
0: 4096
-1,0:
1: 8191
0,-4:
1: 63347
0,-3:
1: 65443
0,-2:
1: 49080
0,-1:
1: 65464
0,0:
1: 36863
0,-5:
0: 61440
0: 32768
1,-4:
0: 8208
1,-3:
1: 4368
0: 16384
1,-2:
1: 14129
1,-1:
1: 13111
1,0:
1: 14131
-2,1:
1: 35022
0: 8192
-2,2:
1: 136
0: 16384
-1,1:
1: 56829
-1,2:
1: 62705
-2,3:
0: 128
-1,3:
1: 3310
0,1:
1: 48123
0,2:
1: 62200
0,3:
1: 887
1,1:
1: 4407
0: 16384
1,2:
1: 17
0: 8192
1,3:
0: 16
uniqueMixes:
- volume: 2500
immutable: True
moles:
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- volume: 2500
temperature: 293.15
moles:
@ -456,21 +467,6 @@ entities:
- 0
- 0
- 0
- volume: 2500
temperature: 293.15
moles:
- 19.481253
- 73.28662
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
chunkSize: 4
- type: RadiationGridResistance
- type: GravityShake
@ -485,8 +481,6 @@ entities:
- type: Transform
pos: 0.5,-12.5
parent: 656
- type: AtmosDevice
joinedGrid: 656
- proto: AirlockCommandGlassLocked
entities:
- uid: 601
@ -2023,6 +2017,14 @@ entities:
- type: Transform
pos: 0.5,14.5
parent: 656
- proto: DefibrillatorCabinetFilled
entities:
- uid: 657
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: -2.5,-6.5
parent: 656
- proto: ExtinguisherCabinetFilled
entities:
- uid: 517
@ -2176,8 +2178,6 @@ entities:
- type: Transform
pos: 0.5,-11.5
parent: 656
- type: AtmosDevice
joinedGrid: 656
- proto: GasPassiveVent
entities:
- uid: 521
@ -2186,8 +2186,6 @@ entities:
rot: -1.5707963267948966 rad
pos: -0.5,-11.5
parent: 656
- type: AtmosDevice
joinedGrid: 656
- proto: GasPipeBend
entities:
- uid: 523
@ -2536,8 +2534,6 @@ entities:
rot: 3.141592653589793 rad
pos: 0.5,-12.5
parent: 656
- type: AtmosDevice
joinedGrid: 656
- proto: GasVentPump
entities:
- uid: 524
@ -2546,72 +2542,54 @@ entities:
rot: -1.5707963267948966 rad
pos: -0.5,-12.5
parent: 656
- type: AtmosDevice
joinedGrid: 656
- uid: 550
components:
- type: Transform
rot: 3.141592653589793 rad
pos: 3.5,-10.5
parent: 656
- type: AtmosDevice
joinedGrid: 656
- uid: 551
components:
- type: Transform
rot: 3.141592653589793 rad
pos: -3.5,-10.5
parent: 656
- type: AtmosDevice
joinedGrid: 656
- uid: 552
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: -4.5,-0.5
parent: 656
- type: AtmosDevice
joinedGrid: 656
- uid: 553
components:
- type: Transform
rot: -1.5707963267948966 rad
pos: 4.5,-0.5
parent: 656
- type: AtmosDevice
joinedGrid: 656
- uid: 573
components:
- type: Transform
rot: -1.5707963267948966 rad
pos: 2.5,11.5
parent: 656
- type: AtmosDevice
joinedGrid: 656
- uid: 574
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: -2.5,11.5
parent: 656
- type: AtmosDevice
joinedGrid: 656
- uid: 580
components:
- type: Transform
rot: -1.5707963267948966 rad
pos: 4.5,8.5
parent: 656
- type: AtmosDevice
joinedGrid: 656
- uid: 589
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: -4.5,8.5
parent: 656
- type: AtmosDevice
joinedGrid: 656
- proto: GeneratorBasic15kW
entities:
- uid: 66
@ -3424,7 +3402,7 @@ entities:
- type: Transform
pos: 1.5,-15.5
parent: 656
- proto: soda_dispenser
- proto: SodaDispenser
entities:
- uid: 166
components:

View file

@ -11876,6 +11876,8 @@ entities:
rot: 3.141592653589793 rad
pos: 0.5,-0.5
parent: 7536
- type: Door
changeAirtight: False
- type: Docking
dockJointId: docking43669
dockedWith: 7332
@ -13198,6 +13200,8 @@ entities:
lastSignals:
DoorStatus: False
DockStatus: True
- type: Door
changeAirtight: False
- uid: 12584
components:
- type: Transform
@ -122226,6 +122230,21 @@ entities:
parent: 60
- proto: SignAi
entities:
- uid: 16533
components:
- type: Transform
pos: -101.5,16.5
parent: 60
- uid: 19800
components:
- type: Transform
pos: -112.5,20.5
parent: 60
- uid: 19808
components:
- type: Transform
pos: -107.5,16.5
parent: 60
- uid: 21237
components:
- type: Transform
@ -122236,6 +122255,18 @@ entities:
- type: Transform
pos: -60.5,16.5
parent: 60
- proto: SignAiUpload
entities:
- uid: 21130
components:
- type: Transform
pos: -110.5,6.5
parent: 60
- uid: 23380
components:
- type: Transform
pos: -112.5,14.5
parent: 60
- proto: SignalButton
entities:
- uid: 3803
@ -123122,11 +123153,12 @@ entities:
- type: Transform
pos: 2.5,-1.5
parent: 60
- proto: SignCanisters
- proto: SignCans
entities:
- uid: 15171
- uid: 13825
components:
- type: Transform
rot: 3.141592653589793 rad
pos: -28.5,37.5
parent: 60
- proto: SignCargo
@ -123183,6 +123215,13 @@ entities:
- type: Transform
pos: 42.5,-25.5
parent: 60
- proto: SignCryo
entities:
- uid: 15171
components:
- type: Transform
pos: -29.5,22.5
parent: 60
- proto: SignCryogenicsMed
entities:
- uid: 4111
@ -123313,11 +123352,11 @@ entities:
rot: 1.5707963267948966 rad
pos: 42.49971,-21.305145
parent: 60
- uid: 16533
- uid: 23381
components:
- type: Transform
rot: -1.5707963267948966 rad
pos: -29.5,22.5
rot: 3.141592653589793 rad
pos: -32.5,18.5
parent: 60
- proto: SignDirectionalDorms
entities:
@ -124056,6 +124095,14 @@ entities:
- type: Transform
pos: -109.5,20.5
parent: 60
- proto: SignKitchen
entities:
- uid: 23382
components:
- type: Transform
rot: 3.141592653589793 rad
pos: 24.5,-25.5
parent: 60
- proto: SignLaserMed
entities:
- uid: 14624
@ -124094,6 +124141,12 @@ entities:
- type: Transform
pos: 3.5,-46.5
parent: 60
- uid: 23969
components:
- type: Transform
rot: 3.141592653589793 rad
pos: 18.5,17.5
parent: 60
- proto: SignMedical
entities:
- uid: 2632
@ -124279,6 +124332,14 @@ entities:
- type: Transform
pos: -19.5,-11.5
parent: 60
- proto: SignRestroom
entities:
- uid: 23970
components:
- type: Transform
rot: 3.141592653589793 rad
pos: 18.5,11.5
parent: 60
- proto: SignRND
entities:
- uid: 7085
@ -124537,6 +124598,14 @@ entities:
- type: Transform
pos: 22.5,6.5
parent: 60
- proto: SignTheater
entities:
- uid: 24271
components:
- type: Transform
rot: 3.141592653589793 rad
pos: 18.5,-20.5
parent: 60
- proto: SignToolStorage
entities:
- uid: 6315
@ -124544,6 +124613,14 @@ entities:
- type: Transform
pos: 14.5,10.5
parent: 60
- proto: SignVault
entities:
- uid: 24268
components:
- type: Transform
rot: 3.141592653589793 rad
pos: 2.5,-5.5
parent: 60
- proto: SignVirology
entities:
- uid: 2969

File diff suppressed because it is too large Load diff

View file

@ -166,6 +166,8 @@
name: Morph into Geras
description: Morphs you into a Geras - a miniature version of you which allows you to move fast, at the cost of your inventory.
components:
- type: ConfirmableAction
popup: gera-transformation-popup
- type: InstantAction
itemIconStyle: BigAction
useDelay: 10 # prevent spam

View file

@ -84,7 +84,7 @@
specificHeat: 40
heatCapacityRatio: 1.3
molarMass: 44
color: 2887E8
color: 8F00FF
reagent: NitrousOxide
pricePerMole: 1

View file

@ -198,3 +198,12 @@
category: cargoproduct-category-name-service
group: market
- type: cargoProduct
id: ServiceCandles
icon:
sprite: Objects/Misc/candles.rsi
state: candle-big
product: CrateCandles
cost: 500
category: cargoproduct-category-name-service
group: market

View file

@ -432,3 +432,17 @@
- id: DartYellow
amount: 2
- type: entity
name: envelope box
parent: BoxCardboard
id: BoxEnvelope
description: A box filled with envelopes.
components:
- type: Sprite
layers:
- state: box
- state: envelope
- type: StorageFill
contents:
- id: Envelope
amount: 9

View file

@ -128,6 +128,7 @@
- id: BoxFolderRed
- id: BoxFolderYellow
- id: NewtonCradle
- id: BoxEnvelope
- type: entity
id: CrateServiceFaxMachine
@ -333,3 +334,16 @@
prob: 0.1
- id: ShardGlassPlasma
prob: 0.1
- type: entity
id: CrateCandles
parent: CrateGenericSteel
name: candles crate
description: Contains 4 boxes of candles, 2 large and 2 small. For atmosphere or something.
components:
- type: StorageFill
contents:
- id: BoxCandle
amount: 2
- id: BoxCandleSmall
amount: 2

View file

@ -1,5 +1,6 @@
- type: entity
id: ClothingShoesBootsCombatFilled
suffix: Filled, Combat Knife
parent:
- ClothingShoesBootsCombat
- ClothingShoesBootsSecFilled
@ -38,3 +39,12 @@
item:
- KukriKnife
- type: entity
id: ClothingShoesBootsSyndieFilled
parent: ClothingShoesBootsCombat
suffix: Filled, Throwing Knife
components:
- type: ContainerFill
containers:
item:
- ThrowingKnife

View file

@ -20,25 +20,27 @@
components:
- type: StorageFill
contents:
- id: ClothingOuterSuitEmergency
- id: ClothingMaskBreath
- id: ClothingOuterSuitEmergency
- id: EmergencyOxygenTankFilled
prob: 0.80
orGroup: EmergencyTankOrRegularTank
prob: 0.5
orGroup: OxygenTank
- id: OxygenTankFilled
prob: 0.20
orGroup: EmergencyTankOrRegularTank
prob: 0.5
orGroup: OxygenTank
- id: ToolboxEmergencyFilled
prob: 0.4
prob: 0.5
- id: MedkitOxygenFilled
prob: 0.2
- id: WeaponFlareGun
prob: 0.05
- id: BoxMRE
prob: 0.1
# Sunrise-Start
- id: CrowbarRed
- id: CrowbarRed
- id: CrowbarRed
# Sunrise-End
- type: entity
id: ClosetWallEmergencyFilledRandom
@ -47,25 +49,27 @@
components:
- type: StorageFill
contents:
- id: ClothingOuterSuitEmergency
- id: ClothingMaskBreath
- id: ClothingOuterSuitEmergency
- id: EmergencyOxygenTankFilled
prob: 0.80
orGroup: EmergencyTankOrRegularTank
prob: 0.5
orGroup: OxygenTank
- id: OxygenTankFilled
prob: 0.20
orGroup: EmergencyTankOrRegularTank
prob: 0.5
orGroup: OxygenTank
- id: ToolboxEmergencyFilled
prob: 0.4
prob: 0.5
- id: MedkitOxygenFilled
prob: 0.2
- id: WeaponFlareGun
prob: 0.05
- id: BoxMRE
prob: 0.1
# Sunrise-Start
- id: CrowbarRed
- id: CrowbarRed
- id: CrowbarRed
# Sunrise-End
- type: entity
id: ClosetEmergencyN2FilledRandom
@ -75,49 +79,75 @@
- type: StorageFill
contents:
- id: ClothingMaskBreath
- id: ClothingOuterSuitEmergency
- id: EmergencyNitrogenTankFilled
prob: 0.80
orGroup: EmergencyTankOrRegularTank
prob: 0.5
orGroup: NitrogenTank
- id: NitrogenTankFilled
prob: 0.20
orGroup: EmergencyTankOrRegularTank
prob: 0.5
orGroup: NitrogenTank
# Sunrise-Start
- id: CrowbarRed
- id: CrowbarRed
- id: CrowbarRed
# Sunrise-End
- type: entity
id: ClosetFireFilled
parent: ClosetFire
suffix: Filled
components:
- type: StorageFill
contents:
- id: ClothingOuterSuitFire
- id: ClothingHeadHelmetFire
- id: ClothingMaskGas
- id: OxygenTankFilled
- id: FireExtinguisher
prob: 0.25
- id: CrowbarRed
- id: CrowbarRed
- id: CrowbarRed
- type: StorageFill
contents:
- id: ClothingOuterSuitFire
- id: ClothingHeadHelmetFire
- id: ClothingMaskGas
- id: EmergencyOxygenTankFilled
prob: 0.5
orGroup: OxygenTank
- id: OxygenTankFilled
prob: 0.5
orGroup: OxygenTank
- id: CrowbarRed
- id: FireExtinguisher
prob: 0.98
orGroup: FireExtinguisher
- id: SprayBottleWater #It's just budget cut after budget cut man
prob: 0.02
orGroup: FireExtinguisher
# Sunrise-Start
- id: CrowbarRed
- id: CrowbarRed
- id: CrowbarRed
# Sunrise-End
- type: entity
id: ClosetWallFireFilledRandom
parent: ClosetWallFire
suffix: Filled
components:
- type: StorageFill
contents:
- id: ClothingOuterSuitFire
- id: ClothingHeadHelmetFire
- id: ClothingMaskGas
- id: OxygenTankFilled
- id: FireExtinguisher
prob: 0.25
- id: CrowbarRed
- id: CrowbarRed
- id: CrowbarRed
- type: StorageFill
contents:
- id: ClothingOuterSuitFire
- id: ClothingHeadHelmetFire
- id: ClothingMaskGas
- id: EmergencyOxygenTankFilled
prob: 0.5
orGroup: OxygenTank
- id: OxygenTankFilled
prob: 0.5
orGroup: OxygenTank
# Sunrise-Start
- id: CrowbarRed
- id: CrowbarRed
- id: CrowbarRed
# Sunrise-End
- id: FireExtinguisher
prob: 0.98
orGroup: FireExtinguisher
- id: SprayBottleWater #It's just budget cut after budget cut man
prob: 0.02
orGroup: FireExtinguisher
- type: entity
id: ClosetMaintenanceFilledRandom

View file

@ -8,6 +8,7 @@
RubberStampApproved: 1
RubberStampDenied: 1
Paper: 10
Envelope: 10
EncryptionKeyCargo: 2
EncryptionKeyEngineering: 2
EncryptionKeyMedical: 2

View file

@ -113,7 +113,7 @@
modifiers:
coefficients:
Heat: 0.90
Radiation: 0.001
Radiation: 0.01
- type: Clothing
sprite: Clothing/OuterClothing/Suits/rad.rsi
- type: GroupExamine

View file

@ -1250,6 +1250,17 @@
tags:
- VimPilot
- DoorBumpOpener
- type: Reactive
groups:
Flammable: [ Touch ]
Extinguish: [ Touch ]
reactions:
- reagents: [ Water, SpaceCleaner ]
methods: [ Touch ]
effects:
- !type:WashCreamPieReaction
- type: entity
name: monkey
@ -1283,6 +1294,7 @@
clumsySound:
path: /Audio/Animals/monkey_scream.ogg
- type: entity
name: monkey
id: MobBaseSyndicateMonkey
@ -1434,7 +1446,7 @@
- type: SentienceTarget
flavorKind: station-event-random-sentience-flavor-kobold
- type: GhostRole
prob: 0.1
prob: 0.05
makeSentient: true
name: ghost-role-information-kobold-name
description: ghost-role-information-kobold-description

View file

@ -100,6 +100,8 @@
- id: FoodMeatCorgi
amount: 2
- id: MaterialHideCorgi
- type: StealTarget
stealGroup: AnimalIan
- type: entity
name: Runtime

View file

@ -257,14 +257,12 @@
- type: entity
name: blueberry pancake
parent: FoodBakedBase
parent: FoodBakedPancake
id: FoodBakedPancakeBb
description: A fluffy and delicious blueberry pancake.
components:
- type: Stack
stackType: Pancake
count: 1
composite: true
stackType: PancakeBb
layerStates:
- pancakesbb1
- pancakesbb2
@ -281,7 +279,6 @@
- state: pancakesbb3
map: ["pancakesbb3"]
visible: false
- type: Appearance
- type: Tag
tags:
- Pancake
@ -289,14 +286,12 @@
- type: entity
name: chocolate chip pancake
parent: FoodBakedBase
parent: FoodBakedPancake
id: FoodBakedPancakeCc
description: A fluffy and delicious chocolate chip pancake.
components:
- type: Stack
stackType: Pancake
count: 1
composite: true
stackType: PancakeCc
layerStates:
- pancakescc1
- pancakescc2
@ -313,7 +308,6 @@
- state: pancakescc3
map: ["pancakescc3"]
visible: false
- type: Appearance
- type: SolutionContainerManager
solutions:
food:
@ -323,9 +317,6 @@
Quantity: 5
- ReagentId: Theobromine
Quantity: 1
- type: Tag
tags:
- Pancake
- type: entity
name: waffles

View file

@ -95,6 +95,21 @@
- type: Produce
seedId: sugarcane
- type: entity
name: papercane roll
description: Why do we even need to grow paper?
id: Papercane
parent: ProduceBase
components:
- type: Sprite
sprite: Objects/Specific/Hydroponics/papercane.rsi
- type: SolutionContainerManager
- type: Produce
seedId: papercane
- type: Log
spawnedPrototype: SheetPaper1
spawnCount: 2
- type: entity
parent: FoodProduceBase
id: FoodLaughinPeaPod
@ -869,6 +884,42 @@
tags:
- Fruit
- type: entity
name: golden apple
parent: FoodProduceBase
id: FoodGoldenApple
description: It should be shaped like a cube, shouldn't it?
components:
- type: FlavorProfile
flavors:
- apple
- metallic
- type: SolutionContainerManager
solutions:
food:
maxVol: 30
reagents:
- ReagentId: Nutriment
Quantity: 10
- ReagentId: Vitamin
Quantity: 4
- ReagentId: DoctorsDelight
Quantity: 13
- type: Sprite
sprite: Objects/Specific/Hydroponics/golden_apple.rsi
- type: Produce
seedId: goldenApple
- type: Extractable
juiceSolution:
reagents:
- ReagentId: JuiceApple
Quantity: 10
- ReagentId: Gold
Quantity: 10
- type: Tag
tags:
- Fruit
- type: entity
name: cocoa pod
parent: FoodProduceBase
@ -1411,6 +1462,71 @@
- Galaxythistle
- Fruit # Probably?
- type: entity
name: glasstle
parent: FoodProduceBase
id: FoodGlasstle
description: A fragile crystal plant with lot of spiky thorns.
components:
- type: Item
size: Small
sprite: Objects/Specific/Hydroponics/glasstle.rsi
heldPrefix: produce
- type: FlavorProfile
flavors:
- sharp
- type: SolutionContainerManager
solutions:
food:
maxVol: 15
reagents:
- ReagentId: Razorium
Quantity: 15
- type: Sprite
sprite: Objects/Specific/Hydroponics/glasstle.rsi
- type: Produce
seedId: glasstle
- type: Extractable
grindableSolutionName: food
- type: Damageable
damageContainer: Inorganic
- type: ToolRefinable
refineResult:
- id: SheetGlass1
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 10
behaviors:
- !type:PlaySoundBehavior
sound:
collection: GlassBreak
params:
volume: -4
- !type:SpawnEntitiesBehavior
spawn:
ShardGlass:
min: 1
max: 1
- !type:DoActsBehavior
acts: [ "Destruction" ]
- type: DamageOnHit
damage:
types:
Blunt: 10
- type: MeleeWeapon
wideAnimationRotation: 60
damage:
types:
Slash: 15
soundHit:
path: /Audio/Weapons/bladeslice.ogg
- type: Tag
tags:
- Galaxythistle
- type: entity
name: fly amanita
parent: FoodProduceBase

View file

@ -301,6 +301,12 @@
Quantity: 10
- ReagentId: JuiceThatMakesYouWeh
Quantity: 10
- type: Clothing
quickEquip: false
sprite: Objects/Fun/toys.rsi
equippedPrefix: lizard
slots:
- HEAD
- type: entity
parent: PlushieLizard

View file

@ -568,3 +568,81 @@
Blunt: 10
- type: StealTarget
stealGroup: BoxFolderQmClipboard
- type: entity
name: envelope
parent: BaseItem
id: Envelope
description: 'A small envelope for keeping prying eyes off of your sensitive documents.'
components:
- type: Sprite
sprite: Objects/Misc/bureaucracy.rsi
layers:
- state: envelope_open
map: ["enum.EnvelopeVisualLayers.Open"]
- state: envelope_closed
map: ["enum.EnvelopeVisualLayers.Sealed"]
visible: false
- state: envelope_torn
map: ["enum.EnvelopeVisualLayers.Torn"]
visible: false
- state: paper_stamp-generic
map: ["enum.PaperVisualLayers.Stamp"]
visible: false
- type: Paper
escapeFormatting: false
content: envelope-default-message
- type: PaperVisuals
headerImagePath: "/Textures/Interface/Paper/paper_heading_postage_stamp.svg.96dpi.png"
headerMargin: 216.0, 0.0, 0.0, 0.0
contentMargin: 0.0, 0.0, 0.0, 0.0
maxWritableArea: 368.0, 256.0
- type: Envelope
- type: ContainerContainer
containers:
letter_slot: !type:ContainerSlot
- type: ItemSlots
slots:
letter_slot:
name: envelope-letter-slot
insertSound: /Audio/Effects/packetrip.ogg
ejectSound: /Audio/Effects/packetrip.ogg
whitelist:
tags:
- Paper
- type: ActivatableUI
key: enum.PaperUiKey.Key
requireHands: false
- type: UserInterface
interfaces:
enum.PaperUiKey.Key:
type: PaperBoundUserInterface
- type: Item
size: Tiny
- type: Tag
tags:
- Trash
- Document
#- type: Appearance, hide stamp marks until we have some kind of displacement
- type: Flammable
fireSpread: true
canResistFire: false
alwaysCombustible: true
canExtinguish: true
damage:
types:
Heat: 1
- type: FireVisuals
sprite: Effects/fire.rsi
normalState: fire
- type: Damageable
damageModifierSet: Wood
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 15
behaviors:
- !type:EmptyAllContainersBehaviour
- !type:DoActsBehavior
acts: [ "Destruction" ]

View file

@ -163,6 +163,16 @@
- type: Sprite
sprite: Objects/Specific/Hydroponics/sugarcane.rsi
- type: entity
parent: SeedBase
name: packet of papercane seeds
id: PapercaneSeeds
components:
- type: Seed
seedId: papercane
- type: Sprite
sprite: Objects/Specific/Hydroponics/papercane.rsi
- type: entity
parent: SeedBase
name: packet of tower cap spores
@ -243,6 +253,16 @@
- type: Sprite
sprite: Objects/Specific/Hydroponics/apple.rsi
- type: entity
parent: SeedBase
name: packet of golden apple seeds
id: GoldenAppleSeeds
components:
- type: Seed
seedId: goldenApple
- type: Sprite
sprite: Objects/Specific/Hydroponics/golden_apple.rsi
- type: entity
parent: SeedBase
name: packet of corn seeds
@ -427,6 +447,17 @@
- type: Sprite
sprite: Objects/Specific/Hydroponics/galaxythistle.rsi
- type: entity
parent: SeedBase
name: packet of glasstle seeds
description: "Scars of gloomy nights."
id: GlasstleSeeds
components:
- type: Seed
seedId: glasstle
- type: Sprite
sprite: Objects/Specific/Hydroponics/glasstle.rsi
- type: entity
parent: SeedBase
name: packet of fly amanita spores

View file

@ -76,9 +76,6 @@
components:
- type: Sprite
sprite: Objects/Weapons/Guns/Pistols/viper.rsi
availableModes:
- FullAuto
- SemiAuto
- type: ItemSlots
slots:
gun_magazine:

View file

@ -62,12 +62,7 @@
- /Maps/_Sunrise/Shuttles/security.yml # Sunrise-edit
nameGrid: false
# Слишком большая нагрузка ради 3 человек
# ruins: !type:GridSpawnGroup
# hide: true
# nameGrid: true
# minCount: 2
# maxCount: 2
# stationGrid: false
# security: !type:GridSpawnGroup
# paths:
# - /Maps/Ruins/chunked_tcomms.yml
# - /Maps/Ruins/biodome_satellite.yml
@ -80,7 +75,8 @@
# - /Maps/Ruins/whiteship_bluespacejumper.yml
# Ебейшие лаги и куча гридов
# vgroid: !type:DungeonSpawnGroup
# minimumDistance: 1000
# minimumDistance: 400
# maximumDistance: 450
# nameDataset: names_borer
# stationGrid: false
# addComponents:

View file

@ -37,6 +37,17 @@
- Chemist
- type: StealTarget
stealGroup: ChemDispenser
- type: Fixtures
fixtures:
fix1:
shape:
!type:PhysShapeAabb
bounds: "-0.25, -0.4, 0.25, 0.4"
density: 190
mask:
- MachineMask
layer:
- MachineLayer
- type: entity
id: ChemDispenserEmpty

View file

@ -115,6 +115,8 @@
- type: Lathe
idleState: icon
runningState: building
unlitIdleState: unlit
unlitRunningState: unlit-building
staticRecipes:
- Wirecutter
- Igniter
@ -270,6 +272,8 @@
- type: Lathe
idleState: icon
runningState: building
unlitIdleState: unlit
unlitRunningState: unlit-building
staticRecipes:
- LargeBeaker
- Dropper
@ -1237,6 +1241,7 @@
- IngotGold30
- IngotSilver30
- MaterialBananium10
- MaterialDiamond
- type: entity
parent: BaseLathe

View file

@ -150,6 +150,8 @@
suffix: keg
description: You probably shouldn't stick around to see if this is armed. It has a tap on the side.
components:
- type: Transform
anchored: false
- type: NukeLabel
- type: Sprite
sprite: Objects/Devices/nuke.rsi
@ -183,11 +185,11 @@
shape:
!type:PhysShapeCircle
radius: 0.45
density: 80
density: 255
mask:
- TabletopMachineMask
- MachineMask
layer:
- TabletopMachineLayer
- WallLayer
- type: SolutionContainerManager
solutions:
tank:

View file

@ -25,6 +25,8 @@
- type: Wires
boardName: wires-board-name-pa
layoutId: ParticleAccelerator
- type: AccessReader
access: [["Engineering"]]
# Unfinished

Some files were not shown because too many files have changed in this diff Show more