mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-18 14:41:17 +03:00
Merge pull request #853 from space-syndicate/upstream-sync
Upstream sync
This commit is contained in:
commit
090aa51074
153 changed files with 30152 additions and 1739 deletions
6
.github/workflows/publish.yml
vendored
6
.github/workflows/publish.yml
vendored
|
|
@ -71,3 +71,9 @@ jobs:
|
|||
username: ${{ secrets.BUILDS_USERNAME }}
|
||||
key: ${{ secrets.BUILDS_SSH_KEY }}
|
||||
script: node ~/scripts/push_to_manifest.js -fork syndicate -id ${{ github.sha }}
|
||||
|
||||
- name: Publish changelog
|
||||
run: Tools/actions_changelogs_since_last_run.py
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.CHANGELOG_DISCORD_WEBHOOK }}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Content.Shared.Verbs;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.Systems
|
||||
{
|
||||
|
|
@ -26,7 +27,7 @@ namespace Content.Client.Administration.Systems
|
|||
Verb verb = new();
|
||||
verb.Category = VerbCategory.Debug;
|
||||
verb.Text = "View Variables";
|
||||
verb.IconTexture = "/Textures/Interface/VerbIcons/vv.svg.192dpi.png";
|
||||
verb.Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/vv.svg.192dpi.png"));
|
||||
verb.Act = () => _clientConsoleHost.ExecuteCommand($"vv {args.Target}");
|
||||
verb.ClientExclusive = true; // opening VV window is client-side. Don't ask server to run this verb.
|
||||
args.Verbs.Add(verb);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
<BoxContainer Orientation="Vertical" MinWidth="200">
|
||||
<LineEdit Name="PlayerSearch" Access="Public" StyleClasses="actionSearchBox"
|
||||
HorizontalExpand="true" PlaceHolder="{Loc admin-logs-search-players-placeholder}"/>
|
||||
<Button Name="IncludeNonPlayersButton" Text="{Loc admin-logs-include-non-player}"
|
||||
MinWidth="100" StyleClasses="ButtonSquare" ToggleMode="True" />
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button Name="SelectAllPlayersButton" Text="{Loc admin-logs-select-all}"
|
||||
MinWidth="100" StyleClasses="ButtonSquare" />
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ public sealed partial class AdminLogsControl : Control
|
|||
SelectAllTypesButton.OnPressed += SelectAllTypes;
|
||||
SelectNoTypesButton.OnPressed += SelectNoTypes;
|
||||
|
||||
IncludeNonPlayersButton.OnPressed += IncludeNonPlayers;
|
||||
SelectAllPlayersButton.OnPressed += SelectAllPlayers;
|
||||
SelectNoPlayersButton.OnPressed += SelectNoPlayers;
|
||||
|
||||
|
|
@ -53,6 +54,7 @@ public sealed partial class AdminLogsControl : Control
|
|||
public string Search => LogSearch.Text;
|
||||
private int ShownLogs { get; set; }
|
||||
private int TotalLogs { get; set; }
|
||||
public bool IncludeNonPlayerLogs { get; set; }
|
||||
|
||||
public HashSet<LogType> SelectedTypes { get; } = new();
|
||||
|
||||
|
|
@ -139,6 +141,13 @@ public sealed partial class AdminLogsControl : Control
|
|||
UpdateLogs();
|
||||
}
|
||||
|
||||
private void IncludeNonPlayers(ButtonEventArgs args)
|
||||
{
|
||||
IncludeNonPlayerLogs = args.Button.Pressed;
|
||||
|
||||
UpdateLogs();
|
||||
}
|
||||
|
||||
private void SelectAllPlayers(ButtonEventArgs args)
|
||||
{
|
||||
SelectedPlayers.Clear();
|
||||
|
|
@ -259,12 +268,33 @@ public sealed partial class AdminLogsControl : Control
|
|||
button.Text.Contains(PlayerSearch.Text, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private bool LogMatchesPlayerFilter(AdminLogLabel label)
|
||||
{
|
||||
if (label.Log.Players.Length == 0)
|
||||
return SelectedPlayers.Count == 0 || IncludeNonPlayerLogs;
|
||||
|
||||
return SelectedPlayers.Overlaps(label.Log.Players);
|
||||
}
|
||||
|
||||
private bool ShouldShowLog(AdminLogLabel label)
|
||||
{
|
||||
return SelectedTypes.Contains(label.Log.Type) &&
|
||||
(SelectedPlayers.Count + label.Log.Players.Length == 0 || SelectedPlayers.Overlaps(label.Log.Players)) &&
|
||||
SelectedImpacts.Contains(label.Log.Impact) &&
|
||||
label.Log.Message.Contains(LogSearch.Text, StringComparison.OrdinalIgnoreCase);
|
||||
// Check log type
|
||||
if (!SelectedTypes.Contains(label.Log.Type))
|
||||
return false;
|
||||
|
||||
// Check players
|
||||
if (!LogMatchesPlayerFilter(label))
|
||||
return false;
|
||||
|
||||
// Check impact
|
||||
if (!SelectedImpacts.Contains(label.Log.Impact))
|
||||
return false;
|
||||
|
||||
// Check search
|
||||
if (!label.Log.Message.Contains(LogSearch.Text, StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void TypeButtonPressed(ButtonEventArgs args)
|
||||
|
|
@ -481,6 +511,7 @@ public sealed partial class AdminLogsControl : Control
|
|||
SelectAllTypesButton.OnPressed -= SelectAllTypes;
|
||||
SelectNoTypesButton.OnPressed -= SelectNoTypes;
|
||||
|
||||
IncludeNonPlayersButton.OnPressed -= IncludeNonPlayers;
|
||||
SelectAllPlayersButton.OnPressed -= SelectAllPlayers;
|
||||
SelectNoPlayersButton.OnPressed -= SelectNoPlayers;
|
||||
|
||||
|
|
|
|||
|
|
@ -51,8 +51,10 @@ public sealed class AdminLogsEui : BaseEui
|
|||
null,
|
||||
null,
|
||||
null,
|
||||
LogsControl.SelectedPlayers.Count != 0,
|
||||
LogsControl.SelectedPlayers.ToArray(),
|
||||
null,
|
||||
LogsControl.IncludeNonPlayerLogs,
|
||||
null,
|
||||
DateOrder.Descending);
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace Content.Client.Audio
|
|||
private float _ambienceVolume = 0.0f;
|
||||
|
||||
private static AudioParams _params = AudioParams.Default.WithVariation(0.01f).WithLoop(true).WithAttenuation(Attenuation.LinearDistance);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// How many times we can be playing 1 particular sound at once.
|
||||
/// </summary>
|
||||
|
|
@ -221,13 +221,18 @@ namespace Content.Client.Audio
|
|||
private void ProcessNearbyAmbience(TransformComponent playerXform)
|
||||
{
|
||||
var query = GetEntityQuery<TransformComponent>();
|
||||
var metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
var mapPos = playerXform.MapPosition;
|
||||
|
||||
// Remove out-of-range ambiences
|
||||
foreach (var (comp, sound) in _playingSounds)
|
||||
{
|
||||
var entity = comp.Owner;
|
||||
if (comp.Enabled && query.TryGetComponent(entity, out var xform) && xform.MapID == playerXform.MapID)
|
||||
|
||||
if (comp.Enabled &&
|
||||
query.TryGetComponent(entity, out var xform) &&
|
||||
xform.MapID == playerXform.MapID &&
|
||||
!metaQuery.GetComponent(entity).EntityPaused)
|
||||
{
|
||||
var distance = (xform.ParentUid == playerXform.ParentUid)
|
||||
? xform.LocalPosition - playerXform.LocalPosition
|
||||
|
|
@ -265,7 +270,10 @@ namespace Content.Client.Audio
|
|||
|
||||
foreach (var (_, comp) in sources)
|
||||
{
|
||||
if (_playingSounds.ContainsKey(comp))
|
||||
var uid = comp.Owner;
|
||||
|
||||
if (_playingSounds.ContainsKey(comp) ||
|
||||
metaQuery.GetComponent(uid).EntityPaused)
|
||||
continue;
|
||||
|
||||
var audioParams = _params
|
||||
|
|
@ -274,7 +282,7 @@ namespace Content.Client.Audio
|
|||
.WithPlayOffset(_random.NextFloat(0.0f, 100.0f))
|
||||
.WithMaxDistance(comp.Range);
|
||||
|
||||
var stream = _audio.PlayPvs(comp.Sound, comp.Owner, audioParams);
|
||||
var stream = _audio.PlayPvs(comp.Sound, uid, audioParams);
|
||||
if (stream == null)
|
||||
continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -49,11 +49,9 @@ public sealed class ExamineButton : ContainerButton
|
|||
SetHeight = ElementHeight
|
||||
};
|
||||
|
||||
if (verb.IconTexture != null)
|
||||
if (verb.Icon != null)
|
||||
{
|
||||
var icon = new SpriteSpecifier.Texture(new ResourcePath(verb.IconTexture));
|
||||
|
||||
Icon.Texture = icon.Frame0();
|
||||
Icon.Texture = verb.Icon.Frame0();
|
||||
Icon.Stretch = TextureRect.StretchMode.KeepAspectCentered;
|
||||
|
||||
AddChild(Icon);
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ namespace Content.Client.Examine
|
|||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.ExamineEntity, new PointerInputCmdHandler(HandleExamine, outsidePrediction: true))
|
||||
.Register<ExamineSystem>();
|
||||
|
||||
|
||||
_idCounter = 0;
|
||||
}
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ namespace Content.Client.Examine
|
|||
// Center it on the entity if they use the verb instead.
|
||||
verb.Act = () => DoExamine(args.Target, false);
|
||||
verb.Text = Loc.GetString("examine-verb-name");
|
||||
verb.IconTexture = "/Textures/Interface/VerbIcons/examine.svg.192dpi.png";
|
||||
verb.Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/examine.svg.192dpi.png"));
|
||||
verb.ShowOnExamineTooltip = false;
|
||||
verb.ClientExclusive = true;
|
||||
args.Verbs.Add(verb);
|
||||
|
|
@ -132,7 +132,7 @@ namespace Content.Client.Examine
|
|||
// Prevent updating a new tooltip.
|
||||
if (ev.Id != 0 && ev.Id != _idCounter)
|
||||
return;
|
||||
|
||||
|
||||
// Tooltips coming in from the server generally prioritize
|
||||
// opening at the old tooltip rather than the cursor/another entity,
|
||||
// since there's probably one open already if it's coming in from the server.
|
||||
|
|
@ -286,7 +286,7 @@ namespace Content.Client.Examine
|
|||
if (verb is not ExamineVerb examine)
|
||||
continue;
|
||||
|
||||
if (examine.IconTexture == null)
|
||||
if (examine.Icon == null)
|
||||
continue;
|
||||
|
||||
if (!examine.ShowOnExamineTooltip)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ using Robust.Shared.Input;
|
|||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Guidebook;
|
||||
|
||||
|
|
@ -56,7 +57,7 @@ public sealed class GuidebookSystem : EntitySystem
|
|||
args.Verbs.Add(new()
|
||||
{
|
||||
Text = Loc.GetString("guide-help-verb"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/information.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/information.svg.192dpi.png")),
|
||||
Act = () => OpenGuidebook(component.Guides, includeChildren: component.IncludeChildren, selected: component.Guides[0]),
|
||||
ClientExclusive = true,
|
||||
CloseMenu = true
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using Content.Shared.Mobs.Systems;
|
|||
using Content.Shared.Pointing;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using DrawDepth = Content.Shared.DrawDepth.DrawDepth;
|
||||
|
||||
namespace Content.Client.Pointing;
|
||||
|
|
@ -54,7 +55,7 @@ public sealed class PointingSystem : SharedPointingSystem
|
|||
Verb verb = new()
|
||||
{
|
||||
Text = Loc.GetString("pointing-verb-get-data-text"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/point.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/point.svg.192dpi.png")),
|
||||
ClientExclusive = true,
|
||||
Act = () => RaiseNetworkEvent(new PointingAttemptEvent(args.Target))
|
||||
};
|
||||
|
|
|
|||
10
Content.Client/Storage/Components/EntityStorageComponent.cs
Normal file
10
Content.Client/Storage/Components/EntityStorageComponent.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
using Content.Shared.Storage.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Storage.Components;
|
||||
|
||||
[RegisterComponent, ComponentReference(typeof(SharedEntityStorageComponent))]
|
||||
public sealed class EntityStorageComponent : SharedEntityStorageComponent
|
||||
{
|
||||
|
||||
}
|
||||
8
Content.Client/Storage/Systems/EntityStorageSystem.cs
Normal file
8
Content.Client/Storage/Systems/EntityStorageSystem.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using Content.Shared.Storage.EntitySystems;
|
||||
|
||||
namespace Content.Client.Storage.Systems;
|
||||
|
||||
public sealed class EntityStorageSystem : SharedEntityStorageSystem
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -13,7 +13,10 @@
|
|||
VerticalExpand="False"
|
||||
VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Center">
|
||||
<SplitContainer Name="ScreenContainer" HorizontalExpand="True" VerticalExpand="True">
|
||||
<SplitContainer Name="ScreenContainer"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="True"
|
||||
SplitWidth="0">
|
||||
<LayoutContainer Name="ViewportContainer" HorizontalExpand="True" VerticalExpand="True">
|
||||
<controls:MainViewport Name="MainViewport"/>
|
||||
<widgets:GhostGui Name="Ghost" Access="Protected" />
|
||||
|
|
|
|||
|
|
@ -17,59 +17,60 @@ namespace Content.IntegrationTests.Tests.Access
|
|||
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
|
||||
var server = pairTracker.Pair.Server;
|
||||
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var system = EntitySystem.Get<AccessReaderSystem>();
|
||||
|
||||
// test empty
|
||||
var reader = new AccessReaderComponent();
|
||||
Assert.That(system.IsAllowed(new[] { "Foo" }, reader), Is.True);
|
||||
Assert.That(system.IsAllowed(new[] { "Bar" }, reader), Is.True);
|
||||
Assert.That(system.IsAllowed(new string[] { }, reader), Is.True);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "Foo" }, reader), Is.True);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "Bar" }, reader), Is.True);
|
||||
Assert.That(system.AreAccessTagsAllowed(new string[] { }, reader), Is.True);
|
||||
|
||||
// test deny
|
||||
reader = new AccessReaderComponent();
|
||||
reader.DenyTags.Add("A");
|
||||
Assert.That(system.IsAllowed(new[] { "Foo" }, reader), Is.True);
|
||||
Assert.That(system.IsAllowed(new[] { "A" }, reader), Is.False);
|
||||
Assert.That(system.IsAllowed(new[] { "A", "Foo" }, reader), Is.False);
|
||||
Assert.That(system.IsAllowed(new string[] { }, reader), Is.True);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "Foo" }, reader), Is.True);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.False);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "A", "Foo" }, reader), Is.False);
|
||||
Assert.That(system.AreAccessTagsAllowed(new string[] { }, reader), Is.True);
|
||||
|
||||
// test one list
|
||||
reader = new AccessReaderComponent();
|
||||
reader.AccessLists.Add(new HashSet<string> { "A" });
|
||||
Assert.That(system.IsAllowed(new[] { "A" }, reader), Is.True);
|
||||
Assert.That(system.IsAllowed(new[] { "B" }, reader), Is.False);
|
||||
Assert.That(system.IsAllowed(new[] { "A", "B" }, reader), Is.True);
|
||||
Assert.That(system.IsAllowed(new string[] { }, reader), Is.False);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.True);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.True);
|
||||
Assert.That(system.AreAccessTagsAllowed(new string[] { }, reader), Is.False);
|
||||
|
||||
// test one list - two items
|
||||
reader = new AccessReaderComponent();
|
||||
reader.AccessLists.Add(new HashSet<string> { "A", "B" });
|
||||
Assert.That(system.IsAllowed(new[] { "A" }, reader), Is.False);
|
||||
Assert.That(system.IsAllowed(new[] { "B" }, reader), Is.False);
|
||||
Assert.That(system.IsAllowed(new[] { "A", "B" }, reader), Is.True);
|
||||
Assert.That(system.IsAllowed(new string[] { }, reader), Is.False);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.False);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.True);
|
||||
Assert.That(system.AreAccessTagsAllowed(new string[] { }, reader), Is.False);
|
||||
|
||||
// test two list
|
||||
reader = new AccessReaderComponent();
|
||||
reader.AccessLists.Add(new HashSet<string> { "A" });
|
||||
reader.AccessLists.Add(new HashSet<string> { "B", "C" });
|
||||
Assert.That(system.IsAllowed(new[] { "A" }, reader), Is.True);
|
||||
Assert.That(system.IsAllowed(new[] { "B" }, reader), Is.False);
|
||||
Assert.That(system.IsAllowed(new[] { "A", "B" }, reader), Is.True);
|
||||
Assert.That(system.IsAllowed(new[] { "C", "B" }, reader), Is.True);
|
||||
Assert.That(system.IsAllowed(new[] { "C", "B", "A" }, reader), Is.True);
|
||||
Assert.That(system.IsAllowed(new string[] { }, reader), Is.False);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.True);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.True);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "C", "B" }, reader), Is.True);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "C", "B", "A" }, reader), Is.True);
|
||||
Assert.That(system.AreAccessTagsAllowed(new string[] { }, reader), Is.False);
|
||||
|
||||
// test deny list
|
||||
reader = new AccessReaderComponent();
|
||||
reader.AccessLists.Add(new HashSet<string> { "A" });
|
||||
reader.DenyTags.Add("B");
|
||||
Assert.That(system.IsAllowed(new[] { "A" }, reader), Is.True);
|
||||
Assert.That(system.IsAllowed(new[] { "B" }, reader), Is.False);
|
||||
Assert.That(system.IsAllowed(new[] { "A", "B" }, reader), Is.False);
|
||||
Assert.That(system.IsAllowed(new string[] { }, reader), Is.False);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.True);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
|
||||
Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.False);
|
||||
Assert.That(system.AreAccessTagsAllowed(new string[] { }, reader), Is.False);
|
||||
});
|
||||
await pairTracker.CleanReturnAsync();
|
||||
}
|
||||
|
|
|
|||
42
Content.IntegrationTests/Tests/SalvageTest.cs
Normal file
42
Content.IntegrationTests/Tests/SalvageTest.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
using System.Threading.Tasks;
|
||||
using Content.Server.Salvage;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.IntegrationTests.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class SalvageTest
|
||||
{
|
||||
[Test]
|
||||
public async Task SalvageGridBoundsTest()
|
||||
{
|
||||
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
|
||||
var server = pairTracker.Pair.Server;
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapLoader = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
foreach (var salvage in protoManager.EnumeratePrototypes<SalvageMapPrototype>())
|
||||
{
|
||||
var mapId = mapMan.CreateMap();
|
||||
mapLoader.TryLoad(mapId, salvage.MapPath.ToString(), out var rootUids);
|
||||
Assert.That(rootUids is { Count: 1 }, $"Salvage map {salvage.ID} does not have a single grid");
|
||||
var grid = rootUids[0];
|
||||
Assert.That(entManager.TryGetComponent<MapGridComponent>(grid, out var gridComp), $"Salvage {salvage.ID}'s grid does not have GridComponent.");
|
||||
Assert.That(gridComp.LocalAABB, Is.EqualTo(salvage.Bounds), $"Salvage {salvage.ID}'s bounds {gridComp.LocalAABB} are not equal to the bounds on the prototype {salvage.Bounds}");
|
||||
}
|
||||
});
|
||||
await pairTracker.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Server.StationRecords;
|
||||
using Content.Server.StationRecords.Systems;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Administration.Logs;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ public sealed class IdExaminableSystem : EntitySystem
|
|||
Category = VerbCategory.Examine,
|
||||
Disabled = !detailsRange,
|
||||
Message = Loc.GetString("id-examinable-component-verb-disabled"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/information.svg.192dpi.png"
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/character.svg.192dpi.png"))
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
|
|
|
|||
|
|
@ -122,14 +122,25 @@ public sealed partial class AdminLogManager
|
|||
query = query.Where(log => log.Date > filter.After);
|
||||
}
|
||||
|
||||
if (filter.AnyPlayers != null)
|
||||
if (filter.IncludePlayers)
|
||||
{
|
||||
query = query.Where(log => filter.AnyPlayers.Any(filterPlayer => log.Players.Contains(filterPlayer)));
|
||||
}
|
||||
if (filter.AnyPlayers != null)
|
||||
{
|
||||
query = query.Where(log =>
|
||||
filter.AnyPlayers.Any(filterPlayer => log.Players.Contains(filterPlayer)) ||
|
||||
log.Players.Length == 0 && filter.IncludeNonPlayers);
|
||||
}
|
||||
|
||||
if (filter.AllPlayers != null)
|
||||
if (filter.AllPlayers != null)
|
||||
{
|
||||
query = query.Where(log =>
|
||||
filter.AllPlayers.All(filterPlayer => log.Players.Contains(filterPlayer)) ||
|
||||
log.Players.Length == 0 && filter.IncludeNonPlayers);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
query = query.Where(log => filter.AllPlayers.All(filterPlayer => log.Players.Contains(filterPlayer)));
|
||||
query = query.Where(log => log.Players.Length == 0);
|
||||
}
|
||||
|
||||
if (filter.LogsSent != 0)
|
||||
|
|
|
|||
|
|
@ -119,8 +119,10 @@ public sealed class AdminLogsEui : BaseEui
|
|||
Impacts = request.Impacts,
|
||||
Before = request.Before,
|
||||
After = request.After,
|
||||
IncludePlayers = request.IncludePlayers,
|
||||
AnyPlayers = request.AnyPlayers,
|
||||
AllPlayers = request.AllPlayers,
|
||||
IncludeNonPlayers = request.IncludeNonPlayers,
|
||||
LastLogId = 0,
|
||||
Limit = _clientBatchSize
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,10 +20,14 @@ public sealed class LogFilter
|
|||
|
||||
public DateTime? After { get; set; }
|
||||
|
||||
public bool IncludePlayers { get; set; } = true;
|
||||
|
||||
public Guid[]? AnyPlayers { get; set; }
|
||||
|
||||
public Guid[]? AllPlayers { get; set; }
|
||||
|
||||
public bool IncludeNonPlayers { get; set; }
|
||||
|
||||
public int? LastLogId { get; set; }
|
||||
|
||||
public int LogsSent { get; set; }
|
||||
|
|
|
|||
|
|
@ -57,6 +57,14 @@ namespace Content.Server.Administration.Managers
|
|||
return null;
|
||||
}
|
||||
|
||||
public AdminData? GetAdminData(EntityUid uid, bool includeDeAdmin = false)
|
||||
{
|
||||
if (_playerManager.TryGetSessionByEntity(uid, out var session) && session is IPlayerSession playerSession)
|
||||
return GetAdminData(playerSession, includeDeAdmin);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void DeAdmin(IPlayerSession session)
|
||||
{
|
||||
if (!_admins.TryGetValue(session, out var reg))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Content.Shared.Administration;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.Player;
|
||||
|
||||
|
||||
|
|
@ -47,6 +47,26 @@ namespace Content.Server.Administration.Managers
|
|||
/// <returns><see langword="null" /> if the player is not an admin.</returns>
|
||||
AdminData? GetAdminData(IPlayerSession session, bool includeDeAdmin = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the admin data for a player, if they are an admin.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity being controlled by the player.</param>
|
||||
/// <param name="includeDeAdmin">
|
||||
/// Whether to return admin data for admins that are current de-adminned.
|
||||
/// </param>
|
||||
/// <returns><see langword="null" /> if the player is not an admin.</returns>
|
||||
AdminData? GetAdminData(EntityUid uid, bool includeDeAdmin = false);
|
||||
|
||||
/// <summary>
|
||||
/// See if a player has an admin flag.
|
||||
/// </summary>
|
||||
/// <returns>True if the player is and admin and has the specified flags.</returns>
|
||||
bool HasAdminFlag(EntityUid player, AdminFlags flag)
|
||||
{
|
||||
var data = GetAdminData(player);
|
||||
return data != null && data.HasFlag(flag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See if a player has an admin flag.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Content.Shared.Database;
|
|||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Administration.Notes;
|
||||
|
||||
|
|
@ -33,7 +34,7 @@ public sealed class AdminNotesSystem : EntitySystem
|
|||
{
|
||||
Text = Loc.GetString("admin-notes-verb-text"),
|
||||
Category = VerbCategory.Admin,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/examine.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/examine.svg.192dpi.png")),
|
||||
Act = () => _console.RemoteExecuteCommand(user, $"{OpenAdminNotesCommand.CommandName} \"{target.UserId}\""),
|
||||
Impact = LogImpact.Low
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Content.Shared.Database;
|
|||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Administration.Systems;
|
||||
|
||||
|
|
@ -35,7 +36,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Make Traitor",
|
||||
Category = VerbCategory.Antag,
|
||||
IconTexture = "/Textures/Structures/Wallmounts/posters.rsi/poster5_contraband.png",
|
||||
Icon = new SpriteSpecifier.Rsi((new ResourcePath("/Textures/Structures/Wallmounts/posters.rsi")), "poster5_contraband"),
|
||||
Act = () =>
|
||||
{
|
||||
if (targetMindComp.Mind == null || targetMindComp.Mind.Session == null)
|
||||
|
|
@ -52,7 +53,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Make Zombie",
|
||||
Category = VerbCategory.Antag,
|
||||
IconTexture = "/Textures/Structures/Wallmounts/signs.rsi/bio.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Structures/Wallmounts/signs.rsi"), "bio"),
|
||||
Act = () =>
|
||||
{
|
||||
TryComp(args.Target, out MindComponent? mindComp);
|
||||
|
|
@ -71,7 +72,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Make nuclear operative",
|
||||
Category = VerbCategory.Antag,
|
||||
IconTexture = "/Textures/Structures/Wallmounts/signs.rsi/radiation.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Structures/Wallmounts/signs.rsi"), "radiation"),
|
||||
Act = () =>
|
||||
{
|
||||
if (targetMindComp.Mind == null || targetMindComp.Mind.Session == null)
|
||||
|
|
@ -88,7 +89,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Make Pirate",
|
||||
Category = VerbCategory.Antag,
|
||||
IconTexture = "/Textures/Clothing/Head/Hats/pirate.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Clothing/Head/Hats/pirate.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
if (targetMindComp.Mind == null || targetMindComp.Mind.Session == null)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ using Robust.Shared.Physics.Components;
|
|||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
namespace Content.Server.Administration.Systems;
|
||||
|
|
@ -71,7 +72,6 @@ public sealed partial class AdminVerbSystem
|
|||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifierSystem = default!;
|
||||
[Dependency] private readonly PolymorphableSystem _polymorphableSystem = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||
[Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
|
|
@ -98,7 +98,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Explode",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/smite.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/smite.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
{
|
||||
var coords = Transform(args.Target).MapPosition;
|
||||
|
|
@ -118,7 +118,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Chess Dimension",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Objects/Fun/Tabletop/chessboard.rsi/chessboard.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Fun/Tabletop/chessboard.rsi"), "chessboard"),
|
||||
Act = () =>
|
||||
{
|
||||
_godmodeSystem.EnableGodmode(args.Target); // So they don't suffocate.
|
||||
|
|
@ -146,7 +146,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Set Alight",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/Alerts/Fire/fire.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/Alerts/Fire/fire.png")),
|
||||
Act = () =>
|
||||
{
|
||||
// Fuck you. Burn Forever.
|
||||
|
|
@ -168,7 +168,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Monkeyify",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Mobs/Animals/monkey.rsi/dead.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Mobs/Animals/monkey.rsi"), "dead"),
|
||||
Act = () =>
|
||||
{
|
||||
_polymorphableSystem.PolymorphEntity(args.Target, "AdminMonkeySmite");
|
||||
|
|
@ -182,7 +182,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Garbage Can",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Structures/Piping/disposal.rsi/disposal.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Structures/Piping/disposal.rsi"), "disposal"),
|
||||
Act = () =>
|
||||
{
|
||||
_polymorphableSystem.PolymorphEntity(args.Target, "AdminDisposalsSmite");
|
||||
|
|
@ -198,7 +198,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Lung Cancer",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Mobs/Species/Human/organs.rsi/lung-l.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Mobs/Species/Human/organs.rsi"), "lung-l"),
|
||||
Act = () =>
|
||||
{
|
||||
_diseaseSystem.TryInfect(carrier, _prototypeManager.Index<DiseasePrototype>("StageIIIALungCancer"),
|
||||
|
|
@ -211,13 +211,13 @@ public sealed partial class AdminVerbSystem
|
|||
}
|
||||
|
||||
if (TryComp<DamageableComponent>(args.Target, out var damageable) &&
|
||||
TryComp<MobStateComponent>(args.Target, out var mobState))
|
||||
HasComp<MobStateComponent>(args.Target))
|
||||
{
|
||||
Verb hardElectrocute = new()
|
||||
{
|
||||
Text = "Electrocute",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Clothing/Hands/Gloves/Color/yellow.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Clothing/Hands/Gloves/Color/yellow.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
int damageToDeal;
|
||||
|
|
@ -262,7 +262,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Creampie",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Objects/Consumable/Food/Baked/pie.rsi/plain-slice.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Consumable/Food/Baked/pie.rsi"), "plain-slice"),
|
||||
Act = () =>
|
||||
{
|
||||
_creamPieSystem.SetCreamPied(args.Target, creamPied, true);
|
||||
|
|
@ -279,7 +279,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Remove blood",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Fluids/tomato_splat.rsi/puddle-1.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Fluids/tomato_splat.rsi"), "puddle-1"),
|
||||
Act = () =>
|
||||
{
|
||||
_bloodstreamSystem.SpillAllSolutions(args.Target, bloodstream);
|
||||
|
|
@ -302,7 +302,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Vomit organs",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Fluids/vomit_toxin.rsi/vomit_toxin-1.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Fluids/vomit_toxin.rsi"), "vomit_toxin-1"),
|
||||
Act = () =>
|
||||
{
|
||||
_vomitSystem.Vomit(args.Target, -1000, -1000); // You feel hollow!
|
||||
|
|
@ -331,7 +331,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Remove hands",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/AdminActions/remove-hands.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/remove-hands.png")),
|
||||
Act = () =>
|
||||
{
|
||||
var baseXform = Transform(args.Target);
|
||||
|
|
@ -353,7 +353,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Remove hands",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/AdminActions/remove-hand.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/remove-hand.png")),
|
||||
Act = () =>
|
||||
{
|
||||
var baseXform = Transform(args.Target);
|
||||
|
|
@ -376,7 +376,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Stomach Removal",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Mobs/Species/Human/organs.rsi/stomach.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Mobs/Species/Human/organs.rsi"), "stomach"),
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var (component, _) in _bodySystem.GetBodyOrganComponents<StomachComponent>(args.Target, body))
|
||||
|
|
@ -396,7 +396,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Lungs Removal",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Mobs/Species/Human/organs.rsi/lung-r.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Mobs/Species/Human/organs.rsi"), "lung-r"),
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var (component, _) in _bodySystem.GetBodyOrganComponents<LungComponent>(args.Target, body))
|
||||
|
|
@ -419,7 +419,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Pinball",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Objects/Fun/toys.rsi/basketball.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Fun/toys.rsi"), "basketball"),
|
||||
Act = () =>
|
||||
{
|
||||
var xform = Transform(args.Target);
|
||||
|
|
@ -453,7 +453,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Yeet",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/eject.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
{
|
||||
var xform = Transform(args.Target);
|
||||
|
|
@ -484,7 +484,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Become Bread",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Objects/Consumable/Food/Baked/bread.rsi/plain.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Consumable/Food/Baked/bread.rsi"), "plain"),
|
||||
Act = () =>
|
||||
{
|
||||
_polymorphableSystem.PolymorphEntity(args.Target, "AdminBreadSmite");
|
||||
|
|
@ -498,7 +498,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Become Mouse",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Mobs/Animals/mouse.rsi/icon-0.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Mobs/Animals/mouse.rsi"), "icon-0"),
|
||||
Act = () =>
|
||||
{
|
||||
_polymorphableSystem.PolymorphEntity(args.Target, "AdminMouseSmite");
|
||||
|
|
@ -514,7 +514,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Ghostkick",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/gavel.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/gavel.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_ghostKickManager.DoDisconnect(actorComponent.PlayerSession.ConnectedClient, "Smitten.");
|
||||
|
|
@ -530,7 +530,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Nyanify",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Clothing/Head/Hats/catears.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Clothing/Head/Hats/catears.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
var ears = Spawn("ClothingHeadHatCatEars", Transform(args.Target).Coordinates);
|
||||
|
|
@ -547,7 +547,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Kill sign",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Objects/Misc/killsign.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Misc/killsign.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
EnsureComp<KillSignComponent>(args.Target);
|
||||
|
|
@ -562,7 +562,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Clown",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Objects/Fun/bikehorn.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Fun/bikehorn.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
SetOutfitCommand.SetOutfit(args.Target, "ClownGear", EntityManager, (_, clothing) =>
|
||||
|
|
@ -581,7 +581,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Maid",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Clothing/Uniforms/Jumpskirt/janimaid.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Clothing/Uniforms/Jumpskirt/janimaid.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
SetOutfitCommand.SetOutfit(args.Target, "JanitorMaidGear", EntityManager, (_, clothing) =>
|
||||
|
|
@ -595,15 +595,13 @@ public sealed partial class AdminVerbSystem
|
|||
Message = Loc.GetString("admin-smite-maid-description")
|
||||
};
|
||||
args.Verbs.Add(maiden);
|
||||
|
||||
|
||||
}
|
||||
|
||||
Verb angerPointingArrows = new()
|
||||
{
|
||||
Text = "Anger Pointing Arrows",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/Misc/pointing.rsi/pointing.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Interface/Misc/pointing.rsi"), "pointing"),
|
||||
Act = () =>
|
||||
{
|
||||
EnsureComp<PointingArrowAngeringComponent>(args.Target);
|
||||
|
|
@ -617,7 +615,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Dust",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Objects/Materials/materials.rsi/ash.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Materials/materials"), "ash"),
|
||||
Act = () =>
|
||||
{
|
||||
EntityManager.QueueDeleteEntity(args.Target);
|
||||
|
|
@ -633,7 +631,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Buffering",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/Misc/buffering_smite_icon.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/Misc/buffering_smite_icon.png")),
|
||||
Act = () =>
|
||||
{
|
||||
EnsureComp<BufferingComponent>(args.Target);
|
||||
|
|
@ -647,7 +645,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Become Instrument",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Objects/Fun/Instruments/h_synthesizer.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Fun/Instruments/h_synthesizer.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
_polymorphableSystem.PolymorphEntity(args.Target, "AdminInstrumentSmite");
|
||||
|
|
@ -661,7 +659,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Remove gravity",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Structures/Machines/gravity_generator.rsi/off.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Structures/Machines/gravity_generator.rsi"), "off"),
|
||||
Act = () =>
|
||||
{
|
||||
var grav = EnsureComp<MovementIgnoreGravityComponent>(args.Target);
|
||||
|
|
@ -678,7 +676,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Reptilian Species Swap",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Objects/Fun/toys.rsi/plushie_lizard.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Fun/toys.rsi"), "plushie_lizard"),
|
||||
Act = () =>
|
||||
{
|
||||
_polymorphableSystem.PolymorphEntity(args.Target, "AdminLizardSmite");
|
||||
|
|
@ -692,7 +690,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Locker stuff",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Structures/Storage/closet.rsi/generic.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Structures/Storage/closet.rsi"), "generic"),
|
||||
Act = () =>
|
||||
{
|
||||
var xform = Transform(args.Target);
|
||||
|
|
@ -714,7 +712,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Headstand",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/refresh.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/refresh.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
{
|
||||
EnsureComp<HeadstandComponent>(args.Target);
|
||||
|
|
@ -728,7 +726,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Zoom in",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/AdminActions/zoom.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/zoom.png")),
|
||||
Act = () =>
|
||||
{
|
||||
var eye = EnsureComp<EyeComponent>(args.Target);
|
||||
|
|
@ -746,7 +744,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Flip eye",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/AdminActions/flip.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/flip.png")),
|
||||
Act = () =>
|
||||
{
|
||||
var eye = EnsureComp<EyeComponent>(args.Target);
|
||||
|
|
@ -764,7 +762,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Run Walk Swap",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/AdminActions/run-walk-swap.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/run-walk-swap.png")),
|
||||
Act = () =>
|
||||
{
|
||||
var movementSpeed = EnsureComp<MovementSpeedModifierComponent>(args.Target);
|
||||
|
|
@ -784,7 +782,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Speak Backwards",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/AdminActions/help-backwards.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/help-backwards.png")),
|
||||
Act = () =>
|
||||
{
|
||||
EnsureComp<BackwardsAccentComponent>(args.Target);
|
||||
|
|
@ -798,7 +796,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Disarm Prone",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/Actions/disarm.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/Actions/disarm.png")),
|
||||
Act = () =>
|
||||
{
|
||||
EnsureComp<DisarmProneComponent>(args.Target);
|
||||
|
|
@ -812,7 +810,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Super speed",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/AdminActions/super_speed.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/super_speed.png")),
|
||||
Act = () =>
|
||||
{
|
||||
var movementSpeed = EnsureComp<MovementSpeedModifierComponent>(args.Target);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ using Robust.Shared.Map.Components;
|
|||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Administration.Systems;
|
||||
|
||||
|
|
@ -68,9 +69,9 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = airlock.BoltsDown ? "Unbolt" : "Bolt",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = airlock.BoltsDown
|
||||
? "/Textures/Interface/AdminActions/unbolt.png"
|
||||
: "/Textures/Interface/AdminActions/bolt.png",
|
||||
Icon = airlock.BoltsDown
|
||||
? new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/unbolt.png"))
|
||||
: new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/bolt.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_airlockSystem.SetBoltsWithAudio(args.Target, airlock, !airlock.BoltsDown);
|
||||
|
|
@ -88,7 +89,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = airlock.EmergencyAccess ? "Emergency Access Off" : "Emergency Access On",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/emergency_access.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/emergency_access.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_airlockSystem.ToggleEmergencyAccess(args.Target, airlock);
|
||||
|
|
@ -110,7 +111,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Rejuvenate",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/rejuvenate.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/rejuvenate.png")),
|
||||
Act = () =>
|
||||
{
|
||||
RejuvenateCommand.PerformRejuvenate(args.Target);
|
||||
|
|
@ -128,7 +129,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Make Indestructible",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/plus.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/plus.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_godmodeSystem.EnableGodmode(args.Target);
|
||||
|
|
@ -145,7 +146,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Make Vulnerable",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/plus.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/plus.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_godmodeSystem.DisableGodmode(args.Target);
|
||||
|
|
@ -163,7 +164,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Refill Battery",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/fill_battery.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/fill_battery.png")),
|
||||
Act = () =>
|
||||
{
|
||||
battery.CurrentCharge = battery.MaxCharge;
|
||||
|
|
@ -179,7 +180,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Drain Battery",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/drain_battery.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/drain_battery.png")),
|
||||
Act = () =>
|
||||
{
|
||||
battery.CurrentCharge = 0;
|
||||
|
|
@ -195,7 +196,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Infinite Battery",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/infinite_battery.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/infinite_battery.png")),
|
||||
Act = () =>
|
||||
{
|
||||
var recharger = EnsureComp<BatterySelfRechargerComponent>(args.Target);
|
||||
|
|
@ -215,7 +216,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Block Unanchoring",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/anchor.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/anchor.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
{
|
||||
RemComp(args.Target, anchor);
|
||||
|
|
@ -233,7 +234,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Refill Internals Oxygen",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Tanks/oxygen.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
RefillGasTank(args.Target, Gas.Oxygen, tank);
|
||||
|
|
@ -248,7 +249,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Refill Internals Nitrogen",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Tanks/red.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Tanks/red.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
RefillGasTank(args.Target, Gas.Nitrogen, tank);
|
||||
|
|
@ -263,7 +264,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Refill Internals Plasma",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Tanks/plasma.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
RefillGasTank(args.Target, Gas.Plasma, tank);
|
||||
|
|
@ -275,13 +276,13 @@ public sealed partial class AdminVerbSystem
|
|||
args.Verbs.Add(refillInternalsPlasma);
|
||||
}
|
||||
|
||||
if (TryComp<InventoryComponent>(args.Target, out var inventory))
|
||||
if (HasComp<InventoryComponent>(args.Target))
|
||||
{
|
||||
Verb refillInternalsO2 = new()
|
||||
{
|
||||
Text = "Refill Internals Oxygen",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Tanks/oxygen.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var slot in _inventorySystem.GetSlots(args.Target))
|
||||
|
|
@ -289,7 +290,7 @@ public sealed partial class AdminVerbSystem
|
|||
if (!_inventorySystem.TryGetSlotEntity(args.Target, slot.Name, out var entity))
|
||||
continue;
|
||||
|
||||
if (!TryComp<GasTankComponent>(entity, out var tank))
|
||||
if (!TryComp(entity, out tank))
|
||||
continue;
|
||||
|
||||
RefillGasTank(entity.Value, Gas.Oxygen, tank);
|
||||
|
|
@ -297,7 +298,7 @@ public sealed partial class AdminVerbSystem
|
|||
|
||||
foreach (var held in _handsSystem.EnumerateHeld(args.Target))
|
||||
{
|
||||
if (!TryComp<GasTankComponent>(held, out var tank))
|
||||
if (!TryComp(held, out tank))
|
||||
continue;
|
||||
|
||||
RefillGasTank(held, Gas.Oxygen, tank);
|
||||
|
|
@ -313,7 +314,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Refill Internals Nitrogen",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Tanks/red.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Tanks/red.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var slot in _inventorySystem.GetSlots(args.Target))
|
||||
|
|
@ -321,7 +322,7 @@ public sealed partial class AdminVerbSystem
|
|||
if (!_inventorySystem.TryGetSlotEntity(args.Target, slot.Name, out var entity))
|
||||
continue;
|
||||
|
||||
if (!TryComp<GasTankComponent>(entity, out var tank))
|
||||
if (!TryComp(entity, out tank))
|
||||
continue;
|
||||
|
||||
RefillGasTank(entity.Value, Gas.Nitrogen, tank);
|
||||
|
|
@ -329,7 +330,7 @@ public sealed partial class AdminVerbSystem
|
|||
|
||||
foreach (var held in _handsSystem.EnumerateHeld(args.Target))
|
||||
{
|
||||
if (!TryComp<GasTankComponent>(held, out var tank))
|
||||
if (!TryComp(held, out tank))
|
||||
continue;
|
||||
|
||||
RefillGasTank(held, Gas.Nitrogen, tank);
|
||||
|
|
@ -345,7 +346,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Refill Internals Plasma",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Tanks/plasma.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var slot in _inventorySystem.GetSlots(args.Target))
|
||||
|
|
@ -353,7 +354,7 @@ public sealed partial class AdminVerbSystem
|
|||
if (!_inventorySystem.TryGetSlotEntity(args.Target, slot.Name, out var entity))
|
||||
continue;
|
||||
|
||||
if (!TryComp<GasTankComponent>(entity, out var tank))
|
||||
if (!TryComp(entity, out tank))
|
||||
continue;
|
||||
|
||||
RefillGasTank(entity.Value, Gas.Plasma, tank);
|
||||
|
|
@ -361,7 +362,7 @@ public sealed partial class AdminVerbSystem
|
|||
|
||||
foreach (var held in _handsSystem.EnumerateHeld(args.Target))
|
||||
{
|
||||
if (!TryComp<GasTankComponent>(held, out var tank))
|
||||
if (!TryComp(held, out tank))
|
||||
continue;
|
||||
|
||||
RefillGasTank(held, Gas.Plasma, tank);
|
||||
|
|
@ -378,7 +379,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Send to test arena",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/eject.svg.192dpi.png")),
|
||||
|
||||
Act = () =>
|
||||
{
|
||||
|
|
@ -399,7 +400,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Grant All Access",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Misc/id_cards.rsi/centcom.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Misc/id_cards.rsi"), "centcom"),
|
||||
Act = () =>
|
||||
{
|
||||
GiveAllAccess(activeId.Value);
|
||||
|
|
@ -414,7 +415,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Revoke All Access",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Misc/id_cards.rsi/default.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Misc/id_cards.rsi"), "default"),
|
||||
Act = () =>
|
||||
{
|
||||
RevokeAllAccess(activeId.Value);
|
||||
|
|
@ -432,7 +433,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Grant All Access",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Misc/id_cards.rsi/centcom.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Misc/id_cards.rsi"), "centcom"),
|
||||
Act = () =>
|
||||
{
|
||||
GiveAllAccess(args.Target);
|
||||
|
|
@ -447,7 +448,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Revoke All Access",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Misc/id_cards.rsi/default.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Misc/id_cards.rsi"), "default"),
|
||||
Act = () =>
|
||||
{
|
||||
RevokeAllAccess(args.Target);
|
||||
|
|
@ -466,7 +467,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Adjust Stack",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/adjust-stack.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/adjust-stack.png")),
|
||||
Act = () =>
|
||||
{
|
||||
// Unbounded intentionally.
|
||||
|
|
@ -485,7 +486,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Fill Stack",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/fill-stack.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/fill-stack.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_stackSystem.SetCount(args.Target, _stackSystem.GetMaxCount(stack), stack);
|
||||
|
|
@ -501,7 +502,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Rename",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/rename.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/rename.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_quickDialog.OpenDialog(player, "Rename", "Name", (string newName) =>
|
||||
|
|
@ -519,7 +520,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Redescribe",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/redescribe.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/redescribe.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_quickDialog.OpenDialog(player, "Redescribe", "Description", (LongString newDescription) =>
|
||||
|
|
@ -537,7 +538,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Redescribe",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/rename_and_redescribe.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/rename_and_redescribe.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_quickDialog.OpenDialog(player, "Rename & Redescribe", "Name", "Description",
|
||||
|
|
@ -562,7 +563,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Bar job slots",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/bar_jobslots.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/bar_jobslots.png")),
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var (job, _) in _stationJobsSystem.GetJobs(args.Target))
|
||||
|
|
@ -581,7 +582,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Locate Cargo Shuttle",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Clothing/Head/Soft/cargosoft.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Clothing/Head/Soft/cargosoft.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
var shuttle = Comp<StationCargoOrderDatabaseComponent>(args.Target).Shuttle;
|
||||
|
|
@ -604,7 +605,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Refill Battery",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/fill_battery.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/fill_battery.png")),
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var ent in childEnum)
|
||||
|
|
@ -626,7 +627,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Drain Battery",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/drain_battery.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/drain_battery.png")),
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var ent in childEnum)
|
||||
|
|
@ -648,7 +649,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Infinite Battery",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/infinite_battery.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/infinite_battery.png")),
|
||||
Act = () =>
|
||||
{
|
||||
// this kills the sloth
|
||||
|
|
@ -677,7 +678,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Halt Movement",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/halt.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/halt.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_physics.SetLinearVelocity(args.Target, Vector2.Zero, body: physics);
|
||||
|
|
@ -700,7 +701,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Unpause Map",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/play.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/play.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_mapManager.SetMapPaused(map.WorldMap, false);
|
||||
|
|
@ -717,7 +718,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Pause Map",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/pause.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/pause.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_mapManager.SetMapPaused(map.WorldMap, true);
|
||||
|
|
@ -737,7 +738,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Snap Joints",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/snap_joints.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/AdminActions/snap_joints.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_jointSystem.ClearJoints(joints);
|
||||
|
|
@ -755,7 +756,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Make Minigun",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Weapons/Guns/HMGs/minigun.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Weapons/Guns/HMGs/minigun.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
gun.FireRate = 15;
|
||||
|
|
@ -773,7 +774,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
Text = "Set Bullet Amount",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Fun/caps.rsi/mag-6.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Fun/caps.rsi"), "mag-6"),
|
||||
Act = () =>
|
||||
{
|
||||
_quickDialog.OpenDialog(player, "Set Bullet Amount", $"Amount (max {ballisticAmmo.Capacity}):", (int amount) =>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ using Robust.Shared.Console;
|
|||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using static Content.Shared.Configurable.ConfigurationComponent;
|
||||
|
||||
namespace Content.Server.Administration.Systems
|
||||
|
|
@ -79,7 +80,7 @@ namespace Content.Server.Administration.Systems
|
|||
Verb verb = new();
|
||||
verb.Text = Loc.GetString("ahelp-verb-get-data-text");
|
||||
verb.Category = VerbCategory.Admin;
|
||||
verb.IconTexture = "/Textures/Interface/gavel.svg.192dpi.png";
|
||||
verb.Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/gavel.svg.192dpi.png"));
|
||||
verb.Act = () =>
|
||||
_console.RemoteExecuteCommand(player, $"openahelp \"{targetActor.PlayerSession.UserId}\"");
|
||||
verb.Impact = LogImpact.Low;
|
||||
|
|
@ -89,7 +90,7 @@ namespace Content.Server.Administration.Systems
|
|||
Verb prayerVerb = new();
|
||||
prayerVerb.Text = Loc.GetString("prayer-verbs-subtle-message");
|
||||
prayerVerb.Category = VerbCategory.Admin;
|
||||
prayerVerb.IconTexture = "/Textures/Interface/pray.svg.png";
|
||||
prayerVerb.Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/pray.svg.png"));
|
||||
prayerVerb.Act = () =>
|
||||
{
|
||||
_quickDialog.OpenDialog(player, "Subtle Message", "Message", "Popup Message", (string message, string popupMessage) =>
|
||||
|
|
@ -109,7 +110,7 @@ namespace Content.Server.Administration.Systems
|
|||
? Loc.GetString("admin-verbs-unfreeze")
|
||||
: Loc.GetString("admin-verbs-freeze"),
|
||||
Category = VerbCategory.Admin,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/snow.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
{
|
||||
if (frozen)
|
||||
|
|
@ -145,7 +146,7 @@ namespace Content.Server.Administration.Systems
|
|||
{
|
||||
Text = Loc.GetString("admin-verbs-teleport-to"),
|
||||
Category = VerbCategory.Admin,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/open.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/open.svg.192dpi.png")),
|
||||
Act = () => _console.ExecuteCommand(player, $"tpto {args.Target}"),
|
||||
Impact = LogImpact.Low
|
||||
});
|
||||
|
|
@ -155,7 +156,7 @@ namespace Content.Server.Administration.Systems
|
|||
{
|
||||
Text = Loc.GetString("admin-verbs-teleport-here"),
|
||||
Category = VerbCategory.Admin,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/close.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/close.svg.192dpi.png")),
|
||||
Act = () => _console.ExecuteCommand(player, $"tpto {args.Target} {args.User}"),
|
||||
Impact = LogImpact.Low
|
||||
});
|
||||
|
|
@ -194,7 +195,7 @@ namespace Content.Server.Administration.Systems
|
|||
{
|
||||
Text = Loc.GetString("delete-verb-get-data-text"),
|
||||
Category = VerbCategory.Debug,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/delete_transparent.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/delete_transparent.svg.192dpi.png")),
|
||||
Act = () => EntityManager.DeleteEntity(args.Target),
|
||||
Impact = LogImpact.Medium,
|
||||
ConfirmationPopup = true
|
||||
|
|
@ -209,7 +210,7 @@ namespace Content.Server.Administration.Systems
|
|||
{
|
||||
Text = Loc.GetString("rejuvenate-verb-get-data-text"),
|
||||
Category = VerbCategory.Debug,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/rejuvenate.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/rejuvenate.svg.192dpi.png")),
|
||||
Act = () => RejuvenateCommand.PerformRejuvenate(args.Target),
|
||||
Impact = LogImpact.Medium
|
||||
};
|
||||
|
|
@ -268,7 +269,7 @@ namespace Content.Server.Administration.Systems
|
|||
{
|
||||
Text = Loc.GetString("make-sentient-verb-get-data-text"),
|
||||
Category = VerbCategory.Debug,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/sentient.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/sentient.svg.192dpi.png")),
|
||||
Act = () => MakeSentientCommand.MakeSentient(args.Target, EntityManager),
|
||||
Impact = LogImpact.Medium
|
||||
};
|
||||
|
|
@ -283,7 +284,7 @@ namespace Content.Server.Administration.Systems
|
|||
{
|
||||
Text = Loc.GetString("set-outfit-verb-get-data-text"),
|
||||
Category = VerbCategory.Debug,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/outfit.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/outfit.svg.192dpi.png")),
|
||||
Act = () => _euiManager.OpenEui(new SetOutfitEui(args.Target), player),
|
||||
Impact = LogImpact.Medium
|
||||
};
|
||||
|
|
@ -297,7 +298,7 @@ namespace Content.Server.Administration.Systems
|
|||
{
|
||||
Text = Loc.GetString("in-range-unoccluded-verb-get-data-text"),
|
||||
Category = VerbCategory.Debug,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/information.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/information.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
{
|
||||
var message = args.User.InRangeUnOccluded(args.Target)
|
||||
|
|
@ -317,7 +318,7 @@ namespace Content.Server.Administration.Systems
|
|||
{
|
||||
Text = Loc.GetString("tube-direction-verb-get-data-text"),
|
||||
Category = VerbCategory.Debug,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/information.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/information.svg.192dpi.png")),
|
||||
Act = () => tube.PopupDirections(args.User)
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
|
|
@ -343,7 +344,7 @@ namespace Content.Server.Administration.Systems
|
|||
Verb verb = new()
|
||||
{
|
||||
Text = Loc.GetString("configure-verb-get-data-text"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/settings.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")),
|
||||
Category = VerbCategory.Debug,
|
||||
Act = () => _uiSystem.TryOpen(args.Target, ConfigurationUiKey.Key, actor.PlayerSession)
|
||||
};
|
||||
|
|
@ -358,7 +359,7 @@ namespace Content.Server.Administration.Systems
|
|||
{
|
||||
Text = Loc.GetString("edit-solutions-verb-get-data-text"),
|
||||
Category = VerbCategory.Debug,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/spill.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/spill.svg.192dpi.png")),
|
||||
Act = () => OpenEditSolutionsEui(player, args.Target),
|
||||
Impact = LogImpact.Medium // maybe high depending on WHAT reagents they add...
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using Content.Shared.Verbs;
|
|||
using Content.Server.Popups;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Body.Systems;
|
||||
|
||||
|
|
@ -48,7 +49,7 @@ public sealed class InternalsSystem : EntitySystem
|
|||
ToggleInternals(uid, args.User, false, component);
|
||||
},
|
||||
Message = Loc.GetString("action-description-internals-toggle"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/dot.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/dot.svg.192dpi.png")),
|
||||
Text = Loc.GetString("action-name-internals-toggle"),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public sealed class BotanySwabSystem : EntitySystem
|
|||
base.Initialize();
|
||||
SubscribeLocalEvent<BotanySwabComponent, ExaminedEvent>(OnExamined);
|
||||
SubscribeLocalEvent<BotanySwabComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<DoAfterEvent>(OnDoAfter);
|
||||
SubscribeLocalEvent<BotanySwabComponent, DoAfterEvent>(OnDoAfter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -41,7 +41,7 @@ public sealed class BotanySwabSystem : EntitySystem
|
|||
/// </summary>
|
||||
private void OnAfterInteract(EntityUid uid, BotanySwabComponent swab, AfterInteractEvent args)
|
||||
{
|
||||
if (args.Target == null || !args.CanReach)
|
||||
if (args.Target == null || !args.CanReach || !HasComp<PlantHolderComponent>(args.Target))
|
||||
return;
|
||||
|
||||
_doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, swab.SwabDelay, target: args.Target, used: uid)
|
||||
|
|
@ -57,7 +57,7 @@ public sealed class BotanySwabSystem : EntitySystem
|
|||
/// <summary>
|
||||
/// Save seed data or cross-pollenate.
|
||||
/// </summary>
|
||||
private void OnDoAfter(DoAfterEvent args)
|
||||
private void OnDoAfter(EntityUid uid, BotanySwabComponent component, DoAfterEvent args)
|
||||
{
|
||||
if (args.Cancelled || args.Handled || !TryComp<PlantHolderComponent>(args.Args.Target, out var plant) || !TryComp<BotanySwabComponent>(args.Args.Used, out var swab))
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ using Content.Shared.Stunnable;
|
|||
using Content.Shared.Vehicle.Components;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Buckle.Systems;
|
||||
|
||||
|
|
@ -44,7 +45,7 @@ public sealed partial class BuckleSystem
|
|||
{
|
||||
Act = () => TryUnbuckle(uid, args.User, buckle: component),
|
||||
Text = Loc.GetString("verb-categories-unbuckle"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/unbuckle.svg.192dpi.png"
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/unbuckle.svg.192dpi.png"))
|
||||
};
|
||||
|
||||
if (args.Target == args.User && args.Using == null)
|
||||
|
|
@ -76,7 +77,8 @@ public sealed partial class BuckleSystem
|
|||
|
||||
private void HandleInteractHand(EntityUid uid, BuckleComponent component, InteractHandEvent args)
|
||||
{
|
||||
args.Handled = TryUnbuckle(uid, args.User, buckle: component);
|
||||
if (TryUnbuckle(uid, args.User, buckle: component))
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void MoveEvent(EntityUid uid, BuckleComponent buckle, ref MoveEvent ev)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ using Content.Server.Administration;
|
|||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Cargo.Components;
|
||||
using Content.Server.Chemistry.Components.SolutionManager;
|
||||
using Content.Server.Stack;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
|
|
@ -88,6 +87,9 @@ public sealed class PricingSystem : EntitySystem
|
|||
|
||||
private void CalculateMobPrice(EntityUid uid, MobPriceComponent component, ref PriceCalculationEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!TryComp<BodyComponent>(uid, out var body) || !TryComp<MobStateComponent>(uid, out var state))
|
||||
{
|
||||
Logger.ErrorS("pricing", $"Tried to get the mob price of {ToPrettyString(uid)}, which has no {nameof(BodyComponent)} and no {nameof(MobStateComponent)}.");
|
||||
|
|
@ -106,6 +108,9 @@ public sealed class PricingSystem : EntitySystem
|
|||
|
||||
private void CalculateStackPrice(EntityUid uid, StackPriceComponent component, ref PriceCalculationEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!TryComp<StackComponent>(uid, out var stack))
|
||||
{
|
||||
Logger.ErrorS("pricing", $"Tried to get the stack price of {ToPrettyString(uid)}, which has no {nameof(StackComponent)}.");
|
||||
|
|
@ -117,6 +122,9 @@ public sealed class PricingSystem : EntitySystem
|
|||
|
||||
private void CalculateSolutionPrice(EntityUid uid, SolutionContainerManagerComponent component, ref PriceCalculationEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
var price = 0f;
|
||||
|
||||
foreach (var solution in component.Solutions.Values)
|
||||
|
|
@ -133,6 +141,9 @@ public sealed class PricingSystem : EntitySystem
|
|||
|
||||
private void CalculateStaticPrice(EntityUid uid, StaticPriceComponent component, ref PriceCalculationEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Price += component.Price;
|
||||
}
|
||||
|
||||
|
|
@ -187,6 +198,9 @@ public sealed class PricingSystem : EntitySystem
|
|||
var ev = new PriceCalculationEvent();
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
|
||||
if (ev.Handled)
|
||||
return ev.Price;
|
||||
|
||||
//TODO: Add an OpaqueToAppraisal component or similar for blocking the recursive descent into containers, or preventing material pricing.
|
||||
|
||||
if (TryComp<MaterialComponent>(uid, out var material) && !HasComp<StackPriceComponent>(uid))
|
||||
|
|
@ -249,5 +263,10 @@ public struct PriceCalculationEvent
|
|||
/// </summary>
|
||||
public double Price = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this event was already handled.
|
||||
/// </summary>
|
||||
public bool Handled = false;
|
||||
|
||||
public PriceCalculationEvent() { }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ public sealed partial class ChatSystem : SharedChatSystem
|
|||
base.Shutdown();
|
||||
ShutdownEmotes();
|
||||
_configurationManager.UnsubValueChanged(CCVars.LoocEnabled, OnLoocEnabledChanged);
|
||||
_configurationManager.UnsubValueChanged(CCVars.DeadLoocEnabled, OnDeadLoocEnabledChanged);
|
||||
}
|
||||
|
||||
private void OnLoocEnabledChanged(bool val)
|
||||
|
|
@ -101,13 +102,17 @@ public sealed partial class ChatSystem : SharedChatSystem
|
|||
|
||||
private void OnGameChange(GameRunLevelChangedEvent ev)
|
||||
{
|
||||
if (_configurationManager.GetCVar(CCVars.OocEnableDuringRound))
|
||||
return;
|
||||
|
||||
if (ev.New == GameRunLevel.InRound)
|
||||
_configurationManager.SetCVar(CCVars.OocEnabled, false);
|
||||
else if (ev.New == GameRunLevel.PostRound)
|
||||
_configurationManager.SetCVar(CCVars.OocEnabled, true);
|
||||
switch(ev.New)
|
||||
{
|
||||
case GameRunLevel.InRound:
|
||||
if(!_configurationManager.GetCVar(CCVars.OocEnableDuringRound))
|
||||
_configurationManager.SetCVar(CCVars.OocEnabled, false);
|
||||
break;
|
||||
case GameRunLevel.PostRound:
|
||||
if(!_configurationManager.GetCVar(CCVars.OocEnableDuringRound))
|
||||
_configurationManager.SetCVar(CCVars.OocEnabled, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -129,11 +129,18 @@ public sealed partial class ChemistrySystem
|
|||
|
||||
private void OnInjectDoAfter(EntityUid uid, InjectorComponent component, DoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled || args.Args.Target == null)
|
||||
if (args.Cancelled)
|
||||
{
|
||||
component.IsInjecting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Handled || args.Args.Target == null)
|
||||
return;
|
||||
|
||||
UseInjector(args.Args.Target.Value, args.Args.User, uid, component);
|
||||
|
||||
component.IsInjecting = false;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
|
|
@ -216,12 +223,18 @@ public sealed partial class ChemistrySystem
|
|||
if (!_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution))
|
||||
return;
|
||||
|
||||
//If it found it's injecting
|
||||
if (component.IsInjecting)
|
||||
return;
|
||||
|
||||
var actualDelay = MathF.Max(component.Delay, 1f);
|
||||
|
||||
// Injections take 1 second longer per additional 5u
|
||||
actualDelay += (float) component.TransferAmount / component.Delay - 1;
|
||||
|
||||
if (user != target)
|
||||
var isTarget = user != target;
|
||||
|
||||
if (isTarget)
|
||||
{
|
||||
// Create a pop-up for the target
|
||||
var userName = Identity.Entity(user, EntityManager);
|
||||
|
|
@ -256,8 +269,12 @@ public sealed partial class ChemistrySystem
|
|||
_adminLogger.Add(LogType.Ingestion, $"{EntityManager.ToPrettyString(user):user} is attempting to inject themselves with a solution {SolutionContainerSystem.ToPrettyString(solution):solution}.");
|
||||
}
|
||||
|
||||
component.IsInjecting = true;
|
||||
|
||||
_doAfter.DoAfter(new DoAfterEventArgs(user, actualDelay, target:target, used:injector)
|
||||
{
|
||||
RaiseOnTarget = isTarget,
|
||||
RaiseOnUser = !isTarget,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnDamage = true,
|
||||
BreakOnStun = true,
|
||||
|
|
|
|||
|
|
@ -11,12 +11,15 @@ using Content.Shared.Interaction;
|
|||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Content.Shared.Timing;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems
|
||||
{
|
||||
public sealed partial class ChemistrySystem
|
||||
{
|
||||
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||
|
||||
private void InitializeHypospray()
|
||||
{
|
||||
SubscribeLocalEvent<HyposprayComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
|
|
@ -65,6 +68,10 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
if (!EligibleEntity(target, _entMan))
|
||||
return false;
|
||||
|
||||
if (TryComp(uid, out UseDelayComponent? delayComp))
|
||||
if (_useDelay.ActiveDelay(uid, delayComp))
|
||||
return false;
|
||||
|
||||
string? msgFormat = null;
|
||||
|
||||
if (target == user)
|
||||
|
|
@ -102,6 +109,11 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
|
||||
_audio.PlayPvs(component.InjectSound, user);
|
||||
|
||||
// Medipens and such use this system and don't have a delay, requiring extra checks
|
||||
// BeginDelay function returns if item is already on delay
|
||||
if (delayComp is not null)
|
||||
_useDelay.BeginDelay(uid, delayComp);
|
||||
|
||||
// Get transfer amount. May be smaller than component.TransferAmount if not enough room
|
||||
var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.AvailableVolume);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using Content.Shared.Verbs;
|
|||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Clothing.Systems;
|
||||
|
||||
|
|
@ -47,7 +48,7 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem
|
|||
args.Verbs.Add(new InteractionVerb()
|
||||
{
|
||||
Text = Loc.GetString("chameleon-component-verb-text"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/settings.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")),
|
||||
Act = () => TryOpenUi(uid, args.User, component)
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -319,6 +319,9 @@ namespace Content.Server.Construction
|
|||
newConstruction.InteractionQueue.Enqueue(ev);
|
||||
}
|
||||
|
||||
if (newConstruction.InteractionQueue.Count > 0 && _queuedUpdates.Add(newUid))
|
||||
_constructionUpdateQueue.Enqueue(newUid);
|
||||
|
||||
// Transform transferring.
|
||||
var newTransform = Transform(newUid);
|
||||
newTransform.LocalRotation = transform.LocalRotation;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Content.Shared.Examine;
|
|||
using Content.Shared.Popups;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Construction
|
||||
{
|
||||
|
|
@ -44,7 +45,8 @@ namespace Content.Server.Construction
|
|||
//verb.Category = VerbCategories.Construction;
|
||||
//TODO VERBS add more construction verbs? Until then, removing construction category
|
||||
verb.Text = Loc.GetString("deconstructible-verb-begin-deconstruct");
|
||||
verb.IconTexture = "/Textures/Interface/hammer_scaled.svg.192dpi.png";
|
||||
verb.Icon = new SpriteSpecifier.Texture(
|
||||
new ResourcePath("/Textures/Interface/hammer_scaled.svg.192dpi.png"));
|
||||
|
||||
verb.Act = () =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Construction.Components;
|
||||
using Content.Server.Temperature.Components;
|
||||
|
|
@ -8,6 +9,7 @@ using Content.Shared.DoAfter;
|
|||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Tools.Components;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Utility;
|
||||
#if EXCEPTION_TOLERANCE
|
||||
// ReSharper disable once RedundantUsingDirective
|
||||
using Robust.Shared.Exceptions;
|
||||
|
|
@ -22,7 +24,8 @@ namespace Content.Server.Construction
|
|||
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
|
||||
#endif
|
||||
|
||||
private readonly HashSet<EntityUid> _constructionUpdateQueue = new();
|
||||
private readonly Queue<EntityUid> _constructionUpdateQueue = new();
|
||||
private readonly HashSet<EntityUid> _queuedUpdates = new();
|
||||
|
||||
private void InitializeInteractions()
|
||||
{
|
||||
|
|
@ -480,19 +483,42 @@ namespace Content.Server.Construction
|
|||
// We iterate all entities waiting for their interactions to be handled.
|
||||
// This is much more performant than making an EntityQuery for ConstructionComponent,
|
||||
// since, for example, every single wall has a ConstructionComponent....
|
||||
foreach (var uid in _constructionUpdateQueue)
|
||||
while (_constructionUpdateQueue.TryDequeue(out var uid))
|
||||
{
|
||||
_queuedUpdates.Remove(uid);
|
||||
|
||||
// Ensure the entity exists and has a Construction component.
|
||||
if (!Exists(uid) || !TryComp(uid, out ConstructionComponent? construction))
|
||||
if (!TryComp(uid, out ConstructionComponent? construction))
|
||||
continue;
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
try
|
||||
{
|
||||
#endif
|
||||
|
||||
// temporary code for debugging a grafana exception. Something is fishy with the girder graph.
|
||||
object? prev = null;
|
||||
var queued = string.Join(", ", construction.InteractionQueue.Select(x => x.GetType().Name));
|
||||
|
||||
// Handle all queued interactions!
|
||||
while (construction.InteractionQueue.TryDequeue(out var interaction))
|
||||
{
|
||||
if (construction.Deleted)
|
||||
{
|
||||
// I suspect the error might just happen if two users try to deconstruction or otherwise modify an entity at the exact same tick?
|
||||
// In which case this isn't really an error, but should just be a `if (deleted) -> break`
|
||||
// But might as well verify this.
|
||||
|
||||
_sawmill.Error($"Construction component was deleted while still processing interactions." +
|
||||
$"Entity {ToPrettyString(uid)}, graph: {construction.Graph}, " +
|
||||
$"Previous: {prev?.GetType()?.Name ?? "null"}, " +
|
||||
$"Next: {interaction.GetType().Name}, " +
|
||||
$"Initial Queue: {queued}, " +
|
||||
$"Remaining Queue: {string.Join(", ", construction.InteractionQueue.Select(x => x.GetType().Name))}");
|
||||
break;
|
||||
}
|
||||
prev = interaction;
|
||||
|
||||
// We set validation to false because we actually want to perform the interaction here.
|
||||
HandleEvent(uid, interaction, false, construction);
|
||||
}
|
||||
|
|
@ -507,7 +533,7 @@ namespace Content.Server.Construction
|
|||
#endif
|
||||
}
|
||||
|
||||
_constructionUpdateQueue.Clear();
|
||||
DebugTools.Assert(_queuedUpdates.Count == 0);
|
||||
}
|
||||
|
||||
#region Event Handlers
|
||||
|
|
@ -543,7 +569,8 @@ namespace Content.Server.Construction
|
|||
construction.InteractionQueue.Enqueue(args);
|
||||
|
||||
// Add this entity to the queue so it'll be updated next tick.
|
||||
_constructionUpdateQueue.Add(uid);
|
||||
if (_queuedUpdates.Add(uid))
|
||||
_constructionUpdateQueue.Enqueue(uid);
|
||||
}
|
||||
|
||||
private void OnDoAfter(EntityUid uid, ConstructionComponent component, DoAfterEvent<ConstructionData> args)
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ public sealed partial class ConstructionSystem
|
|||
Text = Loc.GetString("machine-upgrade-examinable-verb-text"),
|
||||
Message = Loc.GetString("machine-upgrade-examinable-verb-message"),
|
||||
Category = VerbCategory.Examine,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/pickup.svg.192dpi.png"
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/pickup.svg.192dpi.png"))
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
|||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly DecalSystem _decals = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -68,7 +70,7 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
|||
return;
|
||||
|
||||
if (component.UseSound != null)
|
||||
SoundSystem.Play(component.UseSound.GetSound(), Filter.Pvs(uid), uid, AudioHelpers.WithVariation(0.125f));
|
||||
_audio.PlayPvs(component.UseSound, uid, AudioHelpers.WithVariation(0.125f));
|
||||
|
||||
// Decrease "Ammo"
|
||||
component.Charges--;
|
||||
|
|
@ -86,14 +88,18 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
|||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!TryComp<ActorComponent>(args.User, out var actor)) return;
|
||||
if (!TryComp<ActorComponent>(args.User, out var actor) ||
|
||||
component.UserInterface == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
component.UserInterface?.Toggle(actor.PlayerSession);
|
||||
_uiSystem.ToggleUi(component.UserInterface, actor.PlayerSession);
|
||||
|
||||
if (component.UserInterface?.SessionHasOpen(actor.PlayerSession) == true)
|
||||
if (component.UserInterface?.SubscribedSessions.Contains(actor.PlayerSession) == true)
|
||||
{
|
||||
// Tell the user interface the selected stuff
|
||||
component.UserInterface.SetState(new CrayonBoundUserInterfaceState(component.SelectedState, component.SelectableColor, component.Color));
|
||||
_uiSystem.SetUiState(component.UserInterface, new CrayonBoundUserInterfaceState(component.SelectedState, component.SelectableColor, component.Color));
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
|
|
@ -134,7 +140,7 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
|||
private void OnCrayonDropped(EntityUid uid, CrayonComponent component, DroppedEvent args)
|
||||
{
|
||||
if (TryComp<ActorComponent>(args.User, out var actor))
|
||||
component.UserInterface?.Close(actor.PlayerSession);
|
||||
_uiSystem.TryClose(uid, SharedCrayonComponent.CrayonUiKey.Key, actor.PlayerSession);
|
||||
}
|
||||
|
||||
private void UseUpCrayon(EntityUid uid, EntityUid user)
|
||||
|
|
|
|||
|
|
@ -1,22 +1,17 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.EUI;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Server.StationRecords;
|
||||
using Content.Server.StationRecords.Systems;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.CrewManifest;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.CrewManifest;
|
||||
|
||||
|
|
|
|||
|
|
@ -748,14 +748,25 @@ namespace Content.Server.Database
|
|||
query = query.Where(log => log.Date > filter.After);
|
||||
}
|
||||
|
||||
if (filter.AnyPlayers != null)
|
||||
if (filter.IncludePlayers)
|
||||
{
|
||||
query = query.Where(log => log.Players.Any(p => filter.AnyPlayers.Contains(p.PlayerUserId)));
|
||||
}
|
||||
if (filter.AnyPlayers != null)
|
||||
{
|
||||
query = query.Where(log =>
|
||||
log.Players.Any(p => filter.AnyPlayers.Contains(p.PlayerUserId)) ||
|
||||
log.Players.Count == 0 && filter.IncludeNonPlayers);
|
||||
}
|
||||
|
||||
if (filter.AllPlayers != null)
|
||||
if (filter.AllPlayers != null)
|
||||
{
|
||||
query = query.Where(log =>
|
||||
log.Players.All(p => filter.AllPlayers.Contains(p.PlayerUserId)) ||
|
||||
log.Players.Count == 0 && filter.IncludeNonPlayers);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
query = query.Where(log => log.Players.All(p => filter.AllPlayers.Contains(p.PlayerUserId)));
|
||||
query = query.Where(log => log.Players.Count == 0);
|
||||
}
|
||||
|
||||
if (filter.LastLogId != null)
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ namespace Content.Server.DetailExaminable
|
|||
Category = VerbCategory.Examine,
|
||||
Disabled = !detailsRange,
|
||||
Message = Loc.GetString("detail-examinable-verb-disabled"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/examine.svg.192dpi.png"
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/examine.svg.192dpi.png"))
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
|
|
|
|||
|
|
@ -1,20 +1,17 @@
|
|||
using System.Linq;
|
||||
using Content.Server.DeviceNetwork.Components;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.DeviceNetwork;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Movement;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.DeviceNetwork.Systems;
|
||||
|
||||
|
|
@ -56,14 +53,16 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
|
|||
|
||||
foreach (var component in EntityManager.EntityQuery<NetworkConfiguratorComponent>())
|
||||
{
|
||||
var uid = component.Owner;
|
||||
|
||||
if (component.ActiveDeviceList != null && EntityManager.EntityExists(component.ActiveDeviceList.Value) &&
|
||||
_interactionSystem.InRangeUnobstructed(component.Owner, component.ActiveDeviceList.Value))
|
||||
_interactionSystem.InRangeUnobstructed(uid, component.ActiveDeviceList.Value))
|
||||
{
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
//The network configurator is a handheld device. There can only ever be an ui session open for the player holding the device.
|
||||
_uiSystem.GetUiOrNull(component.Owner, NetworkConfiguratorUiKey.Configure)?.CloseAll();
|
||||
_uiSystem.GetUiOrNull(uid, NetworkConfiguratorUiKey.Configure)?.CloseAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +137,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
|
|||
|
||||
private void OnComponentRemoved(EntityUid uid, DeviceListComponent component, ComponentRemove args)
|
||||
{
|
||||
_uiSystem.GetUiOrNull(component.Owner, NetworkConfiguratorUiKey.Configure)?.CloseAll();
|
||||
_uiSystem.GetUiOrNull(uid, NetworkConfiguratorUiKey.Configure)?.CloseAll();
|
||||
}
|
||||
|
||||
#region Interactions
|
||||
|
|
@ -177,7 +176,9 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
|
|||
UtilityVerb verb = new()
|
||||
{
|
||||
Text = Loc.GetString(isDeviceList ? "network-configurator-configure" : "network-configurator-save-device"),
|
||||
IconTexture = isDeviceList ? "/Textures/Interface/VerbIcons/settings.svg.192dpi.png" : "/Textures/Interface/VerbIcons/in.svg.192dpi.png",
|
||||
Icon = isDeviceList ?
|
||||
new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")) :
|
||||
new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/in.svg.192dpi.png")),
|
||||
Act = () => OnUsed(uid, component, args.Target, args.User),
|
||||
Impact = LogImpact.Low
|
||||
};
|
||||
|
|
@ -199,7 +200,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
|
|||
AlternativeVerb verb = new()
|
||||
{
|
||||
Text = Loc.GetString("network-configurator-save-device"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/in.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/in.svg.192dpi.png")),
|
||||
Act = () => TryAddNetworkDevice(args.Target, args.Using.Value, args.User),
|
||||
Impact = LogImpact.Low
|
||||
};
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ namespace Content.Server.Disease
|
|||
// Private Events
|
||||
SubscribeLocalEvent<DiseaseDiagnoserComponent, DiseaseMachineFinishedEvent>(OnDiagnoserFinished);
|
||||
SubscribeLocalEvent<DiseaseVaccineCreatorComponent, DiseaseMachineFinishedEvent>(OnVaccinatorFinished);
|
||||
SubscribeLocalEvent<DoAfterEvent>(OnSwabDoAfter);
|
||||
SubscribeLocalEvent<DiseaseSwabComponent, DoAfterEvent>(OnSwabDoAfter);
|
||||
}
|
||||
|
||||
private Queue<EntityUid> AddQueue = new();
|
||||
|
|
@ -99,7 +99,7 @@ namespace Content.Server.Disease
|
|||
/// </summary>
|
||||
private void OnAfterInteract(EntityUid uid, DiseaseSwabComponent swab, AfterInteractEvent args)
|
||||
{
|
||||
if (args.Target == null || !args.CanReach)
|
||||
if (args.Target == null || !args.CanReach || !HasComp<DiseaseCarrierComponent>(args.Target))
|
||||
return;
|
||||
|
||||
if (swab.Used)
|
||||
|
|
@ -116,8 +116,12 @@ namespace Content.Server.Disease
|
|||
return;
|
||||
}
|
||||
|
||||
var isTarget = args.User != args.Target;
|
||||
|
||||
_doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, swab.SwabDelay, target: args.Target, used: uid)
|
||||
{
|
||||
RaiseOnTarget = isTarget,
|
||||
RaiseOnUser = !isTarget,
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnStun = true,
|
||||
|
|
@ -301,9 +305,9 @@ namespace Content.Server.Disease
|
|||
/// Copies a disease prototype to the swab
|
||||
/// after the doafter completes.
|
||||
/// </summary>
|
||||
private void OnSwabDoAfter(DoAfterEvent args)
|
||||
private void OnSwabDoAfter(EntityUid uid, DiseaseSwabComponent component, DoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled || !TryComp<DiseaseCarrierComponent>(args.Args.Target, out var carrier) || !TryComp<DiseaseSwabComponent>(args.Args.Target, out var swab))
|
||||
if (args.Handled || args.Cancelled || !TryComp<DiseaseCarrierComponent>(args.Args.Target, out var carrier) || !TryComp<DiseaseSwabComponent>(args.Args.Used, out var swab))
|
||||
return;
|
||||
|
||||
swab.Used = true;
|
||||
|
|
@ -313,7 +317,6 @@ namespace Content.Server.Disease
|
|||
return;
|
||||
|
||||
swab.Disease = _random.Pick(carrier.Diseases);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ using Robust.Shared.Containers;
|
|||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Disposal.Unit.EntitySystems
|
||||
{
|
||||
|
|
@ -96,7 +97,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
|
|||
AlternativeVerb flushVerb = new();
|
||||
flushVerb.Act = () => Engage(uid, component);
|
||||
flushVerb.Text = Loc.GetString("disposal-flush-verb-get-data-text");
|
||||
flushVerb.IconTexture = "/Textures/Interface/VerbIcons/delete_transparent.svg.192dpi.png";
|
||||
flushVerb.Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/delete_transparent.svg.192dpi.png"));
|
||||
flushVerb.Priority = 1;
|
||||
args.Verbs.Add(flushVerb);
|
||||
|
||||
|
|
|
|||
|
|
@ -396,7 +396,7 @@ public sealed partial class ExplosionSystem : EntitySystem
|
|||
{
|
||||
var damageStr = string.Join(", ", damage.DamageDict.Select(entry => $"{entry.Key}: {entry.Value}"));
|
||||
_adminLogger.Add(LogType.Explosion, LogImpact.Medium,
|
||||
$"Explosion caused {{{damageStr}}} to {ToPrettyString(uid):target} at {Transform(uid).Coordinates}");
|
||||
$"Explosion caused [{damageStr}] to {ToPrettyString(uid):target} at {Transform(uid).Coordinates}");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -407,7 +407,7 @@ public sealed partial class ExplosionSystem : EntitySystem
|
|||
{
|
||||
var damageStr = string.Join(", ", appliedDamage.DamageDict.Select(entry => $"{entry.Key}: {entry.Value}"));
|
||||
_adminLogger.Add(LogType.Explosion, LogImpact.Medium,
|
||||
$"Explosion caused {{{damageStr}}} to {ToPrettyString(uid):target} at {Transform(uid).Coordinates}");
|
||||
$"Explosion caused [{damageStr}] to {ToPrettyString(uid):target} at {Transform(uid).Coordinates}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using Content.Shared.Storage.Components;
|
|||
using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Foldable
|
||||
{
|
||||
|
|
@ -32,9 +33,9 @@ namespace Content.Server.Foldable
|
|||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
public bool TryToggleFold(FoldableComponent comp)
|
||||
public bool TryToggleFold(EntityUid uid, FoldableComponent comp)
|
||||
{
|
||||
return TrySetFolded(comp, !comp.IsFolded);
|
||||
return TrySetFolded(uid, comp, !comp.IsFolded);
|
||||
}
|
||||
|
||||
public bool CanToggleFold(EntityUid uid, FoldableComponent? fold = null)
|
||||
|
|
@ -62,18 +63,15 @@ namespace Content.Server.Foldable
|
|||
/// <summary>
|
||||
/// Try to fold/unfold
|
||||
/// </summary>
|
||||
/// <param name="comp"></param>
|
||||
/// <param name="state">Folded state we want</param>
|
||||
/// <returns>True if successful</returns>
|
||||
public bool TrySetFolded(FoldableComponent comp, bool state)
|
||||
public bool TrySetFolded(EntityUid uid, FoldableComponent comp, bool state)
|
||||
{
|
||||
if (state == comp.IsFolded)
|
||||
return false;
|
||||
|
||||
if (!CanToggleFold(comp.Owner, comp))
|
||||
if (!CanToggleFold(uid, comp))
|
||||
return false;
|
||||
|
||||
SetFolded(comp, state);
|
||||
SetFolded(uid, comp, state);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -82,12 +80,12 @@ namespace Content.Server.Foldable
|
|||
/// </summary>
|
||||
/// <param name="component"></param>
|
||||
/// <param name="folded">If true, the component will become folded, else unfolded</param>
|
||||
public override void SetFolded(FoldableComponent component, bool folded)
|
||||
public override void SetFolded(EntityUid uid, FoldableComponent component, bool folded)
|
||||
{
|
||||
base.SetFolded(component, folded);
|
||||
base.SetFolded(uid, component, folded);
|
||||
|
||||
// You can't buckle an entity to a folded object
|
||||
_buckle.StrapSetEnabled(component.Owner, !component.IsFolded);
|
||||
_buckle.StrapSetEnabled(uid, !component.IsFolded);
|
||||
}
|
||||
|
||||
public void OnStoreThisAttempt(EntityUid uid, FoldableComponent comp, ref StoreMobInItemContainerAttemptEvent args)
|
||||
|
|
@ -107,9 +105,9 @@ namespace Content.Server.Foldable
|
|||
|
||||
AlternativeVerb verb = new()
|
||||
{
|
||||
Act = () => TryToggleFold(component),
|
||||
Act = () => TryToggleFold(uid, component),
|
||||
Text = component.IsFolded ? Loc.GetString("unfold-verb") : Loc.GetString("fold-verb"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/fold.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/fold.svg.192dpi.png")),
|
||||
|
||||
// If the object is unfolded and they click it, they want to fold it, if it's folded, they want to pick it up
|
||||
Priority = component.IsFolded ? 0 : 2,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ public sealed class HealthExaminableSystem : EntitySystem
|
|||
Category = VerbCategory.Examine,
|
||||
Disabled = !detailsRange,
|
||||
Message = Loc.GetString("health-examinable-verb-disabled"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/rejuvenate.svg.192dpi.png"
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/rejuvenate.svg.192dpi.png"))
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
|
|
|
|||
|
|
@ -129,6 +129,8 @@ public sealed partial class HumanoidAppearanceSystem : SharedHumanoidAppearanceS
|
|||
|
||||
targetHumanoid.Species = sourceHumanoid.Species;
|
||||
targetHumanoid.SkinColor = sourceHumanoid.SkinColor;
|
||||
targetHumanoid.EyeColor = sourceHumanoid.EyeColor;
|
||||
targetHumanoid.Age = sourceHumanoid.Age;
|
||||
SetSex(target, sourceHumanoid.Sex, false, targetHumanoid);
|
||||
targetHumanoid.CustomBaseLayers = new(sourceHumanoid.CustomBaseLayers);
|
||||
targetHumanoid.MarkingSet = new(sourceHumanoid.MarkingSet);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using Content.Shared.Humanoid;
|
|||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Humanoid;
|
||||
|
||||
|
|
@ -28,7 +29,7 @@ public sealed partial class HumanoidAppearanceSystem
|
|||
{
|
||||
Text = "Modify markings",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Mobs/Customization/reptilian_parts.rsi/tail_smooth.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Mobs/Customization/reptilian_parts.rsi"), "tail_smooth"),
|
||||
Act = () =>
|
||||
{
|
||||
_uiSystem.TryOpen(uid, HumanoidMarkingModifierKey.Key, actor.PlayerSession);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ public sealed class InteractionPopupSystem : EntitySystem
|
|||
if (HasComp<SleepingComponent>(uid))
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
var curTime = _gameTiming.CurTime;
|
||||
|
||||
if (curTime < component.LastInteractTime + component.InteractDelay)
|
||||
|
|
@ -85,6 +87,5 @@ public sealed class InteractionPopupSystem : EntitySystem
|
|||
}
|
||||
|
||||
component.LastInteractTime = curTime;
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ using Content.Shared.Kitchen;
|
|||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Server.GameObjects;
|
||||
using Content.Server.Body.Systems;
|
||||
|
||||
namespace Content.Server.Kitchen.EntitySystems
|
||||
{
|
||||
|
|
@ -27,6 +29,8 @@ namespace Content.Server.Kitchen.EntitySystems
|
|||
[Dependency] private readonly IAdminLogManager _logger = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly TransformSystem _transform = default!;
|
||||
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
|
||||
|
|
@ -133,9 +137,13 @@ namespace Content.Server.Kitchen.EntitySystems
|
|||
|
||||
_popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-kill", ("user", Identity.Entity(userUid, EntityManager)), ("victim", victimUid)), uid, PopupType.LargeCaution);
|
||||
|
||||
_transform.SetCoordinates(victimUid, Transform(uid).Coordinates);
|
||||
// THE WHAT?
|
||||
// TODO: Need to be able to leave them on the spike to do DoT, see ss13.
|
||||
EntityManager.QueueDeleteEntity(victimUid);
|
||||
var gibs = _bodySystem.GibBody(victimUid);
|
||||
foreach (var gib in gibs) {
|
||||
QueueDel(gib);
|
||||
}
|
||||
|
||||
_audio.Play(component.SpikeSound, Filter.Pvs(uid), uid, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Server.Kitchen.Components;
|
||||
using Content.Shared.Body.Components;
|
||||
|
|
@ -13,6 +13,7 @@ using Content.Shared.Mobs.Components;
|
|||
using Content.Shared.Mobs.Systems;
|
||||
using Robust.Server.Containers;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Kitchen.EntitySystems;
|
||||
|
||||
|
|
@ -149,7 +150,7 @@ public sealed class SharpSystem : EntitySystem
|
|||
},
|
||||
Message = message,
|
||||
Disabled = disabled,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/cutlery.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/cutlery.svg.192dpi.png")),
|
||||
Text = Loc.GetString("butcherable-verb-name"),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using JetBrains.Annotations;
|
|||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
|
|
@ -183,7 +184,7 @@ namespace Content.Server.Light.EntitySystems
|
|||
ActivationVerb verb = new()
|
||||
{
|
||||
Text = Loc.GetString("expendable-light-start-verb"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/light.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/light.svg.192dpi.png")),
|
||||
Act = () => TryActivate(component)
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ namespace Content.Server.Light.EntitySystems
|
|||
EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
// Not guaranteed to be the correct container for our slot, I don't care.
|
||||
UpdateLevel(component);
|
||||
UpdateLevel(uid, component);
|
||||
}
|
||||
|
||||
private void OnEntRemoved(
|
||||
|
|
@ -67,7 +67,7 @@ namespace Content.Server.Light.EntitySystems
|
|||
EntRemovedFromContainerMessage args)
|
||||
{
|
||||
// Ditto above
|
||||
UpdateLevel(component);
|
||||
UpdateLevel(uid, component);
|
||||
}
|
||||
|
||||
private void OnGetActions(EntityUid uid, HandheldLightComponent component, GetItemActionsEvent args)
|
||||
|
|
@ -88,24 +88,24 @@ namespace Content.Server.Light.EntitySystems
|
|||
return;
|
||||
|
||||
if (component.Activated)
|
||||
TurnOff(component);
|
||||
TurnOff(uid, component);
|
||||
else
|
||||
TurnOn(args.Performer, component);
|
||||
TurnOn(args.Performer, uid, component);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnGetState(EntityUid uid, HandheldLightComponent component, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new HandheldLightComponent.HandheldLightComponentState(component.Activated, GetLevel(component));
|
||||
args.State = new HandheldLightComponent.HandheldLightComponentState(component.Activated, GetLevel(uid, component));
|
||||
}
|
||||
|
||||
private byte? GetLevel(HandheldLightComponent component)
|
||||
private byte? GetLevel(EntityUid uid, HandheldLightComponent component)
|
||||
{
|
||||
// Curently every single flashlight has the same number of levels for status and that's all it uses the charge for
|
||||
// Thus we'll just check if the level changes.
|
||||
|
||||
if (!_powerCell.TryGetBatteryFromSlot(component.Owner, out var battery))
|
||||
if (!_powerCell.TryGetBatteryFromSlot(uid, out var battery))
|
||||
return null;
|
||||
|
||||
if (MathHelper.CloseToPercent(battery.CurrentCharge, 0) || component.Wattage > battery.CurrentCharge)
|
||||
|
|
@ -121,9 +121,10 @@ namespace Content.Server.Light.EntitySystems
|
|||
|
||||
private void OnActivate(EntityUid uid, HandheldLightComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (args.Handled) return;
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (ToggleStatus(args.User, component))
|
||||
if (ToggleStatus(args.User, uid, component))
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
|
|
@ -131,9 +132,9 @@ namespace Content.Server.Light.EntitySystems
|
|||
/// Illuminates the light if it is not active, extinguishes it if it is active.
|
||||
/// </summary>
|
||||
/// <returns>True if the light's status was toggled, false otherwise.</returns>
|
||||
public bool ToggleStatus(EntityUid user, HandheldLightComponent component)
|
||||
public bool ToggleStatus(EntityUid user, EntityUid uid, HandheldLightComponent component)
|
||||
{
|
||||
return component.Activated ? TurnOff(component) : TurnOn(user, component);
|
||||
return component.Activated ? TurnOff(uid, component) : TurnOn(user, uid, component);
|
||||
}
|
||||
|
||||
private void OnExamine(EntityUid uid, HandheldLightComponent component, ExaminedEvent args)
|
||||
|
|
@ -155,14 +156,16 @@ namespace Content.Server.Light.EntitySystems
|
|||
|
||||
foreach (var handheld in _activeLights)
|
||||
{
|
||||
var uid = handheld.Owner;
|
||||
|
||||
if (handheld.Deleted)
|
||||
{
|
||||
toRemove.Add(handheld);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Paused(handheld.Owner)) continue;
|
||||
TryUpdate(handheld, frameTime);
|
||||
if (Paused(uid)) continue;
|
||||
TryUpdate(uid, handheld, frameTime);
|
||||
}
|
||||
|
||||
foreach (var light in toRemove)
|
||||
|
|
@ -173,46 +176,47 @@ namespace Content.Server.Light.EntitySystems
|
|||
|
||||
private void AddToggleLightVerb(EntityUid uid, HandheldLightComponent component, GetVerbsEvent<ActivationVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract) return;
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
ActivationVerb verb = new()
|
||||
{
|
||||
Text = Loc.GetString("verb-common-toggle-light"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/light.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/light.svg.192dpi.png")),
|
||||
Act = component.Activated
|
||||
? () => TurnOff(component)
|
||||
: () => TurnOn(args.User, component)
|
||||
? () => TurnOff(uid, component)
|
||||
: () => TurnOn(args.User, uid, component)
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
public bool TurnOff(HandheldLightComponent component, bool makeNoise = true)
|
||||
public bool TurnOff(EntityUid uid, HandheldLightComponent component, bool makeNoise = true)
|
||||
{
|
||||
if (!component.Activated || !TryComp<PointLightComponent>(component.Owner, out var pointLightComponent))
|
||||
if (!component.Activated || !TryComp<PointLightComponent>(uid, out var pointLightComponent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pointLightComponent.Enabled = false;
|
||||
SetActivated(component.Owner, false, component, makeNoise);
|
||||
SetActivated(uid, false, component, makeNoise);
|
||||
component.Level = null;
|
||||
_activeLights.Remove(component);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TurnOn(EntityUid user, HandheldLightComponent component)
|
||||
public bool TurnOn(EntityUid user, EntityUid uid, HandheldLightComponent component)
|
||||
{
|
||||
if (component.Activated || !TryComp<PointLightComponent>(component.Owner, out var pointLightComponent))
|
||||
if (component.Activated || !TryComp<PointLightComponent>(uid, out var pointLightComponent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_powerCell.TryGetBatteryFromSlot(component.Owner, out var battery) &&
|
||||
!TryComp(component.Owner, out battery))
|
||||
if (!_powerCell.TryGetBatteryFromSlot(uid, out var battery) &&
|
||||
!TryComp(uid, out battery))
|
||||
{
|
||||
_audio.PlayPvs(_audio.GetSound(component.TurnOnFailSound), component.Owner);
|
||||
_popup.PopupEntity(Loc.GetString("handheld-light-component-cell-missing-message"), component.Owner, user);
|
||||
_audio.PlayPvs(_audio.GetSound(component.TurnOnFailSound), uid);
|
||||
_popup.PopupEntity(Loc.GetString("handheld-light-component-cell-missing-message"), uid, user);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -221,52 +225,52 @@ namespace Content.Server.Light.EntitySystems
|
|||
// Simple enough.
|
||||
if (component.Wattage > battery.CurrentCharge)
|
||||
{
|
||||
_audio.PlayPvs(_audio.GetSound(component.TurnOnFailSound), component.Owner);
|
||||
_popup.PopupEntity(Loc.GetString("handheld-light-component-cell-dead-message"), component.Owner, user);
|
||||
_audio.PlayPvs(_audio.GetSound(component.TurnOnFailSound), uid);
|
||||
_popup.PopupEntity(Loc.GetString("handheld-light-component-cell-dead-message"), uid, user);
|
||||
return false;
|
||||
}
|
||||
|
||||
pointLightComponent.Enabled = true;
|
||||
SetActivated(component.Owner, true, component, true);
|
||||
SetActivated(uid, true, component, true);
|
||||
_activeLights.Add(component);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void TryUpdate(HandheldLightComponent component, float frameTime)
|
||||
public void TryUpdate(EntityUid uid, HandheldLightComponent component, float frameTime)
|
||||
{
|
||||
if (!_powerCell.TryGetBatteryFromSlot(component.Owner, out var battery) &&
|
||||
!TryComp(component.Owner, out battery))
|
||||
if (!_powerCell.TryGetBatteryFromSlot(uid, out var battery) &&
|
||||
!TryComp(uid, out battery))
|
||||
{
|
||||
TurnOff(component, false);
|
||||
TurnOff(uid, component, false);
|
||||
return;
|
||||
}
|
||||
|
||||
var appearanceComponent = EntityManager.GetComponent<AppearanceComponent>(component.Owner);
|
||||
var appearanceComponent = EntityManager.GetComponentOrNull<AppearanceComponent>(uid);
|
||||
|
||||
var fraction = battery.CurrentCharge / battery.MaxCharge;
|
||||
if (fraction >= 0.30)
|
||||
{
|
||||
_appearance.SetData(component.Owner, HandheldLightVisuals.Power, HandheldLightPowerStates.FullPower, appearanceComponent);
|
||||
_appearance.SetData(uid, HandheldLightVisuals.Power, HandheldLightPowerStates.FullPower, appearanceComponent);
|
||||
}
|
||||
else if (fraction >= 0.10)
|
||||
{
|
||||
_appearance.SetData(component.Owner, HandheldLightVisuals.Power, HandheldLightPowerStates.LowPower, appearanceComponent);
|
||||
_appearance.SetData(uid, HandheldLightVisuals.Power, HandheldLightPowerStates.LowPower, appearanceComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
_appearance.SetData(component.Owner, HandheldLightVisuals.Power, HandheldLightPowerStates.Dying, appearanceComponent);
|
||||
_appearance.SetData(uid, HandheldLightVisuals.Power, HandheldLightPowerStates.Dying, appearanceComponent);
|
||||
}
|
||||
|
||||
if (component.Activated && !battery.TryUseCharge(component.Wattage * frameTime))
|
||||
TurnOff(component, false);
|
||||
TurnOff(uid, component, false);
|
||||
|
||||
UpdateLevel(component);
|
||||
UpdateLevel(uid, component);
|
||||
}
|
||||
|
||||
private void UpdateLevel(HandheldLightComponent comp)
|
||||
private void UpdateLevel(EntityUid uid, HandheldLightComponent comp)
|
||||
{
|
||||
var level = GetLevel(comp);
|
||||
var level = GetLevel(uid, comp);
|
||||
|
||||
if (level == comp.Level)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using Content.Shared.Verbs;
|
|||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
|
|
@ -48,7 +49,7 @@ namespace Content.Server.Light.EntitySystems
|
|||
|
||||
ActivationVerb verb = new();
|
||||
verb.Text = Loc.GetString("toggle-flashlight-verb-get-data-text");
|
||||
verb.IconTexture = "/Textures/Interface/VerbIcons/light.svg.192dpi.png";
|
||||
verb.Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/light.svg.192dpi.png"));
|
||||
verb.Act = () => ToggleLight(uid, component);
|
||||
verb.Priority = -1; // For things like PDA's, Open-UI and other verbs that should be higher priority.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Content.Shared.Interaction;
|
|||
using Content.Shared.MachineLinking;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.MachineLinking.System
|
||||
{
|
||||
|
|
@ -65,7 +66,7 @@ namespace Content.Server.MachineLinking.System
|
|||
Category = VerbCategory.Lever,
|
||||
Message = Loc.GetString("two-way-lever-cant"),
|
||||
Disabled = component.State == TwoWayLeverState.Left,
|
||||
IconTexture = $"/Textures/Interface/VerbIcons/{_leftToggleImage}",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath($"/Textures/Interface/VerbIcons/{_leftToggleImage}")),
|
||||
Text = Loc.GetString("two-way-lever-left"),
|
||||
};
|
||||
|
||||
|
|
@ -86,7 +87,7 @@ namespace Content.Server.MachineLinking.System
|
|||
Category = VerbCategory.Lever,
|
||||
Message = Loc.GetString("two-way-lever-cant"),
|
||||
Disabled = component.State == TwoWayLeverState.Right,
|
||||
IconTexture = $"/Textures/Interface/VerbIcons/{_rightToggleImage}",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath($"/Textures/Interface/VerbIcons/{_rightToggleImage}")),
|
||||
Text = Loc.GetString("two-way-lever-right"),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,12 @@ namespace Content.Server.Medical.Components
|
|||
[DataField("delay")]
|
||||
public float Delay = 3f;
|
||||
|
||||
/// <summary>
|
||||
/// Cancel token to prevent rapid healing
|
||||
/// </summary>
|
||||
[DataField("cancelToken")]
|
||||
public CancellationTokenSource? CancelToken;
|
||||
|
||||
/// <summary>
|
||||
/// Delay multiplier when healing yourself.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Threading;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.DoAfter;
|
||||
|
|
@ -40,6 +41,12 @@ public sealed class HealingSystem : EntitySystem
|
|||
|
||||
private void OnDoAfter(EntityUid uid, DamageableComponent component, DoAfterEvent<HealingData> args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
{
|
||||
args.AdditionalData.HealingComponent.CancelToken = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Handled || args.Cancelled || _mobStateSystem.IsDead(uid) || args.Args.Used == null)
|
||||
return;
|
||||
|
||||
|
|
@ -67,6 +74,7 @@ public sealed class HealingSystem : EntitySystem
|
|||
if (args.AdditionalData.HealingComponent.HealingEndSound != null)
|
||||
_audio.PlayPvs(args.AdditionalData.HealingComponent.HealingEndSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f));
|
||||
|
||||
args.AdditionalData.HealingComponent.CancelToken = null;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
|
|
@ -90,7 +98,7 @@ public sealed class HealingSystem : EntitySystem
|
|||
|
||||
private bool TryHeal(EntityUid uid, EntityUid user, EntityUid target, HealingComponent component)
|
||||
{
|
||||
if (_mobStateSystem.IsDead(target) || !TryComp<DamageableComponent>(target, out var targetDamage))
|
||||
if (_mobStateSystem.IsDead(target) || !TryComp<DamageableComponent>(target, out var targetDamage) || component.CancelToken != null)
|
||||
return false;
|
||||
|
||||
if (targetDamage.TotalDamage == 0)
|
||||
|
|
@ -108,14 +116,21 @@ public sealed class HealingSystem : EntitySystem
|
|||
if (component.HealingBeginSound != null)
|
||||
_audio.PlayPvs(component.HealingBeginSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f));
|
||||
|
||||
var delay = user != target
|
||||
var isNotSelf = user != target;
|
||||
|
||||
var delay = isNotSelf
|
||||
? component.Delay
|
||||
: component.Delay * GetScaledHealingPenalty(user, component);
|
||||
|
||||
component.CancelToken = new CancellationTokenSource();
|
||||
|
||||
var healingData = new HealingData(component, stack);
|
||||
|
||||
var doAfterEventArgs = new DoAfterEventArgs(user, delay, target: target, used: uid)
|
||||
var doAfterEventArgs = new DoAfterEventArgs(user, delay, cancelToken: component.CancelToken.Token,target: target, used: uid)
|
||||
{
|
||||
//Raise the event on the target if it's not self, otherwise raise it on self.
|
||||
RaiseOnTarget = isNotSelf,
|
||||
RaiseOnUser = !isNotSelf,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnTargetMove = true,
|
||||
// Didn't break on damage as they may be trying to prevent it and
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using Content.Shared.Verbs;
|
|||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Medical
|
||||
{
|
||||
|
|
@ -82,7 +83,7 @@ namespace Content.Server.Medical
|
|||
StartListening(component.Stethoscope, uid, args.Target, stetho); // start doafter
|
||||
},
|
||||
Text = Loc.GetString("stethoscope-verb"),
|
||||
IconTexture = "Clothing/Neck/Misc/stethoscope.rsi/icon.png",
|
||||
Icon = new SpriteSpecifier.Rsi(new ResourcePath("Clothing/Neck/Misc/stethoscope.rsi"), "icon"),
|
||||
Priority = 2
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
using Content.Server.Access.Systems;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Administration.Systems;
|
||||
using Content.Server.Cloning;
|
||||
using Content.Server.Mind.Components;
|
||||
using Content.Server.PDA;
|
||||
using Content.Server.StationRecords;
|
||||
using Content.Server.StationRecords.Systems;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.PDA;
|
||||
|
|
|
|||
20
Content.Server/Motd/GetMOTDCommand.cs
Normal file
20
Content.Server/Motd/GetMOTDCommand.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Motd;
|
||||
|
||||
/// <summary>
|
||||
/// A command that can be used by any player to print the Message of the Day.
|
||||
/// </summary>
|
||||
[AnyCommand]
|
||||
public sealed class GetMotdCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
public override string Command => "get-motd";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
_entityManager.EntitySysManager.GetEntitySystem<MOTDSystem>().TrySendMOTD(shell);
|
||||
}
|
||||
}
|
||||
36
Content.Server/Motd/MOTDCommand.cs
Normal file
36
Content.Server/Motd/MOTDCommand.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
using Content.Server.Administration.Managers;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Motd;
|
||||
|
||||
/// <summary>
|
||||
/// A console command which acts as an alias for <see cref="GetMotdCommand"/> or <see cref="SetMotdCommand"/> depending on the number of arguments given.
|
||||
/// </summary>
|
||||
[AnyCommand]
|
||||
internal sealed class MOTDCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||
|
||||
public override string Command => "motd";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var player = (IPlayerSession?)shell.Player;
|
||||
if (args.Length < 1 || (player != null && _adminManager is AdminManager aMan && !aMan.CanCommand(player, "set-motd")))
|
||||
shell.ConsoleHost.ExecuteCommand(shell.Player, "get-motd");
|
||||
else
|
||||
shell.ConsoleHost.ExecuteCommand(shell.Player, $"set-motd {string.Join(" ", args)}");
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
var player = (IPlayerSession?)shell.Player;
|
||||
if (player != null && _adminManager is AdminManager aMan && !aMan.CanCommand(player, "set-motd"))
|
||||
return CompletionResult.Empty;
|
||||
if (args.Length == 1)
|
||||
return CompletionResult.FromHint(Loc.GetString("cmd-set-motd-hint-head"));
|
||||
return CompletionResult.FromHint(Loc.GetString("cmd-set-motd-hint-cont"));
|
||||
}
|
||||
}
|
||||
101
Content.Server/Motd/MOTDSystem.cs
Normal file
101
Content.Server/Motd/MOTDSystem.cs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Chat;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Server.Motd;
|
||||
|
||||
/// <summary>
|
||||
/// The system that handles broadcasting the Message Of The Day to players when they join the lobby/the MOTD changes/they ask for it to be printed.
|
||||
/// </summary>
|
||||
public sealed class MOTDSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The cached value of the Message of the Day. Used for fast access.
|
||||
/// </summary>
|
||||
private string _messageOfTheDay = "";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_configurationManager.OnValueChanged(CCVars.MOTD, OnMOTDChanged, invokeImmediately: true);
|
||||
SubscribeLocalEvent<PlayerJoinedLobbyEvent>(OnPlayerJoinedLobby);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
_configurationManager.UnsubValueChanged(CCVars.MOTD, OnMOTDChanged);
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the Message Of The Day, if any, to all connected players.
|
||||
/// </summary>
|
||||
public void TrySendMOTD()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_messageOfTheDay))
|
||||
return;
|
||||
|
||||
var wrappedMessage = Loc.GetString("motd-wrap-message", ("motd", _messageOfTheDay));
|
||||
_chatManager.ChatMessageToAll(ChatChannel.Server, _messageOfTheDay, wrappedMessage, source: EntityUid.Invalid, hideChat: false, recordReplay: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the Message Of The Day, if any, to a specific player.
|
||||
/// </summary>
|
||||
public void TrySendMOTD(IPlayerSession player)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_messageOfTheDay))
|
||||
return;
|
||||
|
||||
var wrappedMessage = Loc.GetString("motd-wrap-message", ("motd", _messageOfTheDay));
|
||||
_chatManager.ChatMessageToOne(ChatChannel.Server, _messageOfTheDay, wrappedMessage, source: EntityUid.Invalid, hideChat: false, client: player.ConnectedClient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the Message Of The Day, if any, to a specific player's console and chat.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used by the MOTD console command because we can't tell whether the player is using `console or /console so we send the message to both.
|
||||
/// </remarks>
|
||||
public void TrySendMOTD(IConsoleShell shell)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_messageOfTheDay))
|
||||
return;
|
||||
|
||||
var wrappedMessage = Loc.GetString("motd-wrap-message", ("motd", _messageOfTheDay));
|
||||
shell.WriteLine(wrappedMessage);
|
||||
if (shell.Player is IPlayerSession player)
|
||||
_chatManager.ChatMessageToOne(ChatChannel.Server, _messageOfTheDay, wrappedMessage, source: EntityUid.Invalid, hideChat: false, client: player.ConnectedClient);
|
||||
}
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Posts the Message Of The Day to any players who join the lobby.
|
||||
/// </summary>
|
||||
private void OnPlayerJoinedLobby(PlayerJoinedLobbyEvent ev)
|
||||
{
|
||||
TrySendMOTD(ev.PlayerSession);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts changes to the Message Of The Day to all players.
|
||||
/// </summary>
|
||||
private void OnMOTDChanged(string val)
|
||||
{
|
||||
if (val == _messageOfTheDay)
|
||||
return;
|
||||
|
||||
_messageOfTheDay = val;
|
||||
TrySendMOTD();
|
||||
}
|
||||
|
||||
#endregion Event Handlers
|
||||
}
|
||||
55
Content.Server/Motd/SetMOTDCommand.cs
Normal file
55
Content.Server/Motd/SetMOTDCommand.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
using Content.Server.Administration;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Motd;
|
||||
|
||||
/// <summary>
|
||||
/// A console command usable by any user which prints or sets the Message of the Day.
|
||||
/// </summary>
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
public sealed class SetMotdCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IAdminLogManager _adminLogManager = default!;
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
|
||||
public override string Command => "set-motd";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
string motd = "";
|
||||
var player = (IPlayerSession?)shell.Player;
|
||||
if (args.Length > 0)
|
||||
{
|
||||
motd = string.Join(" ", args).Trim();
|
||||
if (player != null && _chatManager.MessageCharacterLimit(player, motd))
|
||||
return; // check function prints its own error response
|
||||
}
|
||||
|
||||
_configurationManager.SetCVar(CCVars.MOTD, motd); // A hook in MOTDSystem broadcasts changes to the MOTD to everyone so we don't need to do it here.
|
||||
if (string.IsNullOrEmpty(motd))
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-set-motd-cleared-motd-message"));
|
||||
_adminLogManager.Add(LogType.Chat, LogImpact.Low, $"{(player == null ? "LOCALHOST" : player.ConnectedClient.UserName):Player} cleared the MOTD for the server.");
|
||||
}
|
||||
else
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-set-motd-set-motd-message", ("motd", motd)));
|
||||
_adminLogManager.Add(LogType.Chat, LogImpact.Low, $"{(player == null ? "LOCALHOST" : player.ConnectedClient.UserName):Player} set the MOTD for the server to \"{motd:motd}\"");
|
||||
}
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
if (args.Length == 1)
|
||||
return CompletionResult.FromHint(Loc.GetString("cmd-set-motd-hint-head"));
|
||||
return CompletionResult.FromHint(Loc.GetString("cmd-set-motd-hint-cont"));
|
||||
}
|
||||
}
|
||||
|
|
@ -34,6 +34,13 @@ namespace Content.Server.Nutrition.Components
|
|||
[DataField("burstSound")]
|
||||
public SoundSpecifier BurstSound = new SoundPathSpecifier("/Audio/Effects/flash_bang.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// Is this drink being forced on someone else?
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DataField("forceDrink")]
|
||||
public bool ForceDrink;
|
||||
|
||||
/// <summary>
|
||||
/// How long it takes to drink this yourself.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -40,6 +40,13 @@ namespace Content.Server.Nutrition.Components
|
|||
[DataField("eatMessage")]
|
||||
public string EatMessage = "food-nom";
|
||||
|
||||
/// <summary>
|
||||
/// Is this entity being forcefed?
|
||||
/// Prevents the entity from being forced to eat multiple times if not self
|
||||
/// </summary>
|
||||
[DataField("forceFeed")]
|
||||
public bool ForceFeed;
|
||||
|
||||
/// <summary>
|
||||
/// How long it takes to eat the food personally.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
|
||||
private bool TryDrink(EntityUid user, EntityUid target, DrinkComponent drink, EntityUid item)
|
||||
{
|
||||
if (!EntityManager.HasComponent<BodyComponent>(target))
|
||||
if (!EntityManager.HasComponent<BodyComponent>(target) || drink.ForceDrink)
|
||||
return false;
|
||||
|
||||
if (!drink.Opened)
|
||||
|
|
@ -241,9 +241,9 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
if (!_interactionSystem.InRangeUnobstructed(user, item, popup: true))
|
||||
return true;
|
||||
|
||||
var forceDrink = user != target;
|
||||
drink.ForceDrink = user != target;
|
||||
|
||||
if (forceDrink)
|
||||
if (drink.ForceDrink)
|
||||
{
|
||||
var userName = Identity.Entity(user, EntityManager);
|
||||
|
||||
|
|
@ -264,7 +264,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
|
||||
var drinkData = new DrinkData(drinkSolution, flavors);
|
||||
|
||||
var doAfterEventArgs = new DoAfterEventArgs(user, forceDrink ? drink.ForceFeedDelay : drink.Delay,
|
||||
var doAfterEventArgs = new DoAfterEventArgs(user, drink.ForceDrink ? drink.ForceFeedDelay : drink.Delay,
|
||||
target: target, used: item)
|
||||
{
|
||||
BreakOnUserMove = moveBreak,
|
||||
|
|
@ -286,6 +286,14 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
/// </summary>
|
||||
private void OnDoAfter(EntityUid uid, DrinkComponent component, DoAfterEvent<DrinkData> args)
|
||||
{
|
||||
//Special cancel if they're force feeding someone.
|
||||
//Allows self to drink multiple times but prevents force feeding drinks to others rapidly.
|
||||
if (args.Cancelled && component.ForceDrink)
|
||||
{
|
||||
component.ForceDrink = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Handled || args.Cancelled || component.Deleted)
|
||||
return;
|
||||
|
||||
|
|
@ -295,11 +303,11 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
var transferAmount = FixedPoint2.Min(component.TransferAmount, args.AdditionalData.DrinkSolution.Volume);
|
||||
var drained = _solutionContainerSystem.Drain(uid, args.AdditionalData.DrinkSolution, transferAmount);
|
||||
|
||||
var forceDrink = args.Args.Target.Value != args.Args.User;
|
||||
//var forceDrink = args.Args.Target.Value != args.Args.User;
|
||||
|
||||
if (!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(args.Args.Target.Value, out var stomachs, body))
|
||||
{
|
||||
_popupSystem.PopupEntity(forceDrink ? Loc.GetString("drink-component-try-use-drink-cannot-drink-other") : Loc.GetString("drink-component-try-use-drink-had-enough"), args.Args.Target.Value, args.Args.User);
|
||||
_popupSystem.PopupEntity(component.ForceDrink ? Loc.GetString("drink-component-try-use-drink-cannot-drink-other") : Loc.GetString("drink-component-try-use-drink-had-enough"), args.Args.Target.Value, args.Args.User);
|
||||
|
||||
if (HasComp<RefillableSolutionComponent>(args.Args.Target.Value))
|
||||
{
|
||||
|
|
@ -320,7 +328,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough"), args.Args.Target.Value, args.Args.Target.Value);
|
||||
|
||||
if (forceDrink)
|
||||
if (component.ForceDrink)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough-other"), args.Args.Target.Value, args.Args.User);
|
||||
_spillableSystem.SpillAt(args.Args.Target.Value, drained, "PuddleSmear");
|
||||
|
|
@ -334,7 +342,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
|
||||
var flavors = args.AdditionalData.FlavorMessage;
|
||||
|
||||
if (forceDrink)
|
||||
if (component.ForceDrink)
|
||||
{
|
||||
var targetName = Identity.Entity(args.Args.Target.Value, EntityManager);
|
||||
var userName = Identity.Entity(args.Args.User, EntityManager);
|
||||
|
|
@ -366,6 +374,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
//TODO: Grab the stomach UIDs somehow without using Owner
|
||||
_stomachSystem.TryTransferSolution(firstStomach.Value.Comp.Owner, drained, firstStomach.Value.Comp);
|
||||
|
||||
component.ForceDrink = false;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
|
|
@ -387,7 +396,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
{
|
||||
TryDrink(ev.User, ev.User, component, uid);
|
||||
},
|
||||
IconTexture = "/Textures/Interface/VerbIcons/drink.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/drink.svg.192dpi.png")),
|
||||
Text = Loc.GetString("drink-system-verb-drink"),
|
||||
Priority = 2
|
||||
};
|
||||
|
|
|
|||
|
|
@ -86,8 +86,8 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
if (food == user || EntityManager.TryGetComponent<MobStateComponent>(food, out var mobState) && _mobStateSystem.IsAlive(food, mobState)) // Suppresses eating alive mobs
|
||||
return false;
|
||||
|
||||
// Target can't be fed
|
||||
if (!EntityManager.HasComponent<BodyComponent>(target))
|
||||
// Target can't be fed or they're already forcefeeding
|
||||
if (!EntityManager.HasComponent<BodyComponent>(target) || foodComp.ForceFeed)
|
||||
return false;
|
||||
|
||||
if (!_solutionContainerSystem.TryGetSolution(food, foodComp.SolutionName, out var foodSolution))
|
||||
|
|
@ -111,9 +111,9 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
if (!_interactionSystem.InRangeUnobstructed(user, food, popup: true))
|
||||
return true;
|
||||
|
||||
var forceFeed = user != target;
|
||||
foodComp.ForceFeed = user != target;
|
||||
|
||||
if (forceFeed)
|
||||
if (foodComp.ForceFeed)
|
||||
{
|
||||
var userName = Identity.Entity(user, EntityManager);
|
||||
_popupSystem.PopupEntity(Loc.GetString("food-system-force-feed", ("user", userName)),
|
||||
|
|
@ -128,16 +128,16 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
_adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(target):target} is eating {ToPrettyString(food):food} {SolutionContainerSystem.ToPrettyString(foodSolution)}");
|
||||
}
|
||||
|
||||
var moveBreak = user != target;
|
||||
|
||||
var foodData = new FoodData(foodSolution, flavors, utensils);
|
||||
|
||||
var doAfterEventArgs = new DoAfterEventArgs(user, forceFeed ? foodComp.ForceFeedDelay : foodComp.Delay, target: target, used: food)
|
||||
var doAfterEventArgs = new DoAfterEventArgs(user, foodComp.ForceFeed ? foodComp.ForceFeedDelay : foodComp.Delay, target: target, used: food)
|
||||
{
|
||||
BreakOnUserMove = moveBreak,
|
||||
RaiseOnTarget = foodComp.ForceFeed,
|
||||
RaiseOnUser = !foodComp.ForceFeed,
|
||||
BreakOnUserMove = foodComp.ForceFeed,
|
||||
BreakOnDamage = true,
|
||||
BreakOnStun = true,
|
||||
BreakOnTargetMove = moveBreak,
|
||||
BreakOnTargetMove = foodComp.ForceFeed,
|
||||
MovementThreshold = 0.01f,
|
||||
DistanceThreshold = 1.0f,
|
||||
NeedHand = true
|
||||
|
|
@ -151,6 +151,13 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
|
||||
private void OnDoAfter(EntityUid uid, FoodComponent component, DoAfterEvent<FoodData> args)
|
||||
{
|
||||
//Prevents the target from being force fed food but allows the user to chow down
|
||||
if (args.Cancelled && component.ForceFeed)
|
||||
{
|
||||
component.ForceFeed = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Cancelled || args.Handled || component.Deleted || args.Args.Target == null)
|
||||
return;
|
||||
|
||||
|
|
@ -166,13 +173,11 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
//TODO: Get the stomach UID somehow without nabbing owner
|
||||
var firstStomach = stomachs.FirstOrNull(stomach => _stomachSystem.CanTransferSolution(stomach.Comp.Owner, split));
|
||||
|
||||
var forceFeed = args.Args.Target.Value != args.Args.User;
|
||||
|
||||
// No stomach so just popup a message that they can't eat.
|
||||
if (firstStomach == null)
|
||||
{
|
||||
_solutionContainerSystem.TryAddSolution(uid, args.AdditionalData.FoodSolution, split);
|
||||
_popupSystem.PopupEntity(forceFeed ? Loc.GetString("food-system-you-cannot-eat-any-more-other") : Loc.GetString("food-system-you-cannot-eat-any-more"), args.Args.Target.Value, args.Args.User);
|
||||
_popupSystem.PopupEntity(component.ForceFeed ? Loc.GetString("food-system-you-cannot-eat-any-more-other") : Loc.GetString("food-system-you-cannot-eat-any-more"), args.Args.Target.Value, args.Args.User);
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
|
@ -182,7 +187,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
|
||||
var flavors = args.AdditionalData.FlavorMessage;
|
||||
|
||||
if (forceFeed)
|
||||
if (component.ForceFeed)
|
||||
{
|
||||
var targetName = Identity.Entity(args.Args.Target.Value, EntityManager);
|
||||
var userName = Identity.Entity(args.Args.User, EntityManager);
|
||||
|
|
@ -264,7 +269,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||
{
|
||||
TryFeed(ev.User, ev.User, uid, component);
|
||||
},
|
||||
IconTexture = "/Textures/Interface/VerbIcons/cutlery.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/cutlery.svg.192dpi.png")),
|
||||
Text = Loc.GetString("food-system-verb-eat"),
|
||||
Priority = -1
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using Content.Server.Cargo.Systems;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Rejuvenate;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Server.Power.EntitySystems
|
||||
|
|
@ -13,12 +14,24 @@ namespace Content.Server.Power.EntitySystems
|
|||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ExaminableBatteryComponent, ExaminedEvent>(OnExamine);
|
||||
SubscribeLocalEvent<PowerNetworkBatteryComponent, RejuvenateEvent>(OnNetBatteryRejuvenate);
|
||||
SubscribeLocalEvent<BatteryComponent, RejuvenateEvent>(OnBatteryRejuvenate);
|
||||
SubscribeLocalEvent<BatteryComponent, PriceCalculationEvent>(CalculateBatteryPrice);
|
||||
|
||||
SubscribeLocalEvent<NetworkBatteryPreSync>(PreSync);
|
||||
SubscribeLocalEvent<NetworkBatteryPostSync>(PostSync);
|
||||
}
|
||||
|
||||
private void OnNetBatteryRejuvenate(EntityUid uid, PowerNetworkBatteryComponent component, RejuvenateEvent args)
|
||||
{
|
||||
component.NetworkBattery.CurrentStorage = component.NetworkBattery.Capacity;
|
||||
}
|
||||
|
||||
private void OnBatteryRejuvenate(EntityUid uid, BatteryComponent component, RejuvenateEvent args)
|
||||
{
|
||||
component.CurrentCharge = component.MaxCharge;
|
||||
}
|
||||
|
||||
private void OnExamine(EntityUid uid, ExaminableBatteryComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (!TryComp<BatteryComponent>(uid, out var batteryComponent))
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ namespace Content.Server.Power.EntitySystems
|
|||
Message = Loc.GetString("cable-multitool-system-verb-tooltip"),
|
||||
Text = Loc.GetString("cable-multitool-system-verb-name"),
|
||||
Category = VerbCategory.Examine,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/zap.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/zap.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
{
|
||||
var markup = FormattedMessage.FromMarkup(GenerateCableMarkup(uid));
|
||||
|
|
|
|||
|
|
@ -7,14 +7,19 @@ using Content.Shared.Verbs;
|
|||
using Content.Shared.Database;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Power.EntitySystems
|
||||
{
|
||||
public sealed class PowerReceiverSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly AudioSystem _audio = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
|
@ -27,9 +32,25 @@ namespace Content.Server.Power.EntitySystems
|
|||
SubscribeLocalEvent<ApcPowerProviderComponent, ExtensionCableSystem.ReceiverConnectedEvent>(OnReceiverConnected);
|
||||
SubscribeLocalEvent<ApcPowerProviderComponent, ExtensionCableSystem.ReceiverDisconnectedEvent>(OnReceiverDisconnected);
|
||||
|
||||
SubscribeLocalEvent<ApcPowerReceiverComponent, GetVerbsEvent<Verb>>(OnGetVerbs);
|
||||
SubscribeLocalEvent<PowerSwitchComponent, GetVerbsEvent<AlternativeVerb>>(AddSwitchPowerVerb);
|
||||
}
|
||||
|
||||
private void OnGetVerbs(EntityUid uid, ApcPowerReceiverComponent component, GetVerbsEvent<Verb> args)
|
||||
{
|
||||
if (!_adminManager.HasAdminFlag(args.User, AdminFlags.Admin))
|
||||
return;
|
||||
|
||||
// add debug verb to toggle power requirements
|
||||
args.Verbs.Add(new()
|
||||
{
|
||||
Text = Loc.GetString("verb-debug-toggle-need-power"),
|
||||
Category = VerbCategory.Debug,
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/smite.svg.192dpi.png")), // "smite" is a lightning bolt
|
||||
Act = () => component.NeedsPower = !component.NeedsPower
|
||||
});
|
||||
}
|
||||
|
||||
///<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>
|
||||
|
|
@ -106,7 +127,7 @@ namespace Content.Server.Power.EntitySystems
|
|||
{
|
||||
TogglePower(uid, user: args.User);
|
||||
},
|
||||
IconTexture = "/Textures/Interface/VerbIcons/Spare/poweronoff.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/Spare/poweronoff.svg.192dpi.png")),
|
||||
Text = Loc.GetString("power-switch-component-toggle-verb"),
|
||||
Priority = -3
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using Robust.Shared.Containers;
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Server.Kitchen.Components;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Rejuvenate;
|
||||
|
||||
namespace Content.Server.PowerCell;
|
||||
|
||||
|
|
@ -29,6 +30,7 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
|
|||
|
||||
SubscribeLocalEvent<PowerCellComponent, ChargeChangedEvent>(OnChargeChanged);
|
||||
SubscribeLocalEvent<PowerCellComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<PowerCellComponent, RejuvenateEvent>(OnRejuvenate);
|
||||
|
||||
SubscribeLocalEvent<PowerCellComponent, ExaminedEvent>(OnCellExamined);
|
||||
|
||||
|
|
@ -37,6 +39,11 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
|
|||
SubscribeLocalEvent<BatteryComponent, BeingMicrowavedEvent>(OnMicrowaved);
|
||||
}
|
||||
|
||||
private void OnRejuvenate(EntityUid uid, PowerCellComponent component, RejuvenateEvent args)
|
||||
{
|
||||
component.IsRigged = false;
|
||||
}
|
||||
|
||||
private void OnSlotMicrowaved(EntityUid uid, PowerCellSlotComponent component, BeingMicrowavedEvent args)
|
||||
{
|
||||
if (_itemSlotsSystem.TryGetSlot(uid, component.CellSlotId, out ItemSlot? slot))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
namespace Content.Server.Prayer
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Prayer
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows an entity to be prayed on in the context menu
|
||||
|
|
@ -39,7 +41,7 @@
|
|||
/// </summary>
|
||||
[DataField("verbImage")]
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public string VerbImage = "/Textures/Interface/pray.svg.png";
|
||||
public SpriteSpecifier? VerbImage = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/pray.svg.png"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public sealed class PrayerSystem : EntitySystem
|
|||
var prayerVerb = new ActivationVerb
|
||||
{
|
||||
Text = Loc.GetString(comp.Verb),
|
||||
IconTexture = comp.VerbImage == "" ? null : comp.VerbImage,
|
||||
Icon = comp.VerbImage,
|
||||
Act = () =>
|
||||
{
|
||||
if (comp.BibleUserOnly && !EntityManager.TryGetComponent<BibleUserComponent>(args.User, out var bibleUser))
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
using Content.Server.Popups;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Rotatable;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Rotatable
|
||||
{
|
||||
|
|
@ -11,6 +13,8 @@ namespace Content.Server.Rotatable
|
|||
/// </summary>
|
||||
public sealed class RotatableSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<FlippableComponent, GetVerbsEvent<Verb>>(AddFlipVerb);
|
||||
|
|
@ -19,13 +23,15 @@ namespace Content.Server.Rotatable
|
|||
|
||||
private void AddFlipVerb(EntityUid uid, FlippableComponent component, GetVerbsEvent<Verb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || component.MirrorEntity == null)
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Act = () => TryFlip(component, args.User);
|
||||
verb.Text = Loc.GetString("flippable-verb-get-data-text");
|
||||
verb.DoContactInteraction = true;
|
||||
Verb verb = new()
|
||||
{
|
||||
Act = () => TryFlip(uid, component, args.User),
|
||||
Text = Loc.GetString("flippable-verb-get-data-text"),
|
||||
DoContactInteraction = true
|
||||
};
|
||||
// TODO VERB ICONS Add Uno reverse card style icon?
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
|
@ -39,16 +45,16 @@ namespace Content.Server.Rotatable
|
|||
|
||||
// Check if the object is anchored, and whether we are still allowed to rotate it.
|
||||
if (!component.RotateWhileAnchored &&
|
||||
EntityManager.TryGetComponent(component.Owner, out PhysicsComponent? physics) &&
|
||||
EntityManager.TryGetComponent(uid, out PhysicsComponent? physics) &&
|
||||
physics.BodyType == BodyType.Static)
|
||||
return;
|
||||
|
||||
Verb resetRotation = new ()
|
||||
{
|
||||
DoContactInteraction = true,
|
||||
Act = () => EntityManager.GetComponent<TransformComponent>(component.Owner).LocalRotation = Angle.Zero,
|
||||
Act = () => EntityManager.GetComponent<TransformComponent>(uid).LocalRotation = Angle.Zero,
|
||||
Category = VerbCategory.Rotate,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/refresh.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/refresh.svg.192dpi.png")),
|
||||
Text = "Reset",
|
||||
Priority = -2, // show CCW, then CW, then reset
|
||||
CloseMenu = false,
|
||||
|
|
@ -58,9 +64,9 @@ namespace Content.Server.Rotatable
|
|||
// rotate clockwise
|
||||
Verb rotateCW = new()
|
||||
{
|
||||
Act = () => EntityManager.GetComponent<TransformComponent>(component.Owner).LocalRotation -= component.Increment,
|
||||
Act = () => EntityManager.GetComponent<TransformComponent>(uid).LocalRotation -= component.Increment,
|
||||
Category = VerbCategory.Rotate,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/rotate_cw.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/rotate_cw.svg.192dpi.png")),
|
||||
Priority = -1,
|
||||
CloseMenu = false, // allow for easy double rotations.
|
||||
};
|
||||
|
|
@ -69,9 +75,9 @@ namespace Content.Server.Rotatable
|
|||
// rotate counter-clockwise
|
||||
Verb rotateCCW = new()
|
||||
{
|
||||
Act = () => EntityManager.GetComponent<TransformComponent>(component.Owner).LocalRotation += component.Increment,
|
||||
Act = () => EntityManager.GetComponent<TransformComponent>(uid).LocalRotation += component.Increment,
|
||||
Category = VerbCategory.Rotate,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/rotate_ccw.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/rotate_ccw.svg.192dpi.png")),
|
||||
Priority = 0,
|
||||
CloseMenu = false, // allow for easy double rotations.
|
||||
};
|
||||
|
|
@ -81,21 +87,21 @@ namespace Content.Server.Rotatable
|
|||
/// <summary>
|
||||
/// Replace a flippable entity with it's flipped / mirror-symmetric entity.
|
||||
/// </summary>
|
||||
public void TryFlip(FlippableComponent component, EntityUid user)
|
||||
public void TryFlip(EntityUid uid, FlippableComponent component, EntityUid user)
|
||||
{
|
||||
if (EntityManager.TryGetComponent(component.Owner, out PhysicsComponent? physics) &&
|
||||
if (EntityManager.TryGetComponent(uid, out PhysicsComponent? physics) &&
|
||||
physics.BodyType == BodyType.Static)
|
||||
{
|
||||
component.Owner.PopupMessage(user, Loc.GetString("flippable-component-try-flip-is-stuck"));
|
||||
_popup.PopupEntity(Loc.GetString("flippable-component-try-flip-is-stuck"), uid, user);
|
||||
return;
|
||||
}
|
||||
|
||||
var oldTransform = EntityManager.GetComponent<TransformComponent>(component.Owner);
|
||||
var oldTransform = EntityManager.GetComponent<TransformComponent>(uid);
|
||||
var entity = EntityManager.SpawnEntity(component.MirrorEntity, oldTransform.Coordinates);
|
||||
var newTransform = EntityManager.GetComponent<TransformComponent>(entity);
|
||||
newTransform.LocalRotation = oldTransform.LocalRotation;
|
||||
newTransform.Anchored = false;
|
||||
EntityManager.DeleteEntity(component.Owner);
|
||||
EntityManager.DeleteEntity(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
using Content.Shared.StationRecords;
|
||||
|
||||
namespace Content.Server.StationRecords;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class StationRecordKeyStorageComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The key stored in this component.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public StationRecordKey? Key;
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
using Content.Server.StationRecords.Systems;
|
||||
|
||||
namespace Content.Server.StationRecords;
|
||||
|
||||
[Access(typeof(StationRecordsSystem))]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Server.Access.Systems;
|
||||
using System.Linq;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Server.StationRecords;
|
||||
using Content.Server.StationRecords.Systems;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.PDA;
|
||||
|
|
@ -13,6 +11,8 @@ using Content.Shared.StationRecords;
|
|||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.StationRecords.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Station records.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using Content.Shared.Verbs;
|
|||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Sticky.Systems;
|
||||
|
||||
|
|
@ -57,7 +58,7 @@ public sealed class StickySystem : EntitySystem
|
|||
{
|
||||
DoContactInteraction = true,
|
||||
Text = Loc.GetString("comp-sticky-unstick-verb-text"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/eject.svg.192dpi.png")),
|
||||
Act = () => StartUnsticking(uid, args.User, component)
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,97 +1,12 @@
|
|||
using Content.Server.Atmos;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Server.Storage.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class EntityStorageComponent : Component, IGasMixtureHolder
|
||||
[RegisterComponent, ComponentReference(typeof(SharedEntityStorageComponent))]
|
||||
public sealed class EntityStorageComponent : SharedEntityStorageComponent, IGasMixtureHolder
|
||||
{
|
||||
public readonly float MaxSize = 1.0f; // maximum width or height of an entity allowed inside the storage.
|
||||
public const float GasMixVolume = 70f;
|
||||
|
||||
public static readonly TimeSpan InternalOpenAttemptDelay = TimeSpan.FromSeconds(0.5);
|
||||
public TimeSpan LastInternalOpenAttempt;
|
||||
|
||||
/// <summary>
|
||||
/// Collision masks that get removed when the storage gets opened.
|
||||
/// </summary>
|
||||
public readonly int MasksToRemove = (int) (
|
||||
CollisionGroup.MidImpassable |
|
||||
CollisionGroup.HighImpassable |
|
||||
CollisionGroup.LowImpassable);
|
||||
|
||||
/// <summary>
|
||||
/// Collision masks that were removed from ANY layer when the storage was opened;
|
||||
/// </summary>
|
||||
[DataField("removedMasks")]
|
||||
public int RemovedMasks;
|
||||
|
||||
[DataField("capacity")]
|
||||
public int Capacity = 30;
|
||||
|
||||
[DataField("isCollidableWhenOpen")]
|
||||
public bool IsCollidableWhenOpen;
|
||||
|
||||
/// <summary>
|
||||
/// If true, it opens the storage when the entity inside of it moves
|
||||
/// If false, it prevents the storage from opening when the entity inside of it moves.
|
||||
/// This is for objects that you want the player to move while inside, like large cardboard boxes, without opening the storage.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("openOnMove")]
|
||||
public bool OpenOnMove = true;
|
||||
|
||||
//The offset for where items are emptied/vacuumed for the EntityStorage.
|
||||
[DataField("enteringOffset")]
|
||||
public Vector2 EnteringOffset = new(0, 0);
|
||||
|
||||
//The collision groups checked, so that items are depositied or grabbed from inside walls.
|
||||
[DataField("enteringOffsetCollisionFlags")]
|
||||
public readonly CollisionGroup EnteringOffsetCollisionFlags = CollisionGroup.Impassable | CollisionGroup.MidImpassable;
|
||||
|
||||
[DataField("enteringRange")]
|
||||
public float EnteringRange = 0.18f;
|
||||
|
||||
[DataField("showContents")]
|
||||
public bool ShowContents;
|
||||
|
||||
[DataField("occludesLight")]
|
||||
public bool OccludesLight = true;
|
||||
|
||||
[DataField("deleteContentsOnDestruction"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool DeleteContentsOnDestruction = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the container is sealed and traps air inside of it
|
||||
/// </summary>
|
||||
[DataField("airtight"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Airtight = true;
|
||||
|
||||
[DataField("open")]
|
||||
public bool Open;
|
||||
|
||||
[DataField("closeSound")]
|
||||
public SoundSpecifier CloseSound = new SoundPathSpecifier("/Audio/Effects/closetclose.ogg");
|
||||
|
||||
[DataField("openSound")]
|
||||
public SoundSpecifier OpenSound = new SoundPathSpecifier("/Audio/Effects/closetopen.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// Whitelist for what entities are allowed to be inserted into this container. If this is not null, the
|
||||
/// standard requirement that the entity must be an item or mob is waived.
|
||||
/// </summary>
|
||||
[DataField("whitelist")]
|
||||
public EntityWhitelist? Whitelist;
|
||||
|
||||
[ViewVariables]
|
||||
public Container Contents = default!;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool IsWeldedShut;
|
||||
|
||||
/// <summary>
|
||||
/// Gas currently contained in this entity storage.
|
||||
/// None while open. Grabs gas from the atmosphere when closed, and exposes any entities inside to it.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ namespace Content.Server.Storage.Components
|
|||
/// <summary>
|
||||
/// The list of entities to spawn, with amounts and orGroups.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DataField("items", required: true)]
|
||||
public List<EntitySpawnEntry> Items = new();
|
||||
|
||||
|
|
@ -25,7 +24,6 @@ namespace Content.Server.Storage.Components
|
|||
/// <summary>
|
||||
/// How many uses before the item should delete itself.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DataField("uses")]
|
||||
public int Uses = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using Content.Shared.Placeable;
|
|||
using Content.Shared.Storage;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Storage.EntitySystems
|
||||
{
|
||||
|
|
@ -54,7 +55,7 @@ namespace Content.Server.Storage.EntitySystems
|
|||
StartDoAfter(uid, null, args.User, dumpable);//Had multiplier of 0.6f
|
||||
},
|
||||
Text = Loc.GetString("dump-verb-name"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/drop.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/drop.svg.192dpi.png")),
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,93 +1,50 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Construction;
|
||||
using Content.Server.Construction.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Storage.Components;
|
||||
using Content.Server.Tools.Systems;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Lock;
|
||||
using Content.Shared.Placeable;
|
||||
using Content.Shared.Storage;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Content.Shared.Wall;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Server.Containers;
|
||||
using Content.Shared.Storage.EntitySystems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
|
||||
namespace Content.Server.Storage.EntitySystems;
|
||||
|
||||
public sealed class EntityStorageSystem : EntitySystem
|
||||
public sealed class EntityStorageSystem : SharedEntityStorageSystem
|
||||
{
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly ConstructionSystem _construction = default!;
|
||||
[Dependency] private readonly ContainerSystem _container = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||
[Dependency] private readonly PlaceableSurfaceSystem _placeableSurface = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmos = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
|
||||
public const string ContainerName = "entity_storage";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<EntityStorageComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<EntityStorageComponent, ActivateInWorldEvent>(OnInteract);
|
||||
SubscribeLocalEvent<EntityStorageComponent, WeldableAttemptEvent>(OnWeldableAttempt);
|
||||
SubscribeLocalEvent<EntityStorageComponent, WeldableChangedEvent>(OnWelded);
|
||||
SubscribeLocalEvent<EntityStorageComponent, LockToggleAttemptEvent>(OnLockToggleAttempt);
|
||||
SubscribeLocalEvent<EntityStorageComponent, DestructionEventArgs>(OnDestruction);
|
||||
|
||||
SubscribeLocalEvent<InsideEntityStorageComponent, EntGotRemovedFromContainerMessage>(OnRemoved);
|
||||
SubscribeLocalEvent<InsideEntityStorageComponent, InhaleLocationEvent>(OnInsideInhale);
|
||||
SubscribeLocalEvent<InsideEntityStorageComponent, ExhaleLocationEvent>(OnInsideExhale);
|
||||
SubscribeLocalEvent<InsideEntityStorageComponent, AtmosExposedGetAirEvent>(OnInsideExposed);
|
||||
|
||||
SubscribeLocalEvent<InsideEntityStorageComponent, EntGotRemovedFromContainerMessage>(OnRemoved);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, EntityStorageComponent component, ComponentInit args)
|
||||
protected override void OnInit(EntityUid uid, SharedEntityStorageComponent component, ComponentInit args)
|
||||
{
|
||||
component.Contents = _container.EnsureContainer<Container>(uid, ContainerName);
|
||||
component.Contents.ShowContents = component.ShowContents;
|
||||
component.Contents.OccludesLight = component.OccludesLight;
|
||||
base.OnInit(uid, component, args);
|
||||
|
||||
if (TryComp<ConstructionComponent>(uid, out var construction))
|
||||
_construction.AddContainer(uid, ContainerName, construction);
|
||||
|
||||
if (TryComp<PlaceableSurfaceComponent>(uid, out var placeable))
|
||||
_placeableSurface.SetPlaceable(uid, component.Open, placeable);
|
||||
|
||||
if (!component.Open)
|
||||
{
|
||||
// If we're closed on spawn, we need to pull some air into our environment from where we spawned,
|
||||
// so that we have -something-. For example, if you bought an animal crate or something.
|
||||
TakeGas(uid, component);
|
||||
TakeGas(uid, (EntityStorageComponent) component);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInteract(EntityUid uid, EntityStorageComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
ToggleOpen(args.User, uid, component);
|
||||
}
|
||||
|
||||
private void OnWeldableAttempt(EntityUid uid, EntityStorageComponent component, WeldableAttemptEvent args)
|
||||
{
|
||||
if (component.Open)
|
||||
|
|
@ -99,7 +56,7 @@ public sealed class EntityStorageSystem : EntitySystem
|
|||
if (component.Contents.Contains(args.User))
|
||||
{
|
||||
var msg = Loc.GetString("entity-storage-component-already-contains-user-message");
|
||||
_popupSystem.PopupEntity(msg, args.User, args.User);
|
||||
Popup.PopupEntity(msg, args.User, args.User);
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
|
@ -107,334 +64,36 @@ public sealed class EntityStorageSystem : EntitySystem
|
|||
private void OnWelded(EntityUid uid, EntityStorageComponent component, WeldableChangedEvent args)
|
||||
{
|
||||
component.IsWeldedShut = args.IsWelded;
|
||||
Dirty(component);
|
||||
}
|
||||
|
||||
private void OnLockToggleAttempt(EntityUid uid, EntityStorageComponent target, ref LockToggleAttemptEvent args)
|
||||
{
|
||||
// Cannot (un)lock open lockers.
|
||||
if (target.Open)
|
||||
args.Cancelled = true;
|
||||
|
||||
// Cannot (un)lock from the inside. Maybe a bad idea? Security jocks could trap nerds in lockers?
|
||||
if (target.Contents.Contains(args.User))
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnDestruction(EntityUid uid, EntityStorageComponent component, DestructionEventArgs args)
|
||||
{
|
||||
component.Open = true;
|
||||
if (!component.DeleteContentsOnDestruction)
|
||||
{
|
||||
EmptyContents(uid, component);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var ent in new List<EntityUid>(component.Contents.ContainedEntities))
|
||||
{
|
||||
EntityManager.DeleteEntity(ent);
|
||||
}
|
||||
}
|
||||
|
||||
public void ToggleOpen(EntityUid user, EntityUid target, EntityStorageComponent? component = null)
|
||||
{
|
||||
if (!Resolve(target, ref component))
|
||||
return;
|
||||
|
||||
if (component.Open)
|
||||
{
|
||||
TryCloseStorage(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
TryOpenStorage(user, target);
|
||||
}
|
||||
}
|
||||
|
||||
public void EmptyContents(EntityUid uid, EntityStorageComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
var uidXform = Transform(uid);
|
||||
var containedArr = component.Contents.ContainedEntities.ToArray();
|
||||
foreach (var contained in containedArr)
|
||||
{
|
||||
Remove(contained, uid, component, uidXform);
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenStorage(EntityUid uid, EntityStorageComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
var beforeev = new StorageBeforeOpenEvent();
|
||||
RaiseLocalEvent(uid, ref beforeev);
|
||||
component.Open = true;
|
||||
EmptyContents(uid, component);
|
||||
ModifyComponents(uid, component);
|
||||
_audio.PlayPvs(component.OpenSound, uid);
|
||||
ReleaseGas(uid, component);
|
||||
var afterev = new StorageAfterOpenEvent();
|
||||
RaiseLocalEvent(uid, ref afterev);
|
||||
}
|
||||
|
||||
public void CloseStorage(EntityUid uid, EntityStorageComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
component.Open = false;
|
||||
|
||||
var targetCoordinates = new EntityCoordinates(uid, component.EnteringOffset);
|
||||
|
||||
var entities = _lookup.GetEntitiesInRange(targetCoordinates, component.EnteringRange, LookupFlags.Approximate | LookupFlags.Dynamic | LookupFlags.Sundries);
|
||||
|
||||
var ev = new StorageBeforeCloseEvent(entities, new());
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
var count = 0;
|
||||
foreach (var entity in ev.Contents)
|
||||
{
|
||||
if (!ev.BypassChecks.Contains(entity))
|
||||
{
|
||||
if (!CanFit(entity, uid, component.Whitelist))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!AddToContents(entity, uid, component))
|
||||
continue;
|
||||
|
||||
count++;
|
||||
if (count >= component.Capacity)
|
||||
break;
|
||||
}
|
||||
|
||||
TakeGas(uid, component);
|
||||
ModifyComponents(uid, component);
|
||||
_audio.PlayPvs(component.CloseSound, uid);
|
||||
component.LastInternalOpenAttempt = default;
|
||||
var afterev = new StorageAfterCloseEvent();
|
||||
RaiseLocalEvent(uid, ref afterev);
|
||||
}
|
||||
|
||||
public bool Insert(EntityUid toInsert, EntityUid container, EntityStorageComponent? component = null)
|
||||
{
|
||||
if (!Resolve(container, ref component))
|
||||
return false;
|
||||
|
||||
if (component.Open)
|
||||
{
|
||||
Transform(toInsert).WorldPosition = Transform(container).WorldPosition;
|
||||
return true;
|
||||
}
|
||||
|
||||
var inside = EnsureComp<InsideEntityStorageComponent>(toInsert);
|
||||
inside.Storage = container;
|
||||
return component.Contents.Insert(toInsert, EntityManager);
|
||||
}
|
||||
|
||||
public bool Remove(EntityUid toRemove, EntityUid container, EntityStorageComponent? component = null, TransformComponent? xform = null)
|
||||
{
|
||||
if (!Resolve(container, ref component, ref xform, false))
|
||||
return false;
|
||||
|
||||
RemComp<InsideEntityStorageComponent>(toRemove);
|
||||
component.Contents.Remove(toRemove, EntityManager);
|
||||
Transform(toRemove).WorldPosition = xform.WorldPosition + xform.WorldRotation.RotateVec(component.EnteringOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanInsert(EntityUid container, EntityStorageComponent? component = null)
|
||||
{
|
||||
if (!Resolve(container, ref component))
|
||||
return false;
|
||||
|
||||
if (component.Open)
|
||||
return true;
|
||||
|
||||
if (component.Contents.ContainedEntities.Count >= component.Capacity)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryOpenStorage(EntityUid user, EntityUid target, bool silent = false)
|
||||
{
|
||||
if (!CanOpen(user, target, silent))
|
||||
return false;
|
||||
|
||||
OpenStorage(target);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryCloseStorage(EntityUid target)
|
||||
{
|
||||
if (!CanClose(target))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CloseStorage(target);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanOpen(EntityUid user, EntityUid target, bool silent = false, EntityStorageComponent? component = null)
|
||||
{
|
||||
if (!Resolve(target, ref component))
|
||||
return false;
|
||||
|
||||
if (!HasComp<SharedHandsComponent>(user))
|
||||
return false;
|
||||
|
||||
if (component.IsWeldedShut)
|
||||
{
|
||||
if (!silent && !component.Contents.Contains(user))
|
||||
_popupSystem.PopupEntity(Loc.GetString("entity-storage-component-welded-shut-message"), target);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//Checks to see if the opening position, if offset, is inside of a wall.
|
||||
if (component.EnteringOffset != (0, 0) && !HasComp<WallMountComponent>(target)) //if the entering position is offset
|
||||
{
|
||||
var newCoords = new EntityCoordinates(target, component.EnteringOffset);
|
||||
if (!_interactionSystem.InRangeUnobstructed(target, newCoords, 0, collisionMask: component.EnteringOffsetCollisionFlags))
|
||||
{
|
||||
if (!silent)
|
||||
_popupSystem.PopupEntity(Loc.GetString("entity-storage-component-cannot-open-no-space"), target);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var ev = new StorageOpenAttemptEvent(silent);
|
||||
RaiseLocalEvent(target, ref ev, true);
|
||||
|
||||
return !ev.Cancelled;
|
||||
}
|
||||
|
||||
public bool CanClose(EntityUid target, bool silent = false)
|
||||
{
|
||||
var ev = new StorageCloseAttemptEvent();
|
||||
RaiseLocalEvent(target, ref ev, silent);
|
||||
|
||||
return !ev.Cancelled;
|
||||
}
|
||||
|
||||
public bool AddToContents(EntityUid toAdd, EntityUid container, EntityStorageComponent? component = null)
|
||||
{
|
||||
if (!Resolve(container, ref component))
|
||||
return false;
|
||||
|
||||
if (toAdd == container)
|
||||
return false;
|
||||
|
||||
if (TryComp<PhysicsComponent>(toAdd, out var phys))
|
||||
{
|
||||
var aabb = _physics.GetWorldAABB(toAdd, body: phys);
|
||||
|
||||
if (component.MaxSize < aabb.Size.X || component.MaxSize < aabb.Size.Y)
|
||||
return false;
|
||||
}
|
||||
|
||||
return Insert(toAdd, container, component);
|
||||
}
|
||||
|
||||
public bool CanFit(EntityUid toInsert, EntityUid container, EntityWhitelist? whitelist)
|
||||
{
|
||||
// conditions are complicated because of pizzabox-related issues, so follow this guide
|
||||
// 0. Accomplish your goals at all costs.
|
||||
// 1. AddToContents can block anything
|
||||
// 2. maximum item count can block anything
|
||||
// 3. ghosts can NEVER be eaten
|
||||
// 4. items can always be eaten unless a previous law prevents it
|
||||
// 5. if this is NOT AN ITEM, then mobs can always be eaten unless a previous
|
||||
// law prevents it
|
||||
// 6. if this is an item, then mobs must only be eaten if some other component prevents
|
||||
// pick-up interactions while a mob is inside (e.g. foldable)
|
||||
var attemptEvent = new InsertIntoEntityStorageAttemptEvent();
|
||||
RaiseLocalEvent(toInsert, ref attemptEvent);
|
||||
if (attemptEvent.Cancelled)
|
||||
return false;
|
||||
|
||||
var targetIsMob = HasComp<BodyComponent>(toInsert);
|
||||
var storageIsItem = HasComp<ItemComponent>(container);
|
||||
var allowedToEat = whitelist?.IsValid(toInsert) ?? HasComp<ItemComponent>(toInsert);
|
||||
|
||||
// BEFORE REPLACING THIS WITH, I.E. A PROPERTY:
|
||||
// Make absolutely 100% sure you have worked out how to stop people ending up in backpacks.
|
||||
// Seriously, it is insanely hacky and weird to get someone out of a backpack once they end up in there.
|
||||
// And to be clear, they should NOT be in there.
|
||||
// For the record, what you need to do is empty the backpack onto a PlacableSurface (table, rack)
|
||||
if (targetIsMob)
|
||||
{
|
||||
if (!storageIsItem)
|
||||
allowedToEat = true;
|
||||
else
|
||||
{
|
||||
var storeEv = new StoreMobInItemContainerAttemptEvent();
|
||||
RaiseLocalEvent(container, ref storeEv);
|
||||
allowedToEat = storeEv.Handled && !storeEv.Cancelled;
|
||||
}
|
||||
}
|
||||
|
||||
return allowedToEat;
|
||||
}
|
||||
|
||||
public void ModifyComponents(EntityUid uid, EntityStorageComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
if (!component.IsCollidableWhenOpen && TryComp<FixturesComponent>(uid, out var fixtures) && fixtures.Fixtures.Count > 0)
|
||||
{
|
||||
// currently only works for single-fixture entities. If they have more than one fixture, then
|
||||
// RemovedMasks needs to be tracked separately for each fixture, using a fixture Id Dictionary. Also the
|
||||
// fixture IDs probably cant be automatically generated without causing issues, unless there is some
|
||||
// guarantee that they will get deserialized with the same auto-generated ID when saving+loading the map.
|
||||
var fixture = fixtures.Fixtures.Values.First();
|
||||
|
||||
if (component.Open)
|
||||
{
|
||||
component.RemovedMasks = fixture.CollisionLayer & component.MasksToRemove;
|
||||
_physics.SetCollisionLayer(uid, fixture, fixture.CollisionLayer & ~component.MasksToRemove, manager: fixtures);
|
||||
}
|
||||
else
|
||||
{
|
||||
_physics.SetCollisionLayer(uid, fixture, fixture.CollisionLayer | component.RemovedMasks, manager: fixtures);
|
||||
component.RemovedMasks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<PlaceableSurfaceComponent>(uid, out var surface))
|
||||
_placeableSurface.SetPlaceable(uid, component.Open, surface);
|
||||
|
||||
_appearance.SetData(uid, StorageVisuals.Open, component.Open);
|
||||
_appearance.SetData(uid, StorageVisuals.HasContents, component.Contents.ContainedEntities.Count > 0);
|
||||
}
|
||||
|
||||
private void TakeGas(EntityUid uid, EntityStorageComponent component)
|
||||
protected override void TakeGas(EntityUid uid, SharedEntityStorageComponent component)
|
||||
{
|
||||
if (!component.Airtight)
|
||||
return;
|
||||
|
||||
var tile = GetOffsetTileRef(uid, component);
|
||||
var serverComp = (EntityStorageComponent) component;
|
||||
var tile = GetOffsetTileRef(uid, serverComp);
|
||||
|
||||
if (tile != null && _atmos.GetTileMixture(tile.Value.GridUid, null, tile.Value.GridIndices, true) is {} environment)
|
||||
{
|
||||
_atmos.Merge(component.Air, environment.RemoveVolume(EntityStorageComponent.GasMixVolume));
|
||||
_atmos.Merge(serverComp.Air, environment.RemoveVolume(SharedEntityStorageComponent.GasMixVolume));
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseGas(EntityUid uid, EntityStorageComponent component)
|
||||
public override void ReleaseGas(EntityUid uid, SharedEntityStorageComponent component)
|
||||
{
|
||||
if (!component.Airtight)
|
||||
var serverComp = (EntityStorageComponent) component;
|
||||
|
||||
if (!serverComp.Airtight)
|
||||
return;
|
||||
|
||||
var tile = GetOffsetTileRef(uid, component);
|
||||
var tile = GetOffsetTileRef(uid, serverComp);
|
||||
|
||||
if (tile != null && _atmos.GetTileMixture(tile.Value.GridUid, null, tile.Value.GridIndices, true) is {} environment)
|
||||
{
|
||||
_atmos.Merge(environment, component.Air);
|
||||
component.Air.Clear();
|
||||
_atmos.Merge(environment, serverComp.Air);
|
||||
serverComp.Air.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Cargo.Systems;
|
||||
using Content.Server.Storage.Components;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Storage;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using static Content.Shared.Storage.EntitySpawnCollection;
|
||||
|
||||
namespace Content.Server.Storage.EntitySystems
|
||||
{
|
||||
|
|
@ -14,13 +17,47 @@ namespace Content.Server.Storage.EntitySystems
|
|||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly PricingSystem _pricing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SpawnItemsOnUseComponent, UseInHandEvent>(OnUseInHand);
|
||||
SubscribeLocalEvent<SpawnItemsOnUseComponent, PriceCalculationEvent>(CalculatePrice, before: new[] { typeof(PricingSystem) });
|
||||
}
|
||||
|
||||
private void CalculatePrice(EntityUid uid, SpawnItemsOnUseComponent component, ref PriceCalculationEvent args)
|
||||
{
|
||||
var ungrouped = CollectOrGroups(component.Items, out var orGroups);
|
||||
|
||||
foreach (var entry in ungrouped)
|
||||
{
|
||||
var protUid = Spawn(entry.PrototypeId, MapCoordinates.Nullspace);
|
||||
|
||||
// Calculate the average price of the possible spawned items
|
||||
args.Price += _pricing.GetPrice(protUid) * entry.SpawnProbability * entry.GetAmount(getAverage: true);
|
||||
|
||||
EntityManager.DeleteEntity(protUid);
|
||||
}
|
||||
|
||||
foreach (var group in orGroups)
|
||||
{
|
||||
foreach (var entry in group.Entries)
|
||||
{
|
||||
var protUid = Spawn(entry.PrototypeId, MapCoordinates.Nullspace);
|
||||
|
||||
// Calculate the average price of the possible spawned items
|
||||
args.Price += _pricing.GetPrice(protUid) *
|
||||
(entry.SpawnProbability / group.CumulativeProbability) *
|
||||
entry.GetAmount(getAverage: true);
|
||||
|
||||
EntityManager.DeleteEntity(protUid);
|
||||
}
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnUseInHand(EntityUid uid, SpawnItemsOnUseComponent component, UseInHandEvent args)
|
||||
|
|
@ -29,7 +66,7 @@ namespace Content.Server.Storage.EntitySystems
|
|||
return;
|
||||
|
||||
var coords = Transform(args.User).Coordinates;
|
||||
var spawnEntities = EntitySpawnCollection.GetSpawns(component.Items, _random);
|
||||
var spawnEntities = GetSpawns(component.Items, _random);
|
||||
EntityUid? entityToPlaceInHands = null;
|
||||
|
||||
foreach (var proto in spawnEntities)
|
||||
|
|
@ -50,7 +87,7 @@ namespace Content.Server.Storage.EntitySystems
|
|||
|
||||
if (entityToPlaceInHands != null)
|
||||
{
|
||||
_handsSystem.PickupOrDrop(args.User, entityToPlaceInHands.Value);
|
||||
_hands.PickupOrDrop(args.User, entityToPlaceInHands.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,9 +77,6 @@ namespace Content.Server.Storage.EntitySystems
|
|||
|
||||
SubscribeLocalEvent<ServerStorageComponent, DoAfterEvent<StorageData>>(OnDoAfter);
|
||||
|
||||
SubscribeLocalEvent<EntityStorageComponent, GetVerbsEvent<InteractionVerb>>(AddToggleOpenVerb);
|
||||
SubscribeLocalEvent<EntityStorageComponent, ContainerRelayMovementEntityEvent>(OnRelayMovement);
|
||||
|
||||
SubscribeLocalEvent<StorageFillComponent, MapInitEvent>(OnStorageFillMapInit);
|
||||
}
|
||||
|
||||
|
|
@ -95,39 +92,6 @@ namespace Content.Server.Storage.EntitySystems
|
|||
UpdateStorageUI(uid, storageComp);
|
||||
}
|
||||
|
||||
private void OnRelayMovement(EntityUid uid, EntityStorageComponent component, ref ContainerRelayMovementEntityEvent args)
|
||||
{
|
||||
if (!EntityManager.HasComponent<HandsComponent>(args.Entity) || _gameTiming.CurTime < component.LastInternalOpenAttempt + EntityStorageComponent.InternalOpenAttemptDelay)
|
||||
return;
|
||||
|
||||
component.LastInternalOpenAttempt = _gameTiming.CurTime;
|
||||
if (component.OpenOnMove)
|
||||
{
|
||||
_entityStorage.TryOpenStorage(args.Entity, component.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void AddToggleOpenVerb(EntityUid uid, EntityStorageComponent component, GetVerbsEvent<InteractionVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || !_entityStorage.CanOpen(args.User, args.Target, silent: true, component))
|
||||
return;
|
||||
|
||||
InteractionVerb verb = new();
|
||||
if (component.Open)
|
||||
{
|
||||
verb.Text = Loc.GetString("verb-common-close");
|
||||
verb.IconTexture = "/Textures/Interface/VerbIcons/close.svg.192dpi.png";
|
||||
}
|
||||
else
|
||||
{
|
||||
verb.Text = Loc.GetString("verb-common-open");
|
||||
verb.IconTexture = "/Textures/Interface/VerbIcons/open.svg.192dpi.png";
|
||||
}
|
||||
verb.Act = () => _entityStorage.ToggleOpen(args.User, args.Target, component);
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private void AddOpenUiVerb(EntityUid uid, ServerStorageComponent component, GetVerbsEvent<ActivationVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || TryComp<LockComponent>(uid, out var lockComponent) && lockComponent.Locked)
|
||||
|
|
@ -147,12 +111,14 @@ namespace Content.Server.Storage.EntitySystems
|
|||
if (uiOpen)
|
||||
{
|
||||
verb.Text = Loc.GetString("verb-common-close-ui");
|
||||
verb.IconTexture = "/Textures/Interface/VerbIcons/close.svg.192dpi.png";
|
||||
verb.Icon = new SpriteSpecifier.Texture(
|
||||
new ResourcePath("/Textures/Interface/VerbIcons/close.svg.192dpi.png"));
|
||||
}
|
||||
else
|
||||
{
|
||||
verb.Text = Loc.GetString("verb-common-open-ui");
|
||||
verb.IconTexture = "/Textures/Interface/VerbIcons/open.svg.192dpi.png";
|
||||
verb.Icon = new SpriteSpecifier.Texture(
|
||||
new ResourcePath("/Textures/Interface/VerbIcons/open.svg.192dpi.png"));
|
||||
}
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ using Content.Shared.DoAfter;
|
|||
using Content.Shared.Ensnaring.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Strip;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Strip
|
||||
{
|
||||
|
|
@ -132,7 +133,7 @@ namespace Content.Server.Strip
|
|||
Verb verb = new()
|
||||
{
|
||||
Text = Loc.GetString("strip-verb-get-data-text"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/outfit.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/outfit.svg.192dpi.png")),
|
||||
Act = () => StartOpeningStripper(args.User, component, true),
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
|
|
@ -149,7 +150,7 @@ namespace Content.Server.Strip
|
|||
ExamineVerb verb = new()
|
||||
{
|
||||
Text = Loc.GetString("strip-verb-get-data-text"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/outfit.svg.192dpi.png",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/outfit.svg.192dpi.png")),
|
||||
Act = () => StartOpeningStripper(args.User, component, true),
|
||||
Category = VerbCategory.Examine,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using Robust.Server.GameObjects;
|
|||
using Robust.Server.Player;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Tabletop
|
||||
{
|
||||
|
|
@ -58,7 +59,7 @@ namespace Content.Server.Tabletop
|
|||
|
||||
ActivationVerb verb = new();
|
||||
verb.Text = Loc.GetString("tabletop-verb-play-game");
|
||||
verb.IconTexture = "/Textures/Interface/VerbIcons/die.svg.192dpi.png";
|
||||
verb.Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/die.svg.192dpi.png"));
|
||||
verb.Act = () => OpenSessionFor(actor.PlayerSession, uid);
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,5 +15,8 @@ namespace Content.Server.Tools.Components
|
|||
|
||||
[DataField("delay")]
|
||||
public float Delay = 1f;
|
||||
|
||||
[DataField("cancelToken")]
|
||||
public CancellationTokenSource? CancelToken;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,20 @@ public sealed partial class ToolSystem
|
|||
{
|
||||
SubscribeLocalEvent<TilePryingComponent, AfterInteractEvent>(OnTilePryingAfterInteract);
|
||||
SubscribeLocalEvent<TilePryingComponent, TilePryingCompleteEvent>(OnTilePryComplete);
|
||||
SubscribeLocalEvent<TilePryingComponent, TilePryingCancelledEvent>(OnTilePryCancelled);
|
||||
}
|
||||
|
||||
private void OnTilePryingAfterInteract(EntityUid uid, TilePryingComponent component, AfterInteractEvent args)
|
||||
{
|
||||
if (args.Handled || !args.CanReach || (args.Target != null && !HasComp<PuddleComponent>(args.Target))) return;
|
||||
|
||||
if (TryPryTile(uid, args.User, component, args.ClickLocation))
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnTilePryComplete(EntityUid uid, TilePryingComponent component, TilePryingCompleteEvent args)
|
||||
{
|
||||
component.CancelToken = null;
|
||||
var gridUid = args.Coordinates.GetGridUid(EntityManager);
|
||||
if (!_mapManager.TryGetGrid(gridUid, out var grid))
|
||||
{
|
||||
|
|
@ -34,17 +44,14 @@ public sealed partial class ToolSystem
|
|||
_tile.PryTile(tile);
|
||||
}
|
||||
|
||||
private void OnTilePryingAfterInteract(EntityUid uid, TilePryingComponent component, AfterInteractEvent args)
|
||||
private void OnTilePryCancelled(EntityUid uid, TilePryingComponent component, TilePryingCancelledEvent args)
|
||||
{
|
||||
if (args.Handled || !args.CanReach || (args.Target != null && !HasComp<PuddleComponent>(args.Target))) return;
|
||||
|
||||
if (TryPryTile(uid, args.User, component, args.ClickLocation))
|
||||
args.Handled = true;
|
||||
component.CancelToken = null;
|
||||
}
|
||||
|
||||
private bool TryPryTile(EntityUid toolEntity, EntityUid user, TilePryingComponent component, EntityCoordinates clickLocation)
|
||||
{
|
||||
if (!TryComp<ToolComponent?>(toolEntity, out var tool) && component.ToolComponentNeeded)
|
||||
if (!TryComp<ToolComponent?>(toolEntity, out var tool) && component.ToolComponentNeeded || component.CancelToken != null)
|
||||
return false;
|
||||
|
||||
if (!_mapManager.TryGetGrid(clickLocation.GetGridUid(EntityManager), out var mapGrid))
|
||||
|
|
@ -62,9 +69,11 @@ public sealed partial class ToolSystem
|
|||
if (!tileDef.CanCrowbar)
|
||||
return false;
|
||||
|
||||
var toolEvData = new ToolEventData(new TilePryingCompleteEvent(clickLocation), targetEntity:toolEntity);
|
||||
component.CancelToken = new CancellationTokenSource();
|
||||
|
||||
if (!UseTool(toolEntity, user, null, component.Delay, new[] { component.QualityNeeded }, toolEvData, toolComponent: tool))
|
||||
var toolEvData = new ToolEventData(new TilePryingCompleteEvent(clickLocation), cancelledEv:new TilePryingCancelledEvent() ,targetEntity:toolEntity);
|
||||
|
||||
if (!UseTool(toolEntity, user, null, component.Delay, new[] { component.QualityNeeded }, toolEvData, toolComponent: tool, cancelToken: component.CancelToken))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ using Robust.Shared.Map;
|
|||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Weapons.Melee;
|
||||
|
||||
|
|
@ -74,7 +75,7 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
|||
Text = Loc.GetString("damage-examinable-verb-text"),
|
||||
Message = Loc.GetString("damage-examinable-verb-message"),
|
||||
Category = VerbCategory.Examine,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/smite.svg.192dpi.png"
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/smite.svg.192dpi.png")),
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Content.Shared.Verbs;
|
|||
using Content.Shared.Weapons.Ranged;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Weapons.Ranged.Systems;
|
||||
|
||||
|
|
@ -38,11 +39,13 @@ public sealed partial class GunSystem
|
|||
|
||||
private void UpdateShots(EntityUid uid, BatteryAmmoProviderComponent component)
|
||||
{
|
||||
if (!TryComp<BatteryComponent>(uid, out var battery)) return;
|
||||
UpdateShots(component, battery);
|
||||
if (!TryComp<BatteryComponent>(uid, out var battery))
|
||||
return;
|
||||
|
||||
UpdateShots(uid, component, battery);
|
||||
}
|
||||
|
||||
private void UpdateShots(BatteryAmmoProviderComponent component, BatteryComponent battery)
|
||||
private void UpdateShots(EntityUid uid, BatteryAmmoProviderComponent component, BatteryComponent battery)
|
||||
{
|
||||
var shots = (int) (battery.CurrentCharge / component.FireCost);
|
||||
var maxShots = (int) (battery.MaxCharge / component.FireCost);
|
||||
|
|
@ -54,7 +57,7 @@ public sealed partial class GunSystem
|
|||
|
||||
component.Shots = shots;
|
||||
component.Capacity = maxShots;
|
||||
UpdateBatteryAppearance(component.Owner, component);
|
||||
UpdateBatteryAppearance(uid, component);
|
||||
}
|
||||
|
||||
private void OnBatteryExaminableVerb(EntityUid uid, BatteryAmmoProviderComponent component, GetVerbsEvent<ExamineVerb> args)
|
||||
|
|
@ -91,7 +94,7 @@ public sealed partial class GunSystem
|
|||
Text = Loc.GetString("damage-examinable-verb-text"),
|
||||
Message = Loc.GetString("damage-examinable-verb-message"),
|
||||
Category = VerbCategory.Examine,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/smite.svg.192dpi.png"
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/smite.svg.192dpi.png")),
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
|
|
@ -125,9 +128,10 @@ public sealed partial class GunSystem
|
|||
|
||||
protected override void TakeCharge(EntityUid uid, BatteryAmmoProviderComponent component)
|
||||
{
|
||||
if (!TryComp<BatteryComponent>(uid, out var battery)) return;
|
||||
if (!TryComp<BatteryComponent>(uid, out var battery))
|
||||
return;
|
||||
|
||||
battery.CurrentCharge -= component.FireCost;
|
||||
UpdateShots(component, battery);
|
||||
UpdateShots(uid, component, battery);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Content.Shared.Projectiles;
|
|||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Weapons.Ranged.Systems;
|
||||
|
||||
|
|
@ -37,7 +38,7 @@ public sealed partial class GunSystem
|
|||
Text = Loc.GetString("damage-examinable-verb-text"),
|
||||
Message = Loc.GetString("damage-examinable-verb-message"),
|
||||
Category = VerbCategory.Examine,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/smite.svg.192dpi.png"
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/smite.svg.192dpi.png")),
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
|
|
|
|||
|
|
@ -1,22 +1,52 @@
|
|||
namespace Content.Shared.Access.Components
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
|
||||
|
||||
namespace Content.Shared.Access.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Stores access levels necessary to "use" an entity
|
||||
/// and allows checking if something or somebody is authorized with these access levels.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class AccessReaderComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores access levels necessary to "use" an entity
|
||||
/// and allows checking if something or somebody is authorized with these access levels.
|
||||
/// The set of tags that will automatically deny an allowed check, if any of them are present.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class AccessReaderComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The set of tags that will automatically deny an allowed check, if any of them are present.
|
||||
/// </summary>
|
||||
public HashSet<string> DenyTags = new();
|
||||
[DataField("denyTags", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<AccessLevelPrototype>))]
|
||||
public HashSet<string> DenyTags = new();
|
||||
|
||||
/// <summary>
|
||||
/// List of access lists to check allowed against. For an access check to pass
|
||||
/// there has to be an access list that is a subset of the access in the checking list.
|
||||
/// </summary>
|
||||
[DataField("access")]
|
||||
public List<HashSet<string>> AccessLists = new();
|
||||
/// <summary>
|
||||
/// List of access lists to check allowed against. For an access check to pass
|
||||
/// there has to be an access list that is a subset of the access in the checking list.
|
||||
/// </summary>
|
||||
[DataField("access")]
|
||||
public List<HashSet<string>> AccessLists = new();
|
||||
|
||||
/// <summary>
|
||||
/// A list of valid stationrecordkeys
|
||||
/// </summary>
|
||||
[DataField("accessKeys")]
|
||||
public HashSet<StationRecordKey> AccessKeys = new();
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class AccessReaderComponentState : ComponentState
|
||||
{
|
||||
public HashSet<string> DenyTags;
|
||||
|
||||
public List<HashSet<string>> AccessLists;
|
||||
|
||||
public HashSet<StationRecordKey> AccessKeys;
|
||||
|
||||
public AccessReaderComponentState(HashSet<string> denyTags, List<HashSet<string>> accessLists, HashSet<StationRecordKey> accessKeys)
|
||||
{
|
||||
DenyTags = denyTags;
|
||||
AccessLists = accessLists;
|
||||
AccessKeys = accessKeys;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ using Content.Shared.Access.Components;
|
|||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.MachineLinking.Events;
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Access.Systems
|
||||
{
|
||||
|
|
@ -23,6 +25,24 @@ namespace Content.Shared.Access.Systems
|
|||
SubscribeLocalEvent<AccessReaderComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<AccessReaderComponent, GotEmaggedEvent>(OnEmagged);
|
||||
SubscribeLocalEvent<AccessReaderComponent, LinkAttemptEvent>(OnLinkAttempt);
|
||||
|
||||
SubscribeLocalEvent<AccessReaderComponent, ComponentGetState>(OnGetState);
|
||||
SubscribeLocalEvent<AccessReaderComponent, ComponentHandleState>(OnHandleState);
|
||||
}
|
||||
|
||||
private void OnGetState(EntityUid uid, AccessReaderComponent component, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new AccessReaderComponentState(component.DenyTags, component.AccessLists,
|
||||
component.AccessKeys);
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, AccessReaderComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not AccessReaderComponentState state)
|
||||
return;
|
||||
component.AccessKeys = new (state.AccessKeys);
|
||||
component.AccessLists = new (state.AccessLists);
|
||||
component.DenyTags = new (state.DenyTags);
|
||||
}
|
||||
|
||||
private void OnLinkAttempt(EntityUid uid, AccessReaderComponent component, LinkAttemptEvent args)
|
||||
|
|
@ -62,8 +82,7 @@ namespace Content.Shared.Access.Systems
|
|||
{
|
||||
if (!Resolve(target, ref reader, false))
|
||||
return true;
|
||||
var tags = FindAccessTags(source);
|
||||
return IsAllowed(tags, reader);
|
||||
return IsAllowed(source, reader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -74,8 +93,15 @@ namespace Content.Shared.Access.Systems
|
|||
/// <param name="reader">A reader from a different entity</param>
|
||||
public bool IsAllowed(EntityUid entity, AccessReaderComponent reader)
|
||||
{
|
||||
var tags = FindAccessTags(entity);
|
||||
return IsAllowed(tags, reader);
|
||||
var allEnts = FindPotentialAccessItems(entity);
|
||||
|
||||
if (AreAccessTagsAllowed(FindAccessTags(entity, allEnts), reader))
|
||||
return true;
|
||||
|
||||
if (AreStationRecordKeysAllowed(FindStationRecordKeys(entity, allEnts), reader))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -83,7 +109,7 @@ namespace Content.Shared.Access.Systems
|
|||
/// </summary>
|
||||
/// <param name="accessTags">A list of access tags</param>
|
||||
/// <param name="reader">An access reader to check against</param>
|
||||
public bool IsAllowed(ICollection<string> accessTags, AccessReaderComponent reader)
|
||||
public bool AreAccessTagsAllowed(ICollection<string> accessTags, AccessReaderComponent reader)
|
||||
{
|
||||
if (HasComp<EmaggedComponent>(reader.Owner))
|
||||
{
|
||||
|
|
@ -105,17 +131,18 @@ namespace Content.Shared.Access.Systems
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the access tags on the given entity
|
||||
/// Compares the given stationrecordkeys with the accessreader to see if it is allowed.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity that is being searched.</param>
|
||||
public ICollection<string> FindAccessTags(EntityUid uid)
|
||||
public bool AreStationRecordKeysAllowed(ICollection<StationRecordKey> keys, AccessReaderComponent reader)
|
||||
{
|
||||
HashSet<string>? tags = null;
|
||||
var owned = false;
|
||||
|
||||
// check entity itself
|
||||
FindAccessTagsItem(uid, ref tags, ref owned);
|
||||
return keys.Any() && reader.AccessKeys.Any(keys.Contains);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds all the items that could potentially give access to a given entity
|
||||
/// </summary>
|
||||
public HashSet<EntityUid> FindPotentialAccessItems(EntityUid uid)
|
||||
{
|
||||
FindAccessItemsInventory(uid, out var items);
|
||||
|
||||
var ev = new GetAdditionalAccessEvent
|
||||
|
|
@ -123,7 +150,23 @@ namespace Content.Shared.Access.Systems
|
|||
Entities = items
|
||||
};
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
foreach (var ent in ev.Entities)
|
||||
items.Add(uid);
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the access tags on the given entity
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity that is being searched.</param>
|
||||
/// <param name="items">All of the items to search for access. If none are passed in, <see cref="FindPotentialAccessItems"/> will be used.</param>
|
||||
public ICollection<string> FindAccessTags(EntityUid uid, HashSet<EntityUid>? items = null)
|
||||
{
|
||||
HashSet<string>? tags = null;
|
||||
var owned = false;
|
||||
|
||||
items ??= FindPotentialAccessItems(uid);
|
||||
|
||||
foreach (var ent in items)
|
||||
{
|
||||
FindAccessTagsItem(ent, ref tags, ref owned);
|
||||
}
|
||||
|
|
@ -131,6 +174,26 @@ namespace Content.Shared.Access.Systems
|
|||
return (ICollection<string>?) tags ?? Array.Empty<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the access tags on the given entity
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity that is being searched.</param>
|
||||
/// <param name="items">All of the items to search for access. If none are passed in, <see cref="FindPotentialAccessItems"/> will be used.</param>
|
||||
public ICollection<StationRecordKey> FindStationRecordKeys(EntityUid uid, HashSet<EntityUid>? items = null)
|
||||
{
|
||||
HashSet<StationRecordKey> keys = new();
|
||||
|
||||
items ??= FindPotentialAccessItems(uid);
|
||||
|
||||
foreach (var ent in items)
|
||||
{
|
||||
if (FindStationRecordKeyItem(ent, out var key))
|
||||
keys.Add(key.Value);
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to find <see cref="AccessComponent"/> on this item
|
||||
/// or inside this item (if it's pda)
|
||||
|
|
@ -203,5 +266,31 @@ namespace Content.Shared.Access.Systems
|
|||
tags = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to find <see cref="StationRecordKeyStorageComponent"/> on this item
|
||||
/// or inside this item (if it's pda)
|
||||
/// </summary>
|
||||
private bool FindStationRecordKeyItem(EntityUid uid, [NotNullWhen(true)] out StationRecordKey? key)
|
||||
{
|
||||
if (EntityManager.TryGetComponent(uid, out StationRecordKeyStorageComponent? storage) && storage.Key != null)
|
||||
{
|
||||
key = storage.Key;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TryComp<PDAComponent>(uid, out var pda) &&
|
||||
pda.ContainedID?.Owner is {Valid: true} id)
|
||||
{
|
||||
if (TryComp<StationRecordKeyStorageComponent>(id, out var pdastorage) && pdastorage.Key != null)
|
||||
{
|
||||
key = pdastorage.Key;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
key = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,8 +67,10 @@ public static class AdminLogsEuiMsg
|
|||
HashSet<LogImpact>? impacts,
|
||||
DateTime? before,
|
||||
DateTime? after,
|
||||
bool includePlayers,
|
||||
Guid[]? anyPlayers,
|
||||
Guid[]? allPlayers,
|
||||
bool includeNonPlayers,
|
||||
int? lastLogId,
|
||||
DateOrder dateOrder)
|
||||
{
|
||||
|
|
@ -78,8 +80,10 @@ public static class AdminLogsEuiMsg
|
|||
Impacts = impacts;
|
||||
Before = before;
|
||||
After = after;
|
||||
IncludePlayers = includePlayers;
|
||||
AnyPlayers = anyPlayers is { Length: > 0 } ? anyPlayers : null;
|
||||
AllPlayers = allPlayers is { Length: > 0 } ? allPlayers : null;
|
||||
IncludeNonPlayers = includeNonPlayers;
|
||||
LastLogId = lastLogId;
|
||||
DateOrder = dateOrder;
|
||||
}
|
||||
|
|
@ -90,8 +94,10 @@ public static class AdminLogsEuiMsg
|
|||
public HashSet<LogImpact>? Impacts { get; set; }
|
||||
public DateTime? Before { get; set; }
|
||||
public DateTime? After { get; set; }
|
||||
public bool IncludePlayers { get; set; }
|
||||
public Guid[]? AnyPlayers { get; set; }
|
||||
public Guid[]? AllPlayers { get; set; }
|
||||
public bool IncludeNonPlayers { get; set; }
|
||||
public int? LastLogId { get; set; }
|
||||
public DateOrder DateOrder { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1214,6 +1214,15 @@ namespace Content.Shared.CCVar
|
|||
public static readonly CVarDef<bool> ChatShowTypingIndicator =
|
||||
CVarDef.Create("chat.show_typing_indicator", true, CVar.CLIENTONLY);
|
||||
|
||||
/// <summary>
|
||||
/// A message broadcast to each player that joins the lobby.
|
||||
/// May be changed by admins ingame through use of the "set-motd" command.
|
||||
/// In this case the new value, if not empty, is broadcast to all connected players and saved between rounds.
|
||||
/// May be requested by any player through use of the "get-motd" command.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<string> MOTD =
|
||||
CVarDef.Create("chat.motd", "", CVar.SERVER | CVar.SERVERONLY | CVar.ARCHIVE, "A message broadcast to each player that joins the lobby.");
|
||||
|
||||
/*
|
||||
* AFK
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Robust.Shared.Audio;
|
|||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Cabinet;
|
||||
|
||||
|
|
@ -105,12 +106,14 @@ public abstract class SharedItemCabinetSystem : EntitySystem
|
|||
if (cabinet.Opened)
|
||||
{
|
||||
toggleVerb.Text = Loc.GetString("verb-common-close");
|
||||
toggleVerb.IconTexture = "/Textures/Interface/VerbIcons/close.svg.192dpi.png";
|
||||
toggleVerb.Icon =
|
||||
new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/close.svg.192dpi.png"));
|
||||
}
|
||||
else
|
||||
{
|
||||
toggleVerb.Text = Loc.GetString("verb-common-open");
|
||||
toggleVerb.IconTexture = "/Textures/Interface/VerbIcons/open.svg.192dpi.png";
|
||||
toggleVerb.Icon =
|
||||
new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/open.svg.192dpi.png"));
|
||||
}
|
||||
args.Verbs.Add(toggleVerb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,12 @@ namespace Content.Shared.Chemistry.Components
|
|||
[NetworkedComponent, ComponentProtoName("Injector")]
|
||||
public abstract class SharedInjectorComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks to see if the entity being injected
|
||||
/// </summary>
|
||||
[DataField("isInjecting")]
|
||||
public bool IsInjecting;
|
||||
|
||||
/// <summary>
|
||||
/// Component data used for net updates. Used by client for item status ui
|
||||
/// </summary>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue