Merge pull request #792 from DebugOk/Wizmerge-08/02
Merge wizden up to 08/02
This commit is contained in:
commit
bde57bb886
|
|
@ -0,0 +1,27 @@
|
|||
name: Close PR's on master
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [ opened, ready_for_review ]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{github.head_ref == 'master' || github.head_ref == 'main' || github.head_ref == 'develop'}}
|
||||
|
||||
steps:
|
||||
- uses: superbrothers/close-pull-request@v3
|
||||
with:
|
||||
comment: "Thank you for contributing to the Space Station 14 repository. Unfortunately, it looks like you submitted your pull request from the master branch. We suggest you follow [our git usage documentation](https://docs.spacestation14.com/en/general-development/setup/git-for-the-ss14-developer.html) \n\n You can move your current work from the master branch to another branch by doing `git branch <branch_name` and resetting the master branch."
|
||||
|
||||
# If you prefer to just comment on the pr and not close it, uncomment the bellow and comment the above
|
||||
|
||||
# - uses: actions/github-script@v7
|
||||
# with:
|
||||
# script: |
|
||||
# github.rest.issues.createComment({
|
||||
# issue_number: ${{ github.event.number }},
|
||||
# owner: context.repo.owner,
|
||||
# repo: context.repo.repo,
|
||||
# body: "Thank you for contributing to the Space Station 14 repository. Unfortunately, it looks like you submitted your pull request from the master branch. We suggest you follow [our git usage documentation](https://docs.spacestation14.com/en/general-development/setup/git-for-the-ss14-developer.html) \n\n You can move your current work from the master branch to another branch by doing `git branch <branch_name` and resetting the master branch. \n\n This pr won't be automatically closed. However, a maintainer may close it for this reason."
|
||||
# })
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Content Server+Client" type="CompoundRunConfigurationType">
|
||||
<toRun name="Content.Client" type="DotNetProject" />
|
||||
<toRun name="Content.Server" type="DotNetProject" />
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace Content.Client.Access.UI
|
|||
|
||||
foreach (var job in jobs)
|
||||
{
|
||||
if (!job.SetPreference)
|
||||
if (!job.OverrideConsoleVisibility.GetValueOrDefault(job.SetPreference))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,8 @@
|
|||
using System.Linq;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.Administration;
|
||||
|
||||
// mfw they ported input() from BYOND
|
||||
|
||||
/// <summary>
|
||||
/// This handles the client portion of quick dialogs.
|
||||
/// </summary>
|
||||
|
|
@ -21,149 +16,22 @@ public sealed class QuickDialogSystem : EntitySystem
|
|||
|
||||
private void OpenDialog(QuickDialogOpenEvent ev)
|
||||
{
|
||||
var window = new FancyWindow()
|
||||
{
|
||||
Title = ev.Title
|
||||
};
|
||||
var ok = (ev.Buttons & QuickDialogButtonFlag.OkButton) != 0;
|
||||
var cancel = (ev.Buttons & QuickDialogButtonFlag.CancelButton) != 0;
|
||||
var window = new DialogWindow(ev.Title, ev.Prompts, ok: ok, cancel: cancel);
|
||||
|
||||
var entryContainer = new BoxContainer()
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||
Margin = new Thickness(8),
|
||||
};
|
||||
|
||||
var promptsDict = new Dictionary<string, LineEdit>();
|
||||
|
||||
for (var index = 0; index < ev.Prompts.Count; index++)
|
||||
{
|
||||
var entry = ev.Prompts[index];
|
||||
var entryBox = new BoxContainer()
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal
|
||||
};
|
||||
|
||||
entryBox.AddChild(new Label { Text = entry.Prompt, HorizontalExpand = true, SizeFlagsStretchRatio = 0.5f });
|
||||
var edit = new LineEdit() { HorizontalExpand = true };
|
||||
entryBox.AddChild(edit);
|
||||
switch (entry.Type)
|
||||
{
|
||||
case QuickDialogEntryType.Integer:
|
||||
edit.IsValid += VerifyInt;
|
||||
edit.PlaceHolder = Loc.GetString("quick-dialog-ui-integer");
|
||||
break;
|
||||
case QuickDialogEntryType.Float:
|
||||
edit.IsValid += VerifyFloat;
|
||||
edit.PlaceHolder = Loc.GetString("quick-dialog-ui-float");
|
||||
break;
|
||||
case QuickDialogEntryType.ShortText:
|
||||
edit.IsValid += VerifyShortText;
|
||||
edit.PlaceHolder = Loc.GetString("quick-dialog-ui-short-text");
|
||||
break;
|
||||
case QuickDialogEntryType.LongText:
|
||||
edit.IsValid += VerifyLongText;
|
||||
edit.PlaceHolder = Loc.GetString("quick-dialog-ui-long-text");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
promptsDict.Add(entry.FieldId, edit);
|
||||
entryContainer.AddChild(entryBox);
|
||||
|
||||
if (index == ev.Prompts.Count - 1)
|
||||
{
|
||||
// Last text box gets enter confirmation.
|
||||
// Only the last so you don't accidentally confirm early.
|
||||
edit.OnTextEntered += _ => Confirm();
|
||||
}
|
||||
}
|
||||
|
||||
var buttonsBox = new BoxContainer()
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||
HorizontalAlignment = Control.HAlignment.Center,
|
||||
};
|
||||
|
||||
var alreadyReplied = false;
|
||||
|
||||
if ((ev.Buttons & QuickDialogButtonFlag.OkButton) != 0)
|
||||
{
|
||||
var okButton = new Button()
|
||||
{
|
||||
Text = Loc.GetString("quick-dialog-ui-ok"),
|
||||
};
|
||||
|
||||
okButton.OnPressed += _ => Confirm();
|
||||
|
||||
buttonsBox.AddChild(okButton);
|
||||
}
|
||||
|
||||
if ((ev.Buttons & QuickDialogButtonFlag.OkButton) != 0)
|
||||
{
|
||||
var cancelButton = new Button()
|
||||
{
|
||||
Text = Loc.GetString("quick-dialog-ui-cancel"),
|
||||
};
|
||||
|
||||
cancelButton.OnPressed += _ =>
|
||||
{
|
||||
RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
|
||||
new(),
|
||||
QuickDialogButtonFlag.CancelButton));
|
||||
alreadyReplied = true;
|
||||
window.Close();
|
||||
};
|
||||
|
||||
buttonsBox.AddChild(cancelButton);
|
||||
}
|
||||
|
||||
window.OnClose += () =>
|
||||
{
|
||||
if (!alreadyReplied)
|
||||
{
|
||||
RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
|
||||
new(),
|
||||
QuickDialogButtonFlag.CancelButton));
|
||||
}
|
||||
};
|
||||
|
||||
entryContainer.AddChild(buttonsBox);
|
||||
|
||||
window.ContentsContainer.AddChild(entryContainer);
|
||||
|
||||
window.MinWidth *= 2; // Just double it.
|
||||
|
||||
window.OpenCentered();
|
||||
|
||||
return;
|
||||
|
||||
void Confirm()
|
||||
window.OnConfirmed += responses =>
|
||||
{
|
||||
RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
|
||||
promptsDict.Select(x => (x.Key, x.Value.Text)).ToDictionary(x => x.Key, x => x.Text),
|
||||
responses,
|
||||
QuickDialogButtonFlag.OkButton));
|
||||
alreadyReplied = true;
|
||||
window.Close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private bool VerifyInt(string input)
|
||||
{
|
||||
return int.TryParse(input, out var _);
|
||||
}
|
||||
|
||||
private bool VerifyFloat(string input)
|
||||
{
|
||||
return float.TryParse(input, out var _);
|
||||
}
|
||||
|
||||
private bool VerifyShortText(string input)
|
||||
{
|
||||
return input.Length <= 100;
|
||||
}
|
||||
|
||||
private bool VerifyLongText(string input)
|
||||
{
|
||||
return input.Length <= 2000;
|
||||
window.OnCancelled += () =>
|
||||
{
|
||||
RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
|
||||
new(),
|
||||
QuickDialogButtonFlag.CancelButton));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Antag;
|
||||
using Content.Shared.Revolutionary.Components;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Content.Shared.Zombies;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
|
|
@ -9,24 +11,44 @@ namespace Content.Client.Antag;
|
|||
/// <summary>
|
||||
/// Used for assigning specified icons for antags.
|
||||
/// </summary>
|
||||
public abstract class AntagStatusIconSystem<T> : SharedStatusIconSystem
|
||||
where T : IComponent
|
||||
public sealed class AntagStatusIconSystem : SharedStatusIconSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RevolutionaryComponent, GetStatusIconsEvent>(GetRevIcon);
|
||||
SubscribeLocalEvent<ZombieComponent, GetStatusIconsEvent>(GetIcon);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, GetStatusIconsEvent>(GetIcon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will check if the local player has the same component as the one who called it and give the status icon.
|
||||
/// Adds a Status Icon on an entity if the player is supposed to see it.
|
||||
/// </summary>
|
||||
/// <param name="antagStatusIcon">The status icon that your antag uses</param>
|
||||
/// <param name="args">The GetStatusIcon event.</param>
|
||||
protected virtual void GetStatusIcon(string antagStatusIcon, ref GetStatusIconsEvent args)
|
||||
private void GetIcon<T>(EntityUid uid, T comp, ref GetStatusIconsEvent ev) where T: IAntagStatusIconComponent
|
||||
{
|
||||
var ent = _player.LocalPlayer?.ControlledEntity;
|
||||
var ent = _player.LocalSession?.AttachedEntity;
|
||||
|
||||
if (!HasComp<T>(ent) && !HasComp<GhostComponent>(ent))
|
||||
var canEv = new CanDisplayStatusIconsEvent(ent);
|
||||
RaiseLocalEvent(uid, ref canEv);
|
||||
|
||||
if (!canEv.Cancelled)
|
||||
ev.StatusIcons.Add(_prototype.Index(comp.StatusIcon));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Rev Icon on an entity if the player is supposed to see it. This additional function is needed to deal
|
||||
/// with a special case where if someone is a head rev we only want to display the headrev icon.
|
||||
/// </summary>
|
||||
private void GetRevIcon(EntityUid uid, RevolutionaryComponent comp, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (HasComp<HeadRevolutionaryComponent>(uid))
|
||||
return;
|
||||
|
||||
args.StatusIcons.Add(_prototype.Index<StatusIconPrototype>(antagStatusIcon));
|
||||
GetIcon(uid, comp, ref ev);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
using Content.Shared.Atmos.EntitySystems;
|
||||
|
||||
namespace Content.Client.Atmos.EntitySystems;
|
||||
|
||||
public sealed class FirestarterSystem : SharedFirestarterSystem
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ namespace Content.Client.Atmos.UI
|
|||
|
||||
_window = new GasAnalyzerWindow();
|
||||
_window.OnClose += OnClose;
|
||||
_window.OpenCentered();
|
||||
_window.OpenCenteredLeft();
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
|
|
|
|||
|
|
@ -25,8 +25,10 @@ public sealed class BackgroundAudioSystem : EntitySystem
|
|||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
|
||||
private readonly AudioParams _lobbyParams = new(-5f, 1, "Master", 0, 0, 0, true, 0f);
|
||||
private readonly AudioParams _roundEndParams = new(-5f, 1, "Master", 0, 0, 0, false, 0f);
|
||||
|
||||
public EntityUid? LobbyStream;
|
||||
public EntityUid? LobbyMusicStream;
|
||||
public EntityUid? LobbyRoundRestartAudioStream;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -115,7 +117,7 @@ public sealed class BackgroundAudioSystem : EntitySystem
|
|||
|
||||
public void StartLobbyMusic()
|
||||
{
|
||||
if (LobbyStream != null || !_configManager.GetCVar(CCVars.LobbyMusicEnabled))
|
||||
if (LobbyMusicStream != null || !_configManager.GetCVar(CCVars.LobbyMusicEnabled))
|
||||
return;
|
||||
|
||||
var file = _gameTicker.LobbySong;
|
||||
|
|
@ -124,18 +126,21 @@ public sealed class BackgroundAudioSystem : EntitySystem
|
|||
return;
|
||||
}
|
||||
|
||||
LobbyStream = _audio.PlayGlobal(file, Filter.Local(), false,
|
||||
LobbyMusicStream = _audio.PlayGlobal(
|
||||
file,
|
||||
Filter.Local(),
|
||||
false,
|
||||
_lobbyParams.WithVolume(_lobbyParams.Volume + SharedAudioSystem.GainToVolume(_configManager.GetCVar(CCVars.LobbyMusicVolume))))?.Entity;
|
||||
}
|
||||
|
||||
private void EndLobbyMusic()
|
||||
{
|
||||
LobbyStream = _audio.Stop(LobbyStream);
|
||||
LobbyMusicStream = _audio.Stop(LobbyMusicStream);
|
||||
}
|
||||
|
||||
private void PlayRestartSound(RoundRestartCleanupEvent ev)
|
||||
{
|
||||
if (!_configManager.GetCVar(CCVars.LobbyMusicEnabled))
|
||||
if (!_configManager.GetCVar(CCVars.RestartSoundsEnabled))
|
||||
return;
|
||||
|
||||
var file = _gameTicker.RestartSound;
|
||||
|
|
@ -144,10 +149,11 @@ public sealed class BackgroundAudioSystem : EntitySystem
|
|||
return;
|
||||
}
|
||||
|
||||
var volume = _lobbyParams.WithVolume(_lobbyParams.Volume +
|
||||
SharedAudioSystem.GainToVolume(
|
||||
_configManager.GetCVar(CCVars.LobbyMusicVolume)));
|
||||
|
||||
_audio.PlayGlobal(file, Filter.Local(), false, volume);
|
||||
LobbyRoundRestartAudioStream = _audio.PlayGlobal(
|
||||
file,
|
||||
Filter.Local(),
|
||||
false,
|
||||
_roundEndParams.WithVolume(_roundEndParams.Volume + SharedAudioSystem.GainToVolume(_configManager.GetCVar(CCVars.LobbyMusicVolume)))
|
||||
)?.Entity;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,15 +50,24 @@ public sealed partial class ContentAudioSystem : SharedContentAudioSystem
|
|||
_fadingOut.Clear();
|
||||
|
||||
// Preserve lobby music but everything else should get dumped.
|
||||
var lobbyStream = EntityManager.System<BackgroundAudioSystem>().LobbyStream;
|
||||
TryComp(lobbyStream, out AudioComponent? audioComp);
|
||||
var oldGain = audioComp?.Gain;
|
||||
var lobbyMusic = EntityManager.System<BackgroundAudioSystem>().LobbyMusicStream;
|
||||
TryComp(lobbyMusic, out AudioComponent? lobbyMusicComp);
|
||||
var oldMusicGain = lobbyMusicComp?.Gain;
|
||||
|
||||
var restartAudio = EntityManager.System<BackgroundAudioSystem>().LobbyRoundRestartAudioStream;
|
||||
TryComp(restartAudio, out AudioComponent? restartComp);
|
||||
var oldAudioGain = restartComp?.Gain;
|
||||
|
||||
SilenceAudio();
|
||||
|
||||
if (oldGain != null)
|
||||
if (oldMusicGain != null)
|
||||
{
|
||||
Audio.SetGain(lobbyStream, oldGain.Value, audioComp);
|
||||
Audio.SetGain(lobbyMusic, oldMusicGain.Value, lobbyMusicComp);
|
||||
}
|
||||
|
||||
if (oldAudioGain != null)
|
||||
{
|
||||
Audio.SetGain(restartAudio, oldAudioGain.Value, restartComp);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
using Content.Shared.Botany.Components;
|
||||
|
||||
namespace Content.Client.Botany.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class ProduceComponent : SharedProduceComponent
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
using Content.Shared.Botany.Components;
|
||||
|
||||
namespace Content.Client.Botany.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class SeedComponent : SharedSeedComponent
|
||||
{
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ using Content.Client.Rotation;
|
|||
using Content.Shared.Buckle;
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Rotation;
|
||||
using Content.Shared.Vehicle.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Buckle;
|
||||
|
|
@ -26,9 +25,6 @@ internal sealed class BuckleSystem : SharedBuckleSystem
|
|||
if (!TryComp<SpriteComponent>(uid, out var ownerSprite))
|
||||
return;
|
||||
|
||||
if (HasComp<VehicleComponent>(component.LastEntityBuckledTo))
|
||||
return;
|
||||
|
||||
// Adjust draw depth when the chair faces north so that the seat back is drawn over the player.
|
||||
// Reset the draw depth when rotated in any other direction.
|
||||
// TODO when ECSing, make this a visualizer
|
||||
|
|
|
|||
|
|
@ -1,14 +1,34 @@
|
|||
using System.Numerics;
|
||||
using Content.Shared.Camera;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.Camera;
|
||||
|
||||
public sealed class CameraRecoilSystem : SharedCameraRecoilSystem
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
|
||||
protected float Intensity;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeNetworkEvent<CameraKickEvent>(OnCameraKick);
|
||||
|
||||
_configManager.OnValueChanged(CCVars.ScreenShakeIntensity, OnCvarChanged, true);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
_configManager.UnsubValueChanged(CCVars.ScreenShakeIntensity, OnCvarChanged);
|
||||
}
|
||||
|
||||
private void OnCvarChanged(float value)
|
||||
{
|
||||
Intensity = value;
|
||||
}
|
||||
|
||||
private void OnCameraKick(CameraKickEvent ev)
|
||||
|
|
@ -18,9 +38,14 @@ public sealed class CameraRecoilSystem : SharedCameraRecoilSystem
|
|||
|
||||
public override void KickCamera(EntityUid uid, Vector2 recoil, CameraRecoilComponent? component = null)
|
||||
{
|
||||
if (Intensity == 0)
|
||||
return;
|
||||
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return;
|
||||
|
||||
recoil *= Intensity;
|
||||
|
||||
// Use really bad math to "dampen" kicks when we're already kicked.
|
||||
var existing = component.CurrentKick.Length();
|
||||
var dampen = existing / KickMagnitudeMax;
|
||||
|
|
|
|||
|
|
@ -4,16 +4,16 @@
|
|||
Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="NumberLabel"
|
||||
Align="Center"
|
||||
SetWidth="60"
|
||||
Align="Right"
|
||||
SetWidth="26"
|
||||
ClipText="True"/>
|
||||
<Label Name="TimeLabel"
|
||||
Align="Center"
|
||||
SetWidth="280"
|
||||
SetWidth="100"
|
||||
ClipText="True"/>
|
||||
<Label Name="AccessorLabel"
|
||||
Align="Center"
|
||||
SetWidth="110"
|
||||
Align="Left"
|
||||
SetWidth="390"
|
||||
ClipText="True"/>
|
||||
</BoxContainer>
|
||||
<customControls:HSeparator Margin="0 5 0 5"/>
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@
|
|||
BorderColor="#5a5a5a"
|
||||
BorderThickness="0 0 0 1"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Orientation="Horizontal" Align="Center" Margin="8">
|
||||
<Label HorizontalExpand="True" Text="{Loc 'log-probe-label-number'}"/>
|
||||
<Label HorizontalExpand="True" Text="{Loc 'log-probe-label-time'}"/>
|
||||
<Label HorizontalExpand="True" Text="{Loc 'log-probe-label-accessor'}"/>
|
||||
<BoxContainer Orientation="Horizontal" Margin="4 8">
|
||||
<Label Align="Right" SetWidth="26" ClipText="True" Text="{Loc 'log-probe-label-number'}"/>
|
||||
<Label Align="Center" SetWidth="100" ClipText="True" Text="{Loc 'log-probe-label-time'}"/>
|
||||
<Label Align="Left" SetWidth="390" ClipText="True" Text="{Loc 'log-probe-label-accessor'}"/>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
<ScrollContainer VerticalExpand="True" HScrollEnabled="True">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using Content.Client.Items.Systems;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Rounding;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
|
@ -10,11 +12,13 @@ namespace Content.Client.Chemistry.Visualizers;
|
|||
public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionContainerVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly ItemSystem _itemSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<SolutionContainerVisualsComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<SolutionContainerVisualsComponent, GetInhandVisualsEvent>(OnGetHeldVisuals);
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, SolutionContainerVisualsComponent component, MapInitEvent args)
|
||||
|
|
@ -111,6 +115,35 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
|
|||
}
|
||||
}
|
||||
|
||||
// in-hand visuals
|
||||
_itemSystem.VisualsChanged(uid);
|
||||
}
|
||||
|
||||
private void OnGetHeldVisuals(EntityUid uid, SolutionContainerVisualsComponent component, GetInhandVisualsEvent args)
|
||||
{
|
||||
if (component.InHandsFillBaseName == null)
|
||||
return;
|
||||
|
||||
if (!TryComp(uid, out AppearanceComponent? appearance))
|
||||
return;
|
||||
|
||||
if (!AppearanceSystem.TryGetData<float>(uid, SolutionContainerVisuals.FillFraction, out var fraction, appearance))
|
||||
return;
|
||||
|
||||
int closestFillSprite = ContentHelpers.RoundToLevels(fraction, 1, component.InHandsMaxFillLevels + 1);
|
||||
|
||||
if (closestFillSprite > 0)
|
||||
{
|
||||
var layer = new PrototypeLayerData();
|
||||
|
||||
var key = "inhand-" + args.Location.ToString().ToLowerInvariant() + component.InHandsFillBaseName + closestFillSprite;
|
||||
|
||||
layer.State = key;
|
||||
|
||||
if (component.ChangeColor && AppearanceSystem.TryGetData<Color>(uid, SolutionContainerVisuals.Color, out var color, appearance))
|
||||
layer.Color = color;
|
||||
|
||||
args.Layers.Add((key, layer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,12 +79,11 @@ namespace Content.Client.ContextMenu.UI
|
|||
var representation = _entityManager.ToPrettyString(entity);
|
||||
|
||||
var name = representation.Name;
|
||||
var id = representation.Uid.ToString();
|
||||
var prototype = representation.Prototype;
|
||||
var playerName = representation.Session?.Name ?? SearchPlayerName(entity);
|
||||
var deleted = representation.Deleted;
|
||||
|
||||
return $"{name} ({id} / {_entityManager.GetNetEntity(entity).ToString()}{(prototype != null ? $", {prototype}" : "")}{(playerName != null ? $", {playerName}" : "")}){(deleted ? "D" : "")}";
|
||||
return $"{name} ({_entityManager.GetNetEntity(entity).ToString()}{(prototype != null ? $", {prototype}" : "")}{(playerName != null ? $", {playerName}" : "")}){(deleted ? "D" : "")}";
|
||||
}
|
||||
|
||||
private string GetEntityDescription(EntityUid entity)
|
||||
|
|
@ -94,7 +93,7 @@ namespace Content.Client.ContextMenu.UI
|
|||
return GetEntityDescriptionAdmin(entity);
|
||||
}
|
||||
|
||||
return Identity.Name(entity, _entityManager, _playerManager.LocalPlayer!.ControlledEntity!);
|
||||
return Identity.Name(entity, _entityManager, _playerManager.LocalEntity!);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ namespace Content.Client.Crayon.UI
|
|||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
var crayonDecals = prototypeManager.EnumeratePrototypes<DecalPrototype>().Where(x => x.Tags.Contains("crayon"));
|
||||
_menu.Populate(crayonDecals);
|
||||
_menu.OpenCentered();
|
||||
_menu.OpenCenteredLeft();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'criminal-records-console-crime-history'}"
|
||||
MinSize="660 400">
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" Margin="5">
|
||||
<BoxContainer Name="Editing" Orientation="Horizontal" HorizontalExpand="True" Align="Center" Margin="5">
|
||||
<Button Name="AddButton" Text="{Loc 'criminal-records-add-history'}"/>
|
||||
<Button Name="DeleteButton" Text="{Loc 'criminal-records-delete-history'}" Disabled="True"/>
|
||||
</BoxContainer>
|
||||
<Label Name="NoHistory" Text="{Loc 'criminal-records-no-history'}" HorizontalExpand="True" HorizontalAlignment="Center"/>
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<ItemList Name="History"/> <!-- Populated when window opened -->
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
using Content.Shared.Administration;
|
||||
using Content.Shared.CriminalRecords;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.CriminalRecords;
|
||||
|
||||
/// <summary>
|
||||
/// Window opened when Crime History button is pressed
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class CrimeHistoryWindow : FancyWindow
|
||||
{
|
||||
public Action<string>? OnAddHistory;
|
||||
public Action<uint>? OnDeleteHistory;
|
||||
|
||||
private uint _maxLength;
|
||||
private uint? _index;
|
||||
private DialogWindow? _dialog;
|
||||
|
||||
public CrimeHistoryWindow(uint maxLength)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
_maxLength = maxLength;
|
||||
|
||||
OnClose += () =>
|
||||
{
|
||||
_dialog?.Close();
|
||||
// deselect so when reopening the window it doesnt try to use invalid index
|
||||
_index = null;
|
||||
};
|
||||
|
||||
AddButton.OnPressed += _ =>
|
||||
{
|
||||
if (_dialog != null)
|
||||
{
|
||||
_dialog.MoveToFront();
|
||||
return;
|
||||
}
|
||||
|
||||
var field = "line";
|
||||
var prompt = Loc.GetString("criminal-records-console-reason");
|
||||
var placeholder = Loc.GetString("criminal-records-history-placeholder");
|
||||
var entry = new QuickDialogEntry(field, QuickDialogEntryType.LongText, prompt, placeholder);
|
||||
var entries = new List<QuickDialogEntry> { entry };
|
||||
_dialog = new DialogWindow(Title!, entries);
|
||||
|
||||
_dialog.OnConfirmed += responses =>
|
||||
{
|
||||
var line = responses[field];
|
||||
if (line.Length < 1 || line.Length > _maxLength)
|
||||
return;
|
||||
|
||||
OnAddHistory?.Invoke(line);
|
||||
// adding deselects so prevent deleting yeah
|
||||
_index = null;
|
||||
DeleteButton.Disabled = true;
|
||||
};
|
||||
|
||||
// prevent MoveToFront being called on a closed window and double closing
|
||||
_dialog.OnClose += () => { _dialog = null; };
|
||||
};
|
||||
DeleteButton.OnPressed += _ =>
|
||||
{
|
||||
if (_index is not {} index)
|
||||
return;
|
||||
|
||||
OnDeleteHistory?.Invoke(index);
|
||||
// prevent total spam wiping
|
||||
History.ClearSelected();
|
||||
_index = null;
|
||||
DeleteButton.Disabled = true;
|
||||
};
|
||||
|
||||
History.OnItemSelected += args =>
|
||||
{
|
||||
_index = (uint) args.ItemIndex;
|
||||
DeleteButton.Disabled = false;
|
||||
};
|
||||
History.OnItemDeselected += args =>
|
||||
{
|
||||
_index = null;
|
||||
DeleteButton.Disabled = true;
|
||||
};
|
||||
}
|
||||
|
||||
public void UpdateHistory(CriminalRecord record, bool access)
|
||||
{
|
||||
History.Clear();
|
||||
Editing.Visible = access;
|
||||
|
||||
NoHistory.Visible = record.History.Count == 0;
|
||||
|
||||
foreach (var entry in record.History)
|
||||
{
|
||||
var time = entry.AddTime;
|
||||
var line = $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00} - {entry.Crime}";
|
||||
History.AddItem(line);
|
||||
}
|
||||
|
||||
// deselect if something goes wrong
|
||||
if (_index is {} index && record.History.Count >= index)
|
||||
_index = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.CriminalRecords;
|
||||
using Content.Shared.CriminalRecords.Components;
|
||||
using Content.Shared.Security;
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Client.CriminalRecords;
|
||||
|
||||
public sealed class CriminalRecordsConsoleBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
private readonly AccessReaderSystem _accessReader;
|
||||
|
||||
private CriminalRecordsConsoleWindow? _window;
|
||||
private CrimeHistoryWindow? _historyWindow;
|
||||
|
||||
public CriminalRecordsConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
_accessReader = EntMan.System<AccessReaderSystem>();
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
var comp = EntMan.GetComponent<CriminalRecordsConsoleComponent>(Owner);
|
||||
|
||||
_window = new(Owner, comp.MaxStringLength, _playerManager, _proto, _random, _accessReader);
|
||||
_window.OnKeySelected += key =>
|
||||
SendMessage(new SelectStationRecord(key));
|
||||
_window.OnFiltersChanged += (type, filterValue) =>
|
||||
SendMessage(new SetStationRecordFilter(type, filterValue));
|
||||
_window.OnStatusSelected += status =>
|
||||
SendMessage(new CriminalRecordChangeStatus(status, null));
|
||||
_window.OnDialogConfirmed += (_, reason) =>
|
||||
SendMessage(new CriminalRecordChangeStatus(SecurityStatus.Wanted, reason));
|
||||
_window.OnHistoryUpdated += UpdateHistory;
|
||||
_window.OnHistoryClosed += () => _historyWindow?.Close();
|
||||
_window.OnClose += Close;
|
||||
|
||||
_historyWindow = new(comp.MaxStringLength);
|
||||
_historyWindow.OnAddHistory += line => SendMessage(new CriminalRecordAddHistory(line));
|
||||
_historyWindow.OnDeleteHistory += index => SendMessage(new CriminalRecordDeleteHistory(index));
|
||||
|
||||
_historyWindow.Close(); // leave closed until user opens it
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates or opens a new history window.
|
||||
/// </summary>
|
||||
private void UpdateHistory(CriminalRecord record, bool access, bool open)
|
||||
{
|
||||
_historyWindow!.UpdateHistory(record, access);
|
||||
|
||||
if (open)
|
||||
_historyWindow.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not CriminalRecordsConsoleState cast)
|
||||
return;
|
||||
|
||||
_window?.UpdateState(cast);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
_window?.Close();
|
||||
_historyWindow?.Close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'criminal-records-console-window-title'}"
|
||||
MinSize="660 400">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<!-- Record search bar
|
||||
TODO: make this into a control shared with general records -->
|
||||
<BoxContainer Margin="5 5 5 10" HorizontalExpand="true" VerticalAlignment="Center">
|
||||
<OptionButton Name="FilterType" MinWidth="200" Margin="0 0 10 0"/> <!-- Populated in constructor -->
|
||||
<LineEdit Name="FilterText" PlaceHolder="{Loc 'criminal-records-filter-placeholder'}" HorizontalExpand="True"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" VerticalExpand="True">
|
||||
<!-- Record listing -->
|
||||
<BoxContainer Orientation="Vertical" Margin="5" MinWidth="250" MaxWidth="250">
|
||||
<Label Name="RecordListingTitle" Text="{Loc 'criminal-records-console-records-list-title'}" HorizontalExpand="True" Align="Center"/>
|
||||
<Label Name="NoRecords" Text="{Loc 'criminal-records-console-no-records'}" HorizontalExpand="True" Align="Center" FontColorOverride="DarkGray"/>
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<ItemList Name="RecordListing"/> <!-- Populated when loading state -->
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
<Label Name="RecordUnselected" Text="{Loc 'criminal-records-console-select-record-info'}" HorizontalExpand="True" Align="Center" FontColorOverride="DarkGray"/>
|
||||
<!-- Selected record info -->
|
||||
<BoxContainer Name="PersonContainer" Orientation="Vertical" Margin="5" Visible="False">
|
||||
<Label Name="PersonName" StyleClasses="LabelBig"/>
|
||||
<Label Name="PersonPrints"/>
|
||||
<Label Name="PersonDna"/>
|
||||
<PanelContainer StyleClasses="LowDivider" Margin="0 5 0 5" />
|
||||
<BoxContainer Orientation="Horizontal" Margin="5 5 5 5">
|
||||
<Label Name="StatusLabel" Text="{Loc 'criminal-records-console-status'}" FontColorOverride="DarkGray"/>
|
||||
<OptionButton Name="StatusOptionButton"/> <!-- Populated in constructor -->
|
||||
</BoxContainer>
|
||||
<RichTextLabel Name="WantedReason" Visible="False"/>
|
||||
<Button Name="HistoryButton" Text="{Loc 'criminal-records-console-crime-history'}"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CriminalRecords;
|
||||
using Content.Shared.Dataset;
|
||||
using Content.Shared.Security;
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.CriminalRecords;
|
||||
|
||||
// TODO: dedupe shitcode from general records theres a lot
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
|
||||
{
|
||||
private readonly IPlayerManager _player;
|
||||
private readonly IPrototypeManager _proto;
|
||||
private readonly IRobustRandom _random;
|
||||
private readonly AccessReaderSystem _accessReader;
|
||||
|
||||
public readonly EntityUid Console;
|
||||
|
||||
[ValidatePrototypeId<DatasetPrototype>]
|
||||
private const string ReasonPlaceholders = "CriminalRecordsWantedReasonPlaceholders";
|
||||
|
||||
public Action<uint?>? OnKeySelected;
|
||||
public Action<StationRecordFilterType, string>? OnFiltersChanged;
|
||||
public Action<SecurityStatus>? OnStatusSelected;
|
||||
public Action<CriminalRecord, bool, bool>? OnHistoryUpdated;
|
||||
public Action? OnHistoryClosed;
|
||||
public Action<SecurityStatus, string>? OnDialogConfirmed;
|
||||
|
||||
private uint _maxLength;
|
||||
private bool _isPopulating;
|
||||
private bool _access;
|
||||
private uint? _selectedKey;
|
||||
private CriminalRecord? _selectedRecord;
|
||||
|
||||
private DialogWindow? _reasonDialog;
|
||||
|
||||
private StationRecordFilterType _currentFilterType;
|
||||
|
||||
public CriminalRecordsConsoleWindow(EntityUid console, uint maxLength, IPlayerManager playerManager, IPrototypeManager prototypeManager, IRobustRandom robustRandom, AccessReaderSystem accessReader)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
Console = console;
|
||||
_player = playerManager;
|
||||
_proto = prototypeManager;
|
||||
_random = robustRandom;
|
||||
_accessReader = accessReader;
|
||||
|
||||
_maxLength = maxLength;
|
||||
_currentFilterType = StationRecordFilterType.Name;
|
||||
|
||||
OpenCentered();
|
||||
|
||||
foreach (var item in Enum.GetValues<StationRecordFilterType>())
|
||||
{
|
||||
FilterType.AddItem(GetTypeFilterLocals(item), (int)item);
|
||||
}
|
||||
|
||||
foreach (var status in Enum.GetValues<SecurityStatus>())
|
||||
{
|
||||
AddStatusSelect(status);
|
||||
}
|
||||
|
||||
OnClose += () => _reasonDialog?.Close();
|
||||
|
||||
RecordListing.OnItemSelected += args =>
|
||||
{
|
||||
if (_isPopulating || RecordListing[args.ItemIndex].Metadata is not uint cast)
|
||||
return;
|
||||
|
||||
OnKeySelected?.Invoke(cast);
|
||||
};
|
||||
|
||||
RecordListing.OnItemDeselected += _ =>
|
||||
{
|
||||
if (!_isPopulating)
|
||||
OnKeySelected?.Invoke(null);
|
||||
};
|
||||
|
||||
FilterType.OnItemSelected += eventArgs =>
|
||||
{
|
||||
var type = (StationRecordFilterType)eventArgs.Id;
|
||||
|
||||
if (_currentFilterType != type)
|
||||
{
|
||||
_currentFilterType = type;
|
||||
FilterListingOfRecords(FilterText.Text);
|
||||
}
|
||||
};
|
||||
|
||||
FilterText.OnTextEntered += args =>
|
||||
{
|
||||
FilterListingOfRecords(args.Text);
|
||||
};
|
||||
|
||||
StatusOptionButton.OnItemSelected += args =>
|
||||
{
|
||||
SetStatus((SecurityStatus) args.Id);
|
||||
};
|
||||
|
||||
HistoryButton.OnPressed += _ =>
|
||||
{
|
||||
if (_selectedRecord is {} record)
|
||||
OnHistoryUpdated?.Invoke(record, _access, true);
|
||||
};
|
||||
}
|
||||
|
||||
public void UpdateState(CriminalRecordsConsoleState state)
|
||||
{
|
||||
if (state.Filter != null)
|
||||
{
|
||||
if (state.Filter.Type != _currentFilterType)
|
||||
{
|
||||
_currentFilterType = state.Filter.Type;
|
||||
}
|
||||
|
||||
if (state.Filter.Value != FilterText.Text)
|
||||
{
|
||||
FilterText.Text = state.Filter.Value;
|
||||
}
|
||||
}
|
||||
|
||||
_selectedKey = state.SelectedKey;
|
||||
|
||||
FilterType.SelectId((int)_currentFilterType);
|
||||
|
||||
// set up the records listing panel
|
||||
RecordListing.Clear();
|
||||
|
||||
var hasRecords = state.RecordListing != null && state.RecordListing.Count > 0;
|
||||
NoRecords.Visible = !hasRecords;
|
||||
if (hasRecords)
|
||||
PopulateRecordListing(state.RecordListing!);
|
||||
|
||||
// set up the selected person's record
|
||||
var selected = _selectedKey != null;
|
||||
|
||||
PersonContainer.Visible = selected;
|
||||
RecordUnselected.Visible = !selected;
|
||||
|
||||
_access = _player.LocalSession?.AttachedEntity is {} player
|
||||
&& _accessReader.IsAllowed(player, Console);
|
||||
|
||||
// hide access-required editing parts when no access
|
||||
var editing = _access && selected;
|
||||
StatusOptionButton.Disabled = !editing;
|
||||
|
||||
if (state is { CriminalRecord: not null, StationRecord: not null })
|
||||
{
|
||||
PopulateRecordContainer(state.StationRecord, state.CriminalRecord);
|
||||
OnHistoryUpdated?.Invoke(state.CriminalRecord, _access, false);
|
||||
_selectedRecord = state.CriminalRecord;
|
||||
}
|
||||
else
|
||||
{
|
||||
_selectedRecord = null;
|
||||
OnHistoryClosed?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateRecordListing(Dictionary<uint, string> listing)
|
||||
{
|
||||
_isPopulating = true;
|
||||
|
||||
foreach (var (key, name) in listing)
|
||||
{
|
||||
var item = RecordListing.AddItem(name);
|
||||
item.Metadata = key;
|
||||
item.Selected = key == _selectedKey;
|
||||
}
|
||||
_isPopulating = false;
|
||||
|
||||
RecordListing.SortItemsByText();
|
||||
}
|
||||
|
||||
private void PopulateRecordContainer(GeneralStationRecord stationRecord, CriminalRecord criminalRecord)
|
||||
{
|
||||
var na = Loc.GetString("generic-not-available-shorthand");
|
||||
PersonName.Text = stationRecord.Name;
|
||||
PersonPrints.Text = Loc.GetString("general-station-record-console-record-fingerprint", ("fingerprint", stationRecord.Fingerprint ?? na));
|
||||
PersonDna.Text = Loc.GetString("general-station-record-console-record-dna", ("dna", stationRecord.DNA ?? na));
|
||||
|
||||
StatusOptionButton.SelectId((int) criminalRecord.Status);
|
||||
if (criminalRecord.Reason is {} reason)
|
||||
{
|
||||
var message = FormattedMessage.FromMarkup(Loc.GetString("criminal-records-console-wanted-reason"));
|
||||
message.AddText($": {reason}");
|
||||
WantedReason.SetMessage(message);
|
||||
WantedReason.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
WantedReason.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddStatusSelect(SecurityStatus status)
|
||||
{
|
||||
var name = Loc.GetString($"criminal-records-status-{status.ToString().ToLower()}");
|
||||
StatusOptionButton.AddItem(name, (int)status);
|
||||
}
|
||||
|
||||
private void FilterListingOfRecords(string text = "")
|
||||
{
|
||||
if (!_isPopulating)
|
||||
{
|
||||
OnFiltersChanged?.Invoke(_currentFilterType, text);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetStatus(SecurityStatus status)
|
||||
{
|
||||
if (status == SecurityStatus.Wanted)
|
||||
{
|
||||
GetWantedReason();
|
||||
return;
|
||||
}
|
||||
|
||||
OnStatusSelected?.Invoke(status);
|
||||
}
|
||||
|
||||
private void GetWantedReason()
|
||||
{
|
||||
if (_reasonDialog != null)
|
||||
{
|
||||
_reasonDialog.MoveToFront();
|
||||
return;
|
||||
}
|
||||
|
||||
var field = "reason";
|
||||
var title = Loc.GetString("criminal-records-status-wanted");
|
||||
var placeholders = _proto.Index<DatasetPrototype>(ReasonPlaceholders);
|
||||
var placeholder = Loc.GetString("criminal-records-console-reason-placeholder", ("placeholder", _random.Pick(placeholders.Values))); // just funny it doesn't actually get used
|
||||
var prompt = Loc.GetString("criminal-records-console-reason");
|
||||
var entry = new QuickDialogEntry(field, QuickDialogEntryType.LongText, prompt, placeholder);
|
||||
var entries = new List<QuickDialogEntry>() { entry };
|
||||
_reasonDialog = new DialogWindow(title, entries);
|
||||
|
||||
_reasonDialog.OnConfirmed += responses =>
|
||||
{
|
||||
var reason = responses[field];
|
||||
if (reason.Length < 1 || reason.Length > _maxLength)
|
||||
return;
|
||||
|
||||
OnDialogConfirmed?.Invoke(SecurityStatus.Wanted, reason);
|
||||
};
|
||||
|
||||
_reasonDialog.OnClose += () => { _reasonDialog = null; };
|
||||
}
|
||||
|
||||
private string GetTypeFilterLocals(StationRecordFilterType type)
|
||||
{
|
||||
return Loc.GetString($"criminal-records-{type.ToString().ToLower()}-filter");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
using Content.Shared.CriminalRecords.Systems;
|
||||
|
||||
namespace Content.Client.CriminalRecords.Systems;
|
||||
|
||||
public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleSystem
|
||||
{
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared.Explosion;
|
||||
using Content.Shared.Explosion.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Explosion;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using System.Numerics;
|
||||
using Content.Shared.Explosion;
|
||||
using Content.Shared.Explosion.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared.Explosion;
|
||||
using Content.Shared.Explosion.Components;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.GameStates;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
using Content.Shared.Explosion.EntitySystems;
|
||||
|
||||
namespace Content.Client.Explosion;
|
||||
|
||||
public sealed class SmokeOnTriggerSystem : SharedSmokeOnTriggerSystem
|
||||
{
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared.Explosion;
|
||||
using Content.Shared.Explosion.Components;
|
||||
|
||||
namespace Content.Client.Explosion;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@ namespace Content.Client.GameTicking.Managers
|
|||
{
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
|
||||
[ViewVariables] private bool _initialized;
|
||||
private Dictionary<NetEntity, Dictionary<string, uint?>> _jobsAvailable = new();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
<controls:FancyWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
SetSize="250 100">
|
||||
<ScrollContainer
|
||||
VerticalExpand="True">
|
||||
|
|
@ -12,6 +14,16 @@
|
|||
Name="PatientDataContainer"
|
||||
Orientation="Vertical"
|
||||
Margin="0 0 5 10">
|
||||
<BoxContainer Name="ScanModePanel" HorizontalExpand="True" Visible="False" Margin="0 5 0 0">
|
||||
<Label
|
||||
Name="ScanMode"
|
||||
Align="Left"
|
||||
Text="{Loc health-analyzer-window-scan-mode-text}"/>
|
||||
<Label
|
||||
Name="ScanModeText"
|
||||
Align="Right"
|
||||
HorizontalExpand="True"/>
|
||||
</BoxContainer>
|
||||
<Label
|
||||
Name="PatientName"/>
|
||||
<Label
|
||||
|
|
@ -30,4 +42,4 @@
|
|||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</DefaultWindow>
|
||||
</controls:FancyWindow>
|
||||
|
|
|
|||
|
|
@ -1,24 +1,26 @@
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.MedicalScanner;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.HealthAnalyzer.UI
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class HealthAnalyzerWindow : DefaultWindow
|
||||
public sealed partial class HealthAnalyzerWindow : FancyWindow
|
||||
{
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
|
|
@ -60,13 +62,24 @@ namespace Content.Client.HealthAnalyzer.UI
|
|||
entityName = Identity.Name(target.Value, _entityManager);
|
||||
}
|
||||
|
||||
if (msg.ScanMode.HasValue)
|
||||
{
|
||||
ScanModePanel.Visible = true;
|
||||
ScanModeText.Text = Loc.GetString(msg.ScanMode.Value ? "health-analyzer-window-scan-mode-active" : "health-analyzer-window-scan-mode-inactive");
|
||||
ScanModeText.FontColorOverride = msg.ScanMode.Value ? Color.Green : Color.Red;
|
||||
}
|
||||
else
|
||||
{
|
||||
ScanModePanel.Visible = false;
|
||||
}
|
||||
|
||||
PatientName.Text = Loc.GetString(
|
||||
"health-analyzer-window-entity-health-text",
|
||||
("entityName", entityName)
|
||||
);
|
||||
|
||||
Temperature.Text = Loc.GetString("health-analyzer-window-entity-temperature-text",
|
||||
("temperature", float.IsNaN(msg.Temperature) ? "N/A" : $"{msg.Temperature - 273f:F1} °C")
|
||||
("temperature", float.IsNaN(msg.Temperature) ? "N/A" : $"{msg.Temperature - 273f:F1} °C ({msg.Temperature:F1} °K)")
|
||||
);
|
||||
|
||||
BloodLevel.Text = Loc.GetString("health-analyzer-window-entity-blood-level-text",
|
||||
|
|
@ -85,6 +98,19 @@ namespace Content.Client.HealthAnalyzer.UI
|
|||
|
||||
DrawDiagnosticGroups(damageSortedGroups, damagePerType);
|
||||
|
||||
if (_entityManager.TryGetComponent(target, out HungerComponent? hunger)
|
||||
&& hunger.StarvationDamage != null
|
||||
&& hunger.CurrentThreshold <= HungerThreshold.Starving)
|
||||
{
|
||||
var box = new Control { Margin = new Thickness(0, 0, 0, 15) };
|
||||
|
||||
box.AddChild(CreateDiagnosticGroupTitle(
|
||||
Loc.GetString("health-analyzer-window-malnutrition"),
|
||||
"malnutrition"));
|
||||
|
||||
GroupsContainer.AddChild(box);
|
||||
}
|
||||
|
||||
SetHeight = AnalyzerHeight;
|
||||
SetWidth = AnalyzerWidth;
|
||||
}
|
||||
|
|
@ -113,7 +139,7 @@ namespace Content.Client.HealthAnalyzer.UI
|
|||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||
};
|
||||
|
||||
groupContainer.AddChild(CreateDiagnosticGroupTitle(groupTitleText, damageGroupId, damageAmount.Int()));
|
||||
groupContainer.AddChild(CreateDiagnosticGroupTitle(groupTitleText, damageGroupId));
|
||||
|
||||
GroupsContainer.AddChild(groupContainer);
|
||||
|
||||
|
|
@ -166,7 +192,7 @@ namespace Content.Client.HealthAnalyzer.UI
|
|||
};
|
||||
}
|
||||
|
||||
private BoxContainer CreateDiagnosticGroupTitle(string text, string id, int damageAmount)
|
||||
private BoxContainer CreateDiagnosticGroupTitle(string text, string id)
|
||||
{
|
||||
var rootContainer = new BoxContainer
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,17 +23,20 @@ public sealed class LatheSystem : SharedLatheSystem
|
|||
return;
|
||||
|
||||
if (_appearance.TryGetData<bool>(uid, PowerDeviceVisuals.Powered, out var powered, args.Component) &&
|
||||
args.Sprite.LayerMapTryGet(PowerDeviceVisualLayers.Powered, out _))
|
||||
args.Sprite.LayerMapTryGet(PowerDeviceVisualLayers.Powered, out var powerLayer))
|
||||
{
|
||||
args.Sprite.LayerSetVisible(PowerDeviceVisualLayers.Powered, powered);
|
||||
args.Sprite.LayerSetVisible(powerLayer, powered);
|
||||
}
|
||||
|
||||
// Lathe specific stuff
|
||||
if (_appearance.TryGetData<bool>(uid, LatheVisuals.IsRunning, out var isRunning, args.Component))
|
||||
if (_appearance.TryGetData<bool>(uid, LatheVisuals.IsRunning, out var isRunning, args.Component) &&
|
||||
args.Sprite.LayerMapTryGet(LatheVisualLayers.IsRunning, out var runningLayer) &&
|
||||
component.RunningState != null &&
|
||||
component.IdleState != null)
|
||||
{
|
||||
var state = isRunning ? component.RunningState : component.IdleState;
|
||||
args.Sprite.LayerSetAnimationTime(LatheVisualLayers.IsRunning, 0f);
|
||||
args.Sprite.LayerSetState(LatheVisualLayers.IsRunning, state);
|
||||
args.Sprite.LayerSetAnimationTime(runningLayer, 0f);
|
||||
args.Sprite.LayerSetState(runningLayer, state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ namespace Content.Client.Lathe.UI
|
|||
SendMessage(new LatheQueueRecipeMessage(recipe, amount));
|
||||
};
|
||||
|
||||
_menu.OpenCentered();
|
||||
_menu.OpenCenteredRight();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,15 @@
|
|||
FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
|
||||
StyleClasses="LabelKeyText"/>
|
||||
<CheckBox Name="ReducedMotionCheckBox" Text="{Loc 'ui-options-reduced-motion'}" />
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'ui-options-screen-shake-intensity'}" Margin="8 0" />
|
||||
<Slider Name="ScreenShakeIntensitySlider"
|
||||
MinValue="0"
|
||||
MaxValue="100"
|
||||
Rounded="True"
|
||||
MinWidth="200" />
|
||||
<Label Name="ScreenShakeIntensityLabel" Margin="8 0" />
|
||||
</BoxContainer>
|
||||
<Label Text="{Loc 'ui-options-general-discord'}"
|
||||
FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
|
||||
StyleClasses="LabelKeyText"/>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using Robust.Client.UserInterface.XAML;
|
|||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Range = Robust.Client.UserInterface.Controls.Range;
|
||||
|
||||
namespace Content.Client.Options.UI.Tabs
|
||||
{
|
||||
|
|
@ -63,6 +64,7 @@ namespace Content.Client.Options.UI.Tabs
|
|||
FancySpeechBubblesCheckBox.OnToggled += OnCheckBoxToggled;
|
||||
FancyNameBackgroundsCheckBox.OnToggled += OnCheckBoxToggled;
|
||||
ReducedMotionCheckBox.OnToggled += OnCheckBoxToggled;
|
||||
ScreenShakeIntensitySlider.OnValueChanged += OnScreenShakeIntensitySliderChanged;
|
||||
// ToggleWalk.OnToggled += OnCheckBoxToggled;
|
||||
StaticStorageUI.OnToggled += OnCheckBoxToggled;
|
||||
|
||||
|
|
@ -74,6 +76,8 @@ namespace Content.Client.Options.UI.Tabs
|
|||
OpaqueStorageWindowCheckBox.Pressed = _cfg.GetCVar(CCVars.OpaqueStorageWindow);
|
||||
FancySpeechBubblesCheckBox.Pressed = _cfg.GetCVar(CCVars.ChatEnableFancyBubbles);
|
||||
FancyNameBackgroundsCheckBox.Pressed = _cfg.GetCVar(CCVars.ChatFancyNameBackground);
|
||||
ReducedMotionCheckBox.Pressed = _cfg.GetCVar(CCVars.ReducedMotion);
|
||||
ScreenShakeIntensitySlider.Value = _cfg.GetCVar(CCVars.ScreenShakeIntensity) * 100f;
|
||||
// ToggleWalk.Pressed = _cfg.GetCVar(CCVars.ToggleWalk);
|
||||
StaticStorageUI.Pressed = _cfg.GetCVar(CCVars.StaticStorageUI);
|
||||
|
||||
|
|
@ -93,6 +97,12 @@ namespace Content.Client.Options.UI.Tabs
|
|||
UpdateApplyButton();
|
||||
}
|
||||
|
||||
private void OnScreenShakeIntensitySliderChanged(Range obj)
|
||||
{
|
||||
ScreenShakeIntensityLabel.Text = Loc.GetString("ui-options-screen-shake-percent", ("intensity", ScreenShakeIntensitySlider.Value / 100f));
|
||||
UpdateApplyButton();
|
||||
}
|
||||
|
||||
private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
foreach (var theme in _prototypeManager.EnumeratePrototypes<HudThemePrototype>())
|
||||
|
|
@ -111,6 +121,7 @@ namespace Content.Client.Options.UI.Tabs
|
|||
_cfg.SetCVar(CCVars.ChatEnableFancyBubbles, FancySpeechBubblesCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.ChatFancyNameBackground, FancyNameBackgroundsCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.ReducedMotion, ReducedMotionCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.ScreenShakeIntensity, ScreenShakeIntensitySlider.Value / 100f);
|
||||
// _cfg.SetCVar(CCVars.ToggleWalk, ToggleWalk.Pressed);
|
||||
_cfg.SetCVar(CCVars.StaticStorageUI, StaticStorageUI.Pressed);
|
||||
|
||||
|
|
@ -135,6 +146,7 @@ namespace Content.Client.Options.UI.Tabs
|
|||
var isFancyChatSame = FancySpeechBubblesCheckBox.Pressed == _cfg.GetCVar(CCVars.ChatEnableFancyBubbles);
|
||||
var isFancyBackgroundSame = FancyNameBackgroundsCheckBox.Pressed == _cfg.GetCVar(CCVars.ChatFancyNameBackground);
|
||||
var isReducedMotionSame = ReducedMotionCheckBox.Pressed == _cfg.GetCVar(CCVars.ReducedMotion);
|
||||
var isScreenShakeIntensitySame = Math.Abs(ScreenShakeIntensitySlider.Value / 100f - _cfg.GetCVar(CCVars.ScreenShakeIntensity)) < 0.01f;
|
||||
// var isToggleWalkSame = ToggleWalk.Pressed == _cfg.GetCVar(CCVars.ToggleWalk);
|
||||
var isStaticStorageUISame = StaticStorageUI.Pressed == _cfg.GetCVar(CCVars.StaticStorageUI);
|
||||
|
||||
|
|
@ -148,6 +160,7 @@ namespace Content.Client.Options.UI.Tabs
|
|||
isFancyChatSame &&
|
||||
isFancyBackgroundSame &&
|
||||
isReducedMotionSame &&
|
||||
isScreenShakeIntensitySame &&
|
||||
// isToggleWalkSame &&
|
||||
isStaticStorageUISame;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,12 @@
|
|||
using System.Numerics;
|
||||
using Content.Shared.Pointing.Components;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Content.Client.Pointing.Components;
|
||||
[RegisterComponent]
|
||||
public sealed partial class PointingArrowComponent : SharedPointingArrowComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// How long it takes to go from the bottom of the animation to the top.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("animationTime")]
|
||||
public float AnimationTime = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// How far it goes in any direction.
|
||||
/// How far the arrow moves up and down during the floating phase.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("offset")]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
using Content.Client.Pointing.Components;
|
||||
using Content.Shared.Pointing;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Shared.Animations;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Content.Client.Pointing;
|
||||
|
||||
public sealed partial class PointingSystem : SharedPointingSystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
|
||||
|
||||
public void InitializeVisualizer()
|
||||
{
|
||||
SubscribeLocalEvent<PointingArrowComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
||||
}
|
||||
|
||||
private void OnAnimationCompleted(EntityUid uid, PointingArrowComponent component, AnimationCompletedEvent args)
|
||||
{
|
||||
if (args.Key == component.AnimationKey)
|
||||
_animationPlayer.Stop(uid, component.AnimationKey);
|
||||
}
|
||||
|
||||
private void BeginPointAnimation(EntityUid uid, Vector2 startPosition, Vector2 offset, string animationKey)
|
||||
{
|
||||
if (_animationPlayer.HasRunningAnimation(uid, animationKey))
|
||||
return;
|
||||
|
||||
var animation = new Animation
|
||||
{
|
||||
Length = PointDuration,
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty
|
||||
{
|
||||
ComponentType = typeof(SpriteComponent),
|
||||
Property = nameof(SpriteComponent.Offset),
|
||||
InterpolationMode = AnimationInterpolationMode.Cubic,
|
||||
KeyFrames =
|
||||
{
|
||||
// We pad here to prevent improper looping and tighten the overshoot, just a touch
|
||||
new AnimationTrackProperty.KeyFrame(startPosition, 0f),
|
||||
new AnimationTrackProperty.KeyFrame(Vector2.Lerp(startPosition, offset, 0.9f), PointKeyTimeMove),
|
||||
new AnimationTrackProperty.KeyFrame(offset, PointKeyTimeMove),
|
||||
new AnimationTrackProperty.KeyFrame(Vector2.Zero, PointKeyTimeMove),
|
||||
new AnimationTrackProperty.KeyFrame(offset, PointKeyTimeHover),
|
||||
new AnimationTrackProperty.KeyFrame(Vector2.Zero, PointKeyTimeHover),
|
||||
new AnimationTrackProperty.KeyFrame(offset, PointKeyTimeHover),
|
||||
new AnimationTrackProperty.KeyFrame(Vector2.Zero, PointKeyTimeHover),
|
||||
new AnimationTrackProperty.KeyFrame(offset, PointKeyTimeHover),
|
||||
new AnimationTrackProperty.KeyFrame(Vector2.Zero, PointKeyTimeHover),
|
||||
new AnimationTrackProperty.KeyFrame(offset, PointKeyTimeHover),
|
||||
new AnimationTrackProperty.KeyFrame(Vector2.Zero, PointKeyTimeHover),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_animationPlayer.Play(uid, animation, animationKey);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +1,25 @@
|
|||
using Content.Client.Pointing.Components;
|
||||
using Content.Client.Gravity;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Pointing;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Utility;
|
||||
using DrawDepth = Content.Shared.DrawDepth.DrawDepth;
|
||||
|
||||
namespace Content.Client.Pointing;
|
||||
|
||||
public sealed class PointingSystem : SharedPointingSystem
|
||||
public sealed partial class PointingSystem : SharedPointingSystem
|
||||
{
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly FloatingVisualizerSystem _floatingSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<GetVerbsEvent<Verb>>(AddPointingVerb);
|
||||
SubscribeLocalEvent<PointingArrowComponent, ComponentStartup>(OnArrowStartup);
|
||||
SubscribeLocalEvent<PointingArrowComponent, AnimationCompletedEvent>(OnArrowAnimation);
|
||||
SubscribeLocalEvent<RoguePointingArrowComponent, ComponentStartup>(OnRogueArrowStartup);
|
||||
}
|
||||
SubscribeLocalEvent<PointingArrowComponent, ComponentHandleState>(HandleCompState);
|
||||
|
||||
private void OnArrowAnimation(EntityUid uid, PointingArrowComponent component, AnimationCompletedEvent args)
|
||||
{
|
||||
_floatingSystem.FloatAnimation(uid, component.Offset, component.AnimationKey, component.AnimationTime);
|
||||
InitializeVisualizer();
|
||||
}
|
||||
|
||||
private void AddPointingVerb(GetVerbsEvent<Verb> args)
|
||||
|
|
@ -38,15 +31,11 @@ public sealed class PointingSystem : SharedPointingSystem
|
|||
// I'm just adding this verb exclusively to clients so that the verb-loading pop-in on the verb menu isn't
|
||||
// as bad. Important for this verb seeing as its usually an option on just about any entity.
|
||||
|
||||
// this is a pointing arrow. no pointing here...
|
||||
if (HasComp<PointingArrowComponent>(args.Target))
|
||||
{
|
||||
// this is a pointing arrow. no pointing here...
|
||||
return;
|
||||
}
|
||||
|
||||
// Can the user point? Checking mob state directly instead of some action blocker, as many action blockers are blocked for
|
||||
// ghosts and there is no obvious choice for pointing (unless ghosts CanEmote?).
|
||||
if (_mobState.IsIncapacitated(args.User))
|
||||
if (!CanPoint(args.User))
|
||||
return;
|
||||
|
||||
// We won't check in range or visibility, as this verb is currently only executable via the context menu,
|
||||
|
|
@ -66,11 +55,9 @@ public sealed class PointingSystem : SharedPointingSystem
|
|||
private void OnArrowStartup(EntityUid uid, PointingArrowComponent component, ComponentStartup args)
|
||||
{
|
||||
if (TryComp<SpriteComponent>(uid, out var sprite))
|
||||
{
|
||||
sprite.DrawDepth = (int) DrawDepth.Overlays;
|
||||
}
|
||||
|
||||
_floatingSystem.FloatAnimation(uid, component.Offset, component.AnimationKey, component.AnimationTime);
|
||||
BeginPointAnimation(uid, component.StartPosition, component.Offset, component.AnimationKey);
|
||||
}
|
||||
|
||||
private void OnRogueArrowStartup(EntityUid uid, RoguePointingArrowComponent arrow, ComponentStartup args)
|
||||
|
|
@ -81,4 +68,13 @@ public sealed class PointingSystem : SharedPointingSystem
|
|||
sprite.NoRotation = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCompState(Entity<PointingArrowComponent> entity, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not SharedPointingArrowComponentState state)
|
||||
return;
|
||||
|
||||
entity.Comp.StartPosition = state.StartPosition;
|
||||
entity.Comp.EndTime = state.EndTime;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,8 +55,11 @@ namespace Content.Client.Popups
|
|||
.RemoveOverlay<PopupOverlay>();
|
||||
}
|
||||
|
||||
private void PopupMessage(string message, PopupType type, EntityCoordinates coordinates, EntityUid? entity, bool recordReplay)
|
||||
private void PopupMessage(string? message, PopupType type, EntityCoordinates coordinates, EntityUid? entity, bool recordReplay)
|
||||
{
|
||||
if (message == null)
|
||||
return;
|
||||
|
||||
if (recordReplay && _replayRecording.IsRecording)
|
||||
{
|
||||
if (entity != null)
|
||||
|
|
@ -75,25 +78,28 @@ namespace Content.Client.Popups
|
|||
}
|
||||
|
||||
#region Abstract Method Implementations
|
||||
public override void PopupCoordinates(string message, EntityCoordinates coordinates, PopupType type = PopupType.Small)
|
||||
public override void PopupCoordinates(string? message, EntityCoordinates coordinates, PopupType type = PopupType.Small)
|
||||
{
|
||||
PopupMessage(message, type, coordinates, null, true);
|
||||
}
|
||||
|
||||
public override void PopupCoordinates(string message, EntityCoordinates coordinates, ICommonSession recipient, PopupType type = PopupType.Small)
|
||||
public override void PopupCoordinates(string? message, EntityCoordinates coordinates, ICommonSession recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (_playerManager.LocalPlayer?.Session == recipient)
|
||||
if (_playerManager.LocalSession == recipient)
|
||||
PopupMessage(message, type, coordinates, null, true);
|
||||
}
|
||||
|
||||
public override void PopupCoordinates(string message, EntityCoordinates coordinates, EntityUid recipient, PopupType type = PopupType.Small)
|
||||
public override void PopupCoordinates(string? message, EntityCoordinates coordinates, EntityUid recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (_playerManager.LocalPlayer?.ControlledEntity == recipient)
|
||||
if (_playerManager.LocalEntity == recipient)
|
||||
PopupMessage(message, type, coordinates, null, true);
|
||||
}
|
||||
|
||||
private void PopupCursorInternal(string message, PopupType type, bool recordReplay)
|
||||
private void PopupCursorInternal(string? message, PopupType type, bool recordReplay)
|
||||
{
|
||||
if (message == null)
|
||||
return;
|
||||
|
||||
if (recordReplay && _replayRecording.IsRecording)
|
||||
_replayRecording.RecordClientMessage(new PopupCursorEvent(message, type));
|
||||
|
||||
|
|
@ -106,53 +112,53 @@ namespace Content.Client.Popups
|
|||
_aliveCursorLabels.Add(label);
|
||||
}
|
||||
|
||||
public override void PopupCursor(string message, PopupType type = PopupType.Small)
|
||||
public override void PopupCursor(string? message, PopupType type = PopupType.Small)
|
||||
=> PopupCursorInternal(message, type, true);
|
||||
|
||||
public override void PopupCursor(string message, ICommonSession recipient, PopupType type = PopupType.Small)
|
||||
public override void PopupCursor(string? message, ICommonSession recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (_playerManager.LocalPlayer?.Session == recipient)
|
||||
if (_playerManager.LocalSession == recipient)
|
||||
PopupCursor(message, type);
|
||||
}
|
||||
|
||||
public override void PopupCursor(string message, EntityUid recipient, PopupType type = PopupType.Small)
|
||||
public override void PopupCursor(string? message, EntityUid recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (_playerManager.LocalPlayer?.ControlledEntity == recipient)
|
||||
if (_playerManager.LocalEntity == recipient)
|
||||
PopupCursor(message, type);
|
||||
}
|
||||
|
||||
public override void PopupCoordinates(string message, EntityCoordinates coordinates, Filter filter, bool replayRecord, PopupType type = PopupType.Small)
|
||||
public override void PopupCoordinates(string? message, EntityCoordinates coordinates, Filter filter, bool replayRecord, PopupType type = PopupType.Small)
|
||||
{
|
||||
PopupCoordinates(message, coordinates, type);
|
||||
}
|
||||
|
||||
public override void PopupEntity(string message, EntityUid uid, EntityUid recipient, PopupType type = PopupType.Small)
|
||||
public override void PopupEntity(string? message, EntityUid uid, EntityUid recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (_playerManager.LocalPlayer?.ControlledEntity == recipient)
|
||||
if (_playerManager.LocalEntity == recipient)
|
||||
PopupEntity(message, uid, type);
|
||||
}
|
||||
|
||||
public override void PopupEntity(string message, EntityUid uid, ICommonSession recipient, PopupType type = PopupType.Small)
|
||||
public override void PopupEntity(string? message, EntityUid uid, ICommonSession recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (_playerManager.LocalPlayer?.Session == recipient)
|
||||
if (_playerManager.LocalSession == recipient)
|
||||
PopupEntity(message, uid, type);
|
||||
}
|
||||
|
||||
public override void PopupEntity(string message, EntityUid uid, Filter filter, bool recordReplay, PopupType type=PopupType.Small)
|
||||
public override void PopupEntity(string? message, EntityUid uid, Filter filter, bool recordReplay, PopupType type=PopupType.Small)
|
||||
{
|
||||
if (!filter.Recipients.Contains(_playerManager.LocalPlayer?.Session))
|
||||
if (!filter.Recipients.Contains(_playerManager.LocalSession))
|
||||
return;
|
||||
|
||||
PopupEntity(message, uid, type);
|
||||
}
|
||||
|
||||
public override void PopupClient(string message, EntityUid uid, EntityUid recipient, PopupType type = PopupType.Small)
|
||||
public override void PopupClient(string? message, EntityUid uid, EntityUid recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (_timing.IsFirstTimePredicted)
|
||||
PopupEntity(message, uid, recipient, type);
|
||||
}
|
||||
|
||||
public override void PopupEntity(string message, EntityUid uid, PopupType type = PopupType.Small)
|
||||
public override void PopupEntity(string? message, EntityUid uid, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (TryComp(uid, out TransformComponent? transform))
|
||||
PopupMessage(message, type, transform.Coordinates, uid, true);
|
||||
|
|
|
|||
|
|
@ -135,6 +135,14 @@ public sealed partial class GeneratorWindow : FancyWindow
|
|||
|
||||
private bool TryGetStartProgress(out float progress)
|
||||
{
|
||||
// Try to check progress of auto-revving first
|
||||
if (_entityManager.TryGetComponent<ActiveGeneratorRevvingComponent>(_entity, out var activeGeneratorRevvingComponent) && _entityManager.TryGetComponent<PortableGeneratorComponent>(_entity, out var portableGeneratorComponent))
|
||||
{
|
||||
var calculatedProgress = activeGeneratorRevvingComponent.CurrentTime / portableGeneratorComponent.StartTime;
|
||||
progress = (float) calculatedProgress;
|
||||
return true;
|
||||
}
|
||||
|
||||
var doAfterSystem = _entityManager.EntitySysManager.GetEntitySystem<DoAfterSystem>();
|
||||
return doAfterSystem.TryFindActiveDoAfter<GeneratorStartedEvent>(_entity, out _, out _, out progress);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,12 @@
|
|||
<Control HorizontalExpand="True"/>
|
||||
<OptionButton Name="CBackpackButton" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
<!-- Spawn Priority -->
|
||||
<BoxContainer HorizontalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-spawn-priority-label'}" />
|
||||
<Control HorizontalExpand="True"/>
|
||||
<OptionButton Name="CSpawnPriorityButton" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
<!-- Skin -->
|
||||
<BoxContainer Margin="10" HorizontalExpand="True" Orientation="Vertical">
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ namespace Content.Client.Preferences.UI
|
|||
private Slider _skinColor => CSkin;
|
||||
private OptionButton _clothingButton => CClothingButton;
|
||||
private OptionButton _backpackButton => CBackpackButton;
|
||||
private OptionButton _spawnPriorityButton => CSpawnPriorityButton;
|
||||
private SingleMarkingPicker _hairPicker => CHairStylePicker;
|
||||
private SingleMarkingPicker _facialHairPicker => CFacialHairPicker;
|
||||
private EyeColorPicker _eyesPicker => CEyeColorPicker;
|
||||
|
|
@ -340,6 +341,21 @@ namespace Content.Client.Preferences.UI
|
|||
|
||||
#endregion Backpack
|
||||
|
||||
#region SpawnPriority
|
||||
|
||||
foreach (var value in Enum.GetValues<SpawnPriorityPreference>())
|
||||
{
|
||||
_spawnPriorityButton.AddItem(Loc.GetString($"humanoid-profile-editor-preference-spawn-priority-{value.ToString().ToLower()}"), (int) value);
|
||||
}
|
||||
|
||||
_spawnPriorityButton.OnItemSelected += args =>
|
||||
{
|
||||
_spawnPriorityButton.SelectId(args.Id);
|
||||
SetSpawnPriority((SpawnPriorityPreference) args.Id);
|
||||
};
|
||||
|
||||
#endregion SpawnPriority
|
||||
|
||||
#region Eyes
|
||||
|
||||
_eyesPicker.OnEyeColorPicked += newColor =>
|
||||
|
|
@ -831,6 +847,12 @@ namespace Content.Client.Preferences.UI
|
|||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void SetSpawnPriority(SpawnPriorityPreference newSpawnPriority)
|
||||
{
|
||||
Profile = Profile?.WithSpawnPriorityPreference(newSpawnPriority);
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
IsDirty = false;
|
||||
|
|
@ -1006,6 +1028,16 @@ namespace Content.Client.Preferences.UI
|
|||
_backpackButton.SelectId((int) Profile.Backpack);
|
||||
}
|
||||
|
||||
private void UpdateSpawnPriorityControls()
|
||||
{
|
||||
if (Profile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_spawnPriorityButton.SelectId((int) Profile.SpawnPriority);
|
||||
}
|
||||
|
||||
private void UpdateHairPickers()
|
||||
{
|
||||
if (Profile == null)
|
||||
|
|
@ -1145,6 +1177,7 @@ namespace Content.Client.Preferences.UI
|
|||
UpdateSpecies();
|
||||
UpdateClothingControls();
|
||||
UpdateBackpackControls();
|
||||
UpdateSpawnPriorityControls();
|
||||
UpdateAgeEdit();
|
||||
UpdateEyePickers();
|
||||
UpdateSaveButton();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Content.Shared.Antag;
|
||||
using Content.Shared.Revolutionary.Components;
|
||||
using Content.Client.Antag;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
|
||||
namespace Content.Client.Revolutionary;
|
||||
|
|
@ -7,29 +8,37 @@ namespace Content.Client.Revolutionary;
|
|||
/// <summary>
|
||||
/// Used for the client to get status icons from other revs.
|
||||
/// </summary>
|
||||
public sealed class RevolutionarySystem : AntagStatusIconSystem<RevolutionaryComponent>
|
||||
public sealed class RevolutionarySystem : EntitySystem
|
||||
{
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RevolutionaryComponent, GetStatusIconsEvent>(GetRevIcon);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, GetStatusIconsEvent>(GetHeadRevIcon);
|
||||
SubscribeLocalEvent<RevolutionaryComponent, CanDisplayStatusIconsEvent>(OnCanShowRevIcon);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, CanDisplayStatusIconsEvent>(OnCanShowRevIcon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the person who triggers the GetStatusIcon event is also a Rev or a HeadRev.
|
||||
/// Determine whether a client should display the rev icon.
|
||||
/// </summary>
|
||||
private void GetRevIcon(EntityUid uid, RevolutionaryComponent comp, ref GetStatusIconsEvent args)
|
||||
private void OnCanShowRevIcon<T>(EntityUid uid, T comp, ref CanDisplayStatusIconsEvent args) where T : IAntagStatusIconComponent
|
||||
{
|
||||
if (!HasComp<HeadRevolutionaryComponent>(uid))
|
||||
{
|
||||
GetStatusIcon(comp.RevStatusIcon, ref args);
|
||||
}
|
||||
args.Cancelled = !CanDisplayIcon(args.User, comp.IconVisibleToGhost);
|
||||
}
|
||||
|
||||
private void GetHeadRevIcon(EntityUid uid, HeadRevolutionaryComponent comp, ref GetStatusIconsEvent args)
|
||||
/// <summary>
|
||||
/// The criteria that determine whether a client should see Rev/Head rev icons.
|
||||
/// </summary>
|
||||
private bool CanDisplayIcon(EntityUid? uid, bool visibleToGhost)
|
||||
{
|
||||
GetStatusIcon(comp.HeadRevStatusIcon, ref args);
|
||||
if (HasComp<HeadRevolutionaryComponent>(uid) || HasComp<RevolutionaryComponent>(uid))
|
||||
return true;
|
||||
|
||||
if (visibleToGhost && HasComp<GhostComponent>(uid))
|
||||
return true;
|
||||
|
||||
return HasComp<ShowRevIconsComponent>(uid);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ namespace Content.Client.RoundEnd
|
|||
|
||||
Contents.AddChild(roundEndTabs);
|
||||
|
||||
OpenCentered();
|
||||
OpenCenteredRight();
|
||||
MoveToFront();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.NPC;
|
||||
using Content.Shared.SSDIndicator;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
|
|
@ -14,6 +17,7 @@ public sealed class SSDIndicatorSystem : EntitySystem
|
|||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -24,11 +28,15 @@ public sealed class SSDIndicatorSystem : EntitySystem
|
|||
|
||||
private void OnGetStatusIcon(EntityUid uid, SSDIndicatorComponent component, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (!component.IsSSD ||
|
||||
!_cfg.GetCVar(CCVars.ICShowSSDIndicator) ||
|
||||
args.InContainer)
|
||||
return;
|
||||
|
||||
args.StatusIcons.Add(_prototype.Index<StatusIconPrototype>(component.Icon));
|
||||
if (component.IsSSD &&
|
||||
_cfg.GetCVar(CCVars.ICShowSSDIndicator) &&
|
||||
!args.InContainer &&
|
||||
!_mobState.IsDead(uid) &&
|
||||
!HasComp<ActiveNPCComponent>(uid) &&
|
||||
TryComp<MindContainerComponent>(uid, out var mindContainer) &&
|
||||
mindContainer.ShowExamineInfo)
|
||||
{
|
||||
args.StatusIcons.Add(_prototype.Index<StatusIconPrototype>(component.Icon));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,29 +14,31 @@ public sealed class SprayPainterSystem : SharedSprayPainterSystem
|
|||
|
||||
public List<SprayPainterEntry> Entries { get; private set; } = new();
|
||||
|
||||
public override void Initialize()
|
||||
protected override void CacheStyles()
|
||||
{
|
||||
base.Initialize();
|
||||
base.CacheStyles();
|
||||
|
||||
foreach (string style in Styles)
|
||||
Entries.Clear();
|
||||
foreach (var style in Styles)
|
||||
{
|
||||
var name = style.Name;
|
||||
string? iconPath = Groups
|
||||
.FindAll(x => x.StylePaths.ContainsKey(style))?
|
||||
.MaxBy(x => x.IconPriority)?.StylePaths[style];
|
||||
.FindAll(x => x.StylePaths.ContainsKey(name))?
|
||||
.MaxBy(x => x.IconPriority)?.StylePaths[name];
|
||||
if (iconPath == null)
|
||||
{
|
||||
Entries.Add(new SprayPainterEntry(style, null));
|
||||
Entries.Add(new SprayPainterEntry(name, null));
|
||||
continue;
|
||||
}
|
||||
|
||||
RSIResource doorRsi = _resourceCache.GetResource<RSIResource>(SpriteSpecifierSerializer.TextureRoot / new ResPath(iconPath));
|
||||
if (!doorRsi.RSI.TryGetState("closed", out var icon))
|
||||
{
|
||||
Entries.Add(new SprayPainterEntry(style, null));
|
||||
Entries.Add(new SprayPainterEntry(name, null));
|
||||
continue;
|
||||
}
|
||||
|
||||
Entries.Add(new SprayPainterEntry(style, icon.Frame0));
|
||||
Entries.Add(new SprayPainterEntry(name, icon.Frame0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared.SprayPainter;
|
||||
using Content.Shared.SprayPainter.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
|
|
@ -20,14 +21,20 @@ public sealed class SprayPainterBoundUserInterface : BoundUserInterface
|
|||
{
|
||||
base.Open();
|
||||
|
||||
if (!EntMan.TryGetComponent<SprayPainterComponent>(Owner, out var comp))
|
||||
return;
|
||||
|
||||
_window = new SprayPainterWindow();
|
||||
|
||||
_painter = EntMan.System<SprayPainterSystem>();
|
||||
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
_window.OnSpritePicked = OnSpritePicked;
|
||||
_window.OnColorPicked = OnColorPicked;
|
||||
|
||||
_window.Populate(_painter.Entries, comp.Index, comp.PickedColor, comp.ColorPalette);
|
||||
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
|
@ -37,25 +44,6 @@ public sealed class SprayPainterBoundUserInterface : BoundUserInterface
|
|||
_window?.Dispose();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (_window == null)
|
||||
return;
|
||||
|
||||
if (_painter == null)
|
||||
return;
|
||||
|
||||
if (state is not SprayPainterBoundUserInterfaceState stateCast)
|
||||
return;
|
||||
|
||||
_window.Populate(_painter.Entries,
|
||||
stateCast.SelectedStyle,
|
||||
stateCast.SelectedColorKey,
|
||||
stateCast.Palette);
|
||||
}
|
||||
|
||||
private void OnSpritePicked(ItemList.ItemListSelectedEventArgs args)
|
||||
{
|
||||
SendMessage(new SprayPainterSpritePickedMessage(args.ItemIndex));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using Content.Shared.StationRecords;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.StationRecords;
|
||||
|
||||
|
|
@ -17,33 +16,21 @@ public sealed class GeneralStationRecordConsoleBoundUserInterface : BoundUserInt
|
|||
base.Open();
|
||||
|
||||
_window = new();
|
||||
_window.OnKeySelected += OnKeySelected;
|
||||
_window.OnFiltersChanged += OnFiltersChanged;
|
||||
_window.OnKeySelected += key =>
|
||||
SendMessage(new SelectStationRecord(key));
|
||||
_window.OnFiltersChanged += (type, filterValue) =>
|
||||
SendMessage(new SetStationRecordFilter(type, filterValue));
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
private void OnKeySelected((NetEntity, uint)? key)
|
||||
{
|
||||
SendMessage(new SelectGeneralStationRecord(key));
|
||||
}
|
||||
|
||||
private void OnFiltersChanged(
|
||||
GeneralStationRecordFilterType type, string filterValue)
|
||||
{
|
||||
GeneralStationRecordsFilterMsg msg = new(type, filterValue);
|
||||
SendMessage(msg);
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not GeneralStationRecordConsoleState cast)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_window?.UpdateState(cast);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Linq;
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
|
|
@ -11,31 +10,29 @@ namespace Content.Client.StationRecords;
|
|||
[GenerateTypedNameReferences]
|
||||
public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
||||
{
|
||||
public Action<(NetEntity, uint)?>? OnKeySelected;
|
||||
public Action<uint?>? OnKeySelected;
|
||||
|
||||
public Action<GeneralStationRecordFilterType, string>? OnFiltersChanged;
|
||||
public Action<StationRecordFilterType, string>? OnFiltersChanged;
|
||||
|
||||
private bool _isPopulating;
|
||||
|
||||
private GeneralStationRecordFilterType _currentFilterType;
|
||||
private StationRecordFilterType _currentFilterType;
|
||||
|
||||
public GeneralStationRecordConsoleWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
_currentFilterType = GeneralStationRecordFilterType.Name;
|
||||
_currentFilterType = StationRecordFilterType.Name;
|
||||
|
||||
foreach (var item in Enum.GetValues<GeneralStationRecordFilterType>())
|
||||
foreach (var item in Enum.GetValues<StationRecordFilterType>())
|
||||
{
|
||||
StationRecordsFilterType.AddItem(GetTypeFilterLocals(item), (int)item);
|
||||
}
|
||||
|
||||
RecordListing.OnItemSelected += args =>
|
||||
{
|
||||
if (_isPopulating || RecordListing[args.ItemIndex].Metadata is not ValueTuple<NetEntity, uint> cast)
|
||||
{
|
||||
if (_isPopulating || RecordListing[args.ItemIndex].Metadata is not uint cast)
|
||||
return;
|
||||
}
|
||||
|
||||
OnKeySelected?.Invoke(cast);
|
||||
};
|
||||
|
|
@ -48,7 +45,7 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
|||
|
||||
StationRecordsFilterType.OnItemSelected += eventArgs =>
|
||||
{
|
||||
var type = (GeneralStationRecordFilterType)eventArgs.Id;
|
||||
var type = (StationRecordFilterType) eventArgs.Id;
|
||||
|
||||
if (_currentFilterType != type)
|
||||
{
|
||||
|
|
@ -123,7 +120,7 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
|||
RecordContainer.RemoveAllChildren();
|
||||
}
|
||||
}
|
||||
private void PopulateRecordListing(Dictionary<(NetEntity, uint), string> listing, (NetEntity, uint)? selected)
|
||||
private void PopulateRecordListing(Dictionary<uint, string> listing, uint? selected)
|
||||
{
|
||||
RecordListing.Clear();
|
||||
RecordListing.ClearSelected();
|
||||
|
|
@ -134,10 +131,7 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
|||
{
|
||||
var item = RecordListing.AddItem(name);
|
||||
item.Metadata = key;
|
||||
if (selected != null && key.Item1 == selected.Value.Item1 && key.Item2 == selected.Value.Item2)
|
||||
{
|
||||
item.Selected = true;
|
||||
}
|
||||
item.Selected = key == selected;
|
||||
}
|
||||
_isPopulating = false;
|
||||
|
||||
|
|
@ -197,7 +191,7 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
|||
}
|
||||
}
|
||||
|
||||
private string GetTypeFilterLocals(GeneralStationRecordFilterType type)
|
||||
private string GetTypeFilterLocals(StationRecordFilterType type)
|
||||
{
|
||||
return Loc.GetString($"general-station-record-{type.ToString().ToLower()}-filter");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ public sealed class EntityStorageSystem : SharedEntityStorageSystem
|
|||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<EntityStorageComponent, EntityUnpausedEvent>(OnEntityUnpausedEvent);
|
||||
SubscribeLocalEvent<EntityStorageComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<EntityStorageComponent, ComponentStartup>(OnComponentStartup);
|
||||
SubscribeLocalEvent<EntityStorageComponent, ActivateInWorldEvent>(OnInteract, after: new[] { typeof(LockSystem) });
|
||||
|
|
|
|||
|
|
@ -48,6 +48,11 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
|
|||
{
|
||||
SendMessage(new StoreRequestUpdateInterfaceMessage());
|
||||
};
|
||||
|
||||
_menu.OnRefundAttempt += (_) =>
|
||||
{
|
||||
SendMessage(new StoreRequestRefundMessage());
|
||||
};
|
||||
}
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
|
|
@ -64,6 +69,7 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
|
|||
|
||||
_menu.UpdateListing(msg.Listings.ToList());
|
||||
_menu.SetFooterVisibility(msg.ShowFooter);
|
||||
_menu.UpdateRefund(msg.AllowRefund);
|
||||
break;
|
||||
case StoreInitializeState msg:
|
||||
_windowName = msg.Name;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@
|
|||
MinWidth="64"
|
||||
HorizontalAlignment="Right"
|
||||
Text="{Loc 'store-ui-default-withdraw-text'}" />
|
||||
<Button
|
||||
Name="RefundButton"
|
||||
MinWidth="64"
|
||||
HorizontalAlignment="Right"
|
||||
Text="Refund" />
|
||||
</BoxContainer>
|
||||
<PanelContainer VerticalExpand="True">
|
||||
<PanelContainer.PanelOverride>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ public sealed partial class StoreMenu : DefaultWindow
|
|||
public event Action<BaseButton.ButtonEventArgs, string>? OnCategoryButtonPressed;
|
||||
public event Action<BaseButton.ButtonEventArgs, string, int>? OnWithdrawAttempt;
|
||||
public event Action<BaseButton.ButtonEventArgs>? OnRefreshButtonPressed;
|
||||
public event Action<BaseButton.ButtonEventArgs>? OnRefundAttempt;
|
||||
|
||||
public Dictionary<string, FixedPoint2> Balance = new();
|
||||
public string CurrentCategory = string.Empty;
|
||||
|
|
@ -44,6 +45,8 @@ public sealed partial class StoreMenu : DefaultWindow
|
|||
|
||||
WithdrawButton.OnButtonDown += OnWithdrawButtonDown;
|
||||
RefreshButton.OnButtonDown += OnRefreshButtonDown;
|
||||
RefundButton.OnButtonDown += OnRefundButtonDown;
|
||||
|
||||
if (Window != null)
|
||||
Window.Title = name;
|
||||
}
|
||||
|
|
@ -116,6 +119,11 @@ public sealed partial class StoreMenu : DefaultWindow
|
|||
_withdrawWindow.OnWithdrawAttempt += OnWithdrawAttempt;
|
||||
}
|
||||
|
||||
private void OnRefundButtonDown(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
OnRefundAttempt?.Invoke(args);
|
||||
}
|
||||
|
||||
private void AddListingGui(ListingData listing)
|
||||
{
|
||||
if (!listing.Categories.Contains(CurrentCategory))
|
||||
|
|
@ -262,6 +270,11 @@ public sealed partial class StoreMenu : DefaultWindow
|
|||
_withdrawWindow?.Close();
|
||||
}
|
||||
|
||||
public void UpdateRefund(bool allowRefund)
|
||||
{
|
||||
RefundButton.Disabled = !allowRefund;
|
||||
}
|
||||
|
||||
private sealed class StoreCategoryButton : Button
|
||||
{
|
||||
public string? Id;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ namespace Content.Client.Stylesheets
|
|||
{
|
||||
$"/Fonts/NotoSans{ds}/NotoSans{ds}-{variation}.ttf",
|
||||
$"/Fonts/NotoSans/NotoSansSymbols-{sv}.ttf",
|
||||
"/Fonts/NotoSans/NotoSansSymbols2-Regular.ttf",
|
||||
"/Fonts/NotoSans/NotoSansSymbols2-Regular.ttf"
|
||||
},
|
||||
size
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
using Content.Shared.Throwing;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Animations;
|
||||
|
||||
namespace Content.Client.Throwing;
|
||||
|
||||
/// <summary>
|
||||
/// Handles animating thrown items.
|
||||
/// </summary>
|
||||
public sealed class ThrownItemVisualizerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _anim = default!;
|
||||
|
||||
private const string AnimationKey = "thrown-item";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ThrownItemComponent, AfterAutoHandleStateEvent>(OnAutoHandleState);
|
||||
SubscribeLocalEvent<ThrownItemComponent, ComponentShutdown>(OnShutdown);
|
||||
}
|
||||
|
||||
private void OnAutoHandleState(EntityUid uid, ThrownItemComponent component, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
var animationPlayer = EnsureComp<AnimationPlayerComponent>(uid);
|
||||
|
||||
if (_anim.HasRunningAnimation(uid, animationPlayer, AnimationKey))
|
||||
return;
|
||||
|
||||
var anim = GetAnimation((uid, component, sprite));
|
||||
if (anim == null)
|
||||
return;
|
||||
|
||||
component.OriginalScale = sprite.Scale;
|
||||
_anim.Play((uid, animationPlayer), anim, AnimationKey);
|
||||
}
|
||||
|
||||
private void OnShutdown(EntityUid uid, ThrownItemComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (!_anim.HasRunningAnimation(uid, AnimationKey))
|
||||
return;
|
||||
|
||||
if (TryComp<SpriteComponent>(uid, out var sprite) && component.OriginalScale != null)
|
||||
sprite.Scale = component.OriginalScale.Value;
|
||||
|
||||
_anim.Stop(uid, AnimationKey);
|
||||
}
|
||||
|
||||
private static Animation? GetAnimation(Entity<ThrownItemComponent, SpriteComponent> ent)
|
||||
{
|
||||
if (ent.Comp1.LandTime - ent.Comp1.ThrownTime is not { } length)
|
||||
return null;
|
||||
|
||||
if (length <= TimeSpan.Zero)
|
||||
return null;
|
||||
|
||||
length += TimeSpan.FromSeconds(ThrowingSystem.FlyTime);
|
||||
var scale = ent.Comp2.Scale;
|
||||
var lenFloat = (float) length.TotalSeconds;
|
||||
|
||||
// TODO use like actual easings here
|
||||
return new Animation
|
||||
{
|
||||
Length = length,
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty
|
||||
{
|
||||
ComponentType = typeof(SpriteComponent),
|
||||
Property = nameof(SpriteComponent.Scale),
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(scale, 0.0f),
|
||||
new AnimationTrackProperty.KeyFrame(scale * 1.4f, lenFloat * 0.25f),
|
||||
new AnimationTrackProperty.KeyFrame(scale, lenFloat * 0.75f)
|
||||
},
|
||||
InterpolationMode = AnimationInterpolationMode.Linear
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -17,11 +17,11 @@ public sealed class WelderStatusControl : Control
|
|||
private readonly ItemToggleComponent? _toggleComponent;
|
||||
private readonly RichTextLabel _label;
|
||||
|
||||
public WelderStatusControl(WelderComponent parent, EntityUid? uid = null)
|
||||
public WelderStatusControl(Entity<WelderComponent> parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_entMan = IoCManager.Resolve<IEntityManager>();
|
||||
if (_entMan.TryGetComponent<ItemToggleComponent>(uid, out var itemToggle))
|
||||
if (_entMan.TryGetComponent<ItemToggleComponent>(parent, out var itemToggle))
|
||||
_toggleComponent = itemToggle;
|
||||
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||
AddChild(_label);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<controls:FancyWindow xmlns="https://spacestation14.io" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls">
|
||||
<BoxContainer Orientation="Vertical" Margin="8">
|
||||
<BoxContainer Name="Prompts" Orientation="Vertical"/> <!-- Populated in constructor -->
|
||||
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<Button Name="OkButton" Text="{Loc 'quick-dialog-ui-ok'}"/>
|
||||
<Button Name="CancelButton" Text="{Loc 'quick-dialog-ui-cancel'}"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
using Content.Shared.Administration;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.UserInterface.Controls;
|
||||
|
||||
// mfw they ported input() from BYOND
|
||||
|
||||
/// <summary>
|
||||
/// Client-side dialog with multiple prompts.
|
||||
/// Used by admin tools quick dialog system among other things.
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class DialogWindow : FancyWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// Action for when the ok button is pressed or the last field has enter pressed.
|
||||
/// Results maps prompt FieldIds to the LineEdit's text contents.
|
||||
/// </summary>
|
||||
public Action<Dictionary<string, string>>? OnConfirmed;
|
||||
|
||||
/// <summary>
|
||||
/// Action for when the cancel button is pressed or the window is closed.
|
||||
/// </summary>
|
||||
public Action? OnCancelled;
|
||||
|
||||
/// <summary>
|
||||
/// Used to ensure that only one output action is invoked.
|
||||
/// E.g. Pressing cancel will invoke then close the window, but OnClose will not invoke.
|
||||
/// </summary>
|
||||
private bool _finished;
|
||||
|
||||
private List<(string, LineEdit)> _promptLines;
|
||||
|
||||
/// <summary>
|
||||
/// Create and open a new dialog with some prompts.
|
||||
/// </summary>
|
||||
/// <param name="title">String to use for the window title.</param>
|
||||
/// <param name="entries">Quick dialog entries to create prompts with.</param>
|
||||
/// <param name="ok">Whether to have an Ok button.</param>
|
||||
/// <param name="cancel">Whether to have a Cancel button. Closing the window will still cancel it.</param>
|
||||
/// <remarks>
|
||||
/// Won't do anything on its own, you need to handle or network with <see cref="OnConfirmed"/> and <see cref="OnCancelled"/>.
|
||||
/// </remarks>
|
||||
public DialogWindow(string title, List<QuickDialogEntry> entries, bool ok = true, bool cancel = true)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
Title = title;
|
||||
|
||||
OkButton.Visible = ok;
|
||||
CancelButton.Visible = cancel;
|
||||
|
||||
_promptLines = new(entries.Count);
|
||||
|
||||
for (int i = 0; i < entries.Count; i++)
|
||||
{
|
||||
var entry = entries[i];
|
||||
|
||||
var box = new BoxContainer();
|
||||
box.AddChild(new Label() { Text = entry.Prompt, HorizontalExpand = true, SizeFlagsStretchRatio = 0.5f });
|
||||
|
||||
var edit = new LineEdit() { HorizontalExpand = true };
|
||||
|
||||
(Func<string, bool>, string) pair = entry.Type switch
|
||||
{
|
||||
QuickDialogEntryType.Integer => (VerifyInt, "integer"),
|
||||
QuickDialogEntryType.Float => (VerifyFloat, "float"),
|
||||
QuickDialogEntryType.ShortText => (VerifyShortText, "short-text"),
|
||||
QuickDialogEntryType.LongText => (VerifyLongText, "long-text"),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
var (valid, name) = pair;
|
||||
|
||||
edit.IsValid += valid;
|
||||
// try use placeholder from the caller, fall back to the generic one for whatever type is being validated.
|
||||
edit.PlaceHolder = entry.Placeholder ?? Loc.GetString($"quick-dialog-ui-{name}");
|
||||
|
||||
// Last text box gets enter confirmation.
|
||||
// Only the last so you don't accidentally confirm early.
|
||||
if (i == entries.Count - 1)
|
||||
edit.OnTextEntered += _ => Confirm();
|
||||
|
||||
_promptLines.Add((entry.FieldId, edit));
|
||||
box.AddChild(edit);
|
||||
Prompts.AddChild(box);
|
||||
}
|
||||
|
||||
OkButton.OnPressed += _ => Confirm();
|
||||
|
||||
CancelButton.OnPressed += _ =>
|
||||
{
|
||||
_finished = true;
|
||||
OnCancelled?.Invoke();
|
||||
Close();
|
||||
};
|
||||
|
||||
OnClose += () =>
|
||||
{
|
||||
if (!_finished)
|
||||
OnCancelled?.Invoke();
|
||||
};
|
||||
|
||||
MinWidth *= 2; // Just double it.
|
||||
|
||||
OpenCentered();
|
||||
}
|
||||
|
||||
private void Confirm()
|
||||
{
|
||||
var results = new Dictionary<string, string>();
|
||||
foreach (var (field, edit) in _promptLines)
|
||||
{
|
||||
results[field] = edit.Text;
|
||||
}
|
||||
|
||||
_finished = true;
|
||||
OnConfirmed?.Invoke(results);
|
||||
Close();
|
||||
}
|
||||
|
||||
#region Input validation
|
||||
|
||||
|
||||
private bool VerifyInt(string input)
|
||||
{
|
||||
return int.TryParse(input, out var _);
|
||||
}
|
||||
|
||||
private bool VerifyFloat(string input)
|
||||
{
|
||||
return float.TryParse(input, out var _);
|
||||
}
|
||||
|
||||
private bool VerifyShortText(string input)
|
||||
{
|
||||
return input.Length <= 100;
|
||||
}
|
||||
|
||||
private bool VerifyLongText(string input)
|
||||
{
|
||||
return input.Length <= 2000;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
using Content.Shared.Vehicle;
|
||||
using Content.Shared.Vehicle.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Vehicle;
|
||||
|
||||
public sealed class VehicleSystem : SharedVehicleSystem
|
||||
{
|
||||
[Dependency] private EyeSystem _eye = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RiderComponent, ComponentStartup>(OnRiderStartup);
|
||||
SubscribeLocalEvent<RiderComponent, ComponentShutdown>(OnRiderShutdown);
|
||||
SubscribeLocalEvent<RiderComponent, ComponentHandleState>(OnRiderHandleState);
|
||||
SubscribeLocalEvent<VehicleComponent, AppearanceChangeEvent>(OnVehicleAppearanceChange);
|
||||
}
|
||||
|
||||
private void OnRiderStartup(EntityUid uid, RiderComponent component, ComponentStartup args)
|
||||
{
|
||||
// Center the player's eye on the vehicle
|
||||
if (TryComp(uid, out EyeComponent? eyeComp))
|
||||
{
|
||||
_eye.SetTarget(uid, eyeComp.Target ?? component.Vehicle, eyeComp);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRiderShutdown(EntityUid uid, RiderComponent component, ComponentShutdown args)
|
||||
{
|
||||
// reset the riders eye centering.
|
||||
if (TryComp(uid, out EyeComponent? eyeComp))
|
||||
{
|
||||
_eye.SetTarget(uid, null, eyeComp);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRiderHandleState(EntityUid uid, RiderComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not RiderComponentState state)
|
||||
return;
|
||||
|
||||
var entity = EnsureEntity<RiderComponent>(state.Entity, uid);
|
||||
|
||||
if (TryComp(uid, out EyeComponent? eyeComp) && eyeComp.Target == component.Vehicle)
|
||||
{
|
||||
_eye.SetTarget(uid, entity, eyeComp);
|
||||
}
|
||||
|
||||
component.Vehicle = entity;
|
||||
}
|
||||
|
||||
private void OnVehicleAppearanceChange(EntityUid uid, VehicleComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if (component.HideRider
|
||||
&& Appearance.TryGetData<bool>(uid, VehicleVisuals.HideRider, out var hide, args.Component)
|
||||
&& TryComp<SpriteComponent>(component.LastRider, out var riderSprite))
|
||||
riderSprite.Visible = !hide;
|
||||
|
||||
// First check is for the sprite itself
|
||||
if (Appearance.TryGetData<int>(uid, VehicleVisuals.DrawDepth, out var drawDepth, args.Component))
|
||||
args.Sprite.DrawDepth = drawDepth;
|
||||
|
||||
// Set vehicle layer to animated or not (i.e. are the wheels turning or not)
|
||||
if (component.AutoAnimate
|
||||
&& Appearance.TryGetData<bool>(uid, VehicleVisuals.AutoAnimate, out var autoAnimate, args.Component))
|
||||
args.Sprite.LayerSetAutoAnimated(VehicleVisualLayers.AutoAnimate, autoAnimate);
|
||||
}
|
||||
}
|
||||
|
||||
public enum VehicleVisualLayers : byte
|
||||
{
|
||||
/// Layer for the vehicle's wheels
|
||||
AutoAnimate,
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ namespace Content.Client.VendingMachines
|
|||
|
||||
_menu.Populate(_cachedInventory, out _cachedFilteredIndex);
|
||||
|
||||
_menu.OpenCentered();
|
||||
_menu.OpenCenteredLeft();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
|
|
|
|||
|
|
@ -56,11 +56,11 @@ public sealed class GunSpreadOverlay : Overlay
|
|||
return;
|
||||
|
||||
// (☞゚ヮ゚)☞
|
||||
var maxSpread = gun.MaxAngle;
|
||||
var minSpread = gun.MinAngle;
|
||||
var maxSpread = gun.MaxAngleModified;
|
||||
var minSpread = gun.MinAngleModified;
|
||||
var timeSinceLastFire = (_timing.CurTime - gun.NextFire).TotalSeconds;
|
||||
var currentAngle = new Angle(MathHelper.Clamp(gun.CurrentAngle.Theta - gun.AngleDecay.Theta * timeSinceLastFire,
|
||||
gun.MinAngle.Theta, gun.MaxAngle.Theta));
|
||||
var currentAngle = new Angle(MathHelper.Clamp(gun.CurrentAngle.Theta - gun.AngleDecayModified.Theta * timeSinceLastFire,
|
||||
gun.MinAngleModified.Theta, gun.MaxAngleModified.Theta));
|
||||
var direction = (mousePos.Position - mapPos.Position);
|
||||
|
||||
worldHandle.DrawLine(mapPos.Position, mousePos.Position + direction, Color.Orange);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ using Content.Client.Items;
|
|||
using Content.Client.Weapons.Ranged.Components;
|
||||
using Content.Shared.Camera;
|
||||
using Content.Shared.CombatMode;
|
||||
using Robust.Shared.Spawners;
|
||||
using Content.Shared.Weapons.Ranged;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Content.Shared.Weapons.Ranged.Events;
|
||||
|
|
@ -195,7 +194,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||
{
|
||||
if (throwItems)
|
||||
{
|
||||
Recoil(user, direction, gun.CameraRecoilScalar);
|
||||
Recoil(user, direction, gun.CameraRecoilScalarModified);
|
||||
if (IsClientSide(ent!.Value))
|
||||
Del(ent.Value);
|
||||
else
|
||||
|
|
@ -210,8 +209,8 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||
{
|
||||
SetCartridgeSpent(ent!.Value, cartridge, true);
|
||||
MuzzleFlash(gunUid, cartridge, user);
|
||||
Audio.PlayPredicted(gun.SoundGunshot, gunUid, user);
|
||||
Recoil(user, direction, gun.CameraRecoilScalar);
|
||||
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
||||
Recoil(user, direction, gun.CameraRecoilScalarModified);
|
||||
// TODO: Can't predict entity deletions.
|
||||
//if (cartridge.DeleteOnSpawn)
|
||||
// Del(cartridge.Owner);
|
||||
|
|
@ -228,16 +227,16 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||
break;
|
||||
case AmmoComponent newAmmo:
|
||||
MuzzleFlash(gunUid, newAmmo, user);
|
||||
Audio.PlayPredicted(gun.SoundGunshot, gunUid, user);
|
||||
Recoil(user, direction, gun.CameraRecoilScalar);
|
||||
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
||||
Recoil(user, direction, gun.CameraRecoilScalarModified);
|
||||
if (IsClientSide(ent!.Value))
|
||||
Del(ent.Value);
|
||||
else
|
||||
RemoveShootable(ent.Value);
|
||||
break;
|
||||
case HitscanPrototype:
|
||||
Audio.PlayPredicted(gun.SoundGunshot, gunUid, user);
|
||||
Recoil(user, direction, gun.CameraRecoilScalar);
|
||||
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
||||
Recoil(user, direction, gun.CameraRecoilScalarModified);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using System.Linq;
|
||||
using Content.Client.Antag;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Content.Shared.Zombies;
|
||||
|
|
@ -7,15 +7,14 @@ using Robust.Client.GameObjects;
|
|||
|
||||
namespace Content.Client.Zombies;
|
||||
|
||||
public sealed class ZombieSystem : AntagStatusIconSystem<ZombieComponent>
|
||||
public sealed class ZombieSystem : EntitySystem
|
||||
{
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ZombieComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<ZombieComponent, GetStatusIconsEvent>(OnGetStatusIcon);
|
||||
SubscribeLocalEvent<ZombieComponent, CanDisplayStatusIconsEvent>(OnCanDisplayStatusIcons);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, ZombieComponent component, ComponentStartup args)
|
||||
|
|
@ -32,8 +31,17 @@ public sealed class ZombieSystem : AntagStatusIconSystem<ZombieComponent>
|
|||
}
|
||||
}
|
||||
|
||||
private void OnGetStatusIcon(EntityUid uid, ZombieComponent component, ref GetStatusIconsEvent args)
|
||||
/// <summary>
|
||||
/// Determines whether a player should be able to see the StatusIcon for zombies.
|
||||
/// </summary>
|
||||
private void OnCanDisplayStatusIcons(EntityUid uid, ZombieComponent component, ref CanDisplayStatusIconsEvent args)
|
||||
{
|
||||
GetStatusIcon(component.ZombieStatusIcon, ref args);
|
||||
if (HasComp<ZombieComponent>(args.User) || HasComp<ShowZombieIconsComponent>(args.User))
|
||||
return;
|
||||
|
||||
if (component.IconVisibleToGhost && HasComp<GhostComponent>(args.User))
|
||||
return;
|
||||
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,10 @@ public sealed class SecretStartsTest
|
|||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
gameTicker.StartGameRule("Secret");
|
||||
// this mimics roundflow:
|
||||
// rules added, then round starts
|
||||
gameTicker.AddGameRule("Secret");
|
||||
gameTicker.StartGamePresetRules();
|
||||
});
|
||||
|
||||
// Wait three ticks for any random update loops that might happen
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Content.IntegrationTests.Tests.Interaction;
|
||||
using Content.Server.Explosion.Components;
|
||||
using Content.Shared.Explosion.Components;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ namespace Content.IntegrationTests.Tests.Preferences
|
|||
),
|
||||
ClothingPreference.Jumpskirt,
|
||||
BackpackPreference.Backpack,
|
||||
SpawnPriorityPreference.None,
|
||||
new Dictionary<string, JobPriority>
|
||||
{
|
||||
{SharedGameTicker.FallbackOverflowJob, JobPriority.High}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Prototypes;
|
||||
using Content.Shared.Pulling.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Puller;
|
||||
|
||||
#nullable enable
|
||||
|
||||
[TestFixture]
|
||||
public sealed class PullerTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks that needsHands on PullerComponent is not set on mobs that don't even have hands.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task PullerSanityTest()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var compFactory = server.ResolveDependency<IComponentFactory>();
|
||||
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
foreach (var proto in protoManager.EnumeratePrototypes<EntityPrototype>())
|
||||
{
|
||||
if (!proto.TryGetComponent(out SharedPullerComponent? puller))
|
||||
continue;
|
||||
|
||||
if (!puller.NeedsHands)
|
||||
continue;
|
||||
|
||||
Assert.That(proto.HasComponent<HandsComponent>(compFactory), $"Found puller {proto} with NeedsHand pulling but has no hands?");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
using Content.Server.Storage.EntitySystems;
|
||||
using Content.Shared.Damage;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Storage;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class EntityStorageTests
|
||||
{
|
||||
[TestPrototypes]
|
||||
private const string Prototypes = @"
|
||||
- type: entity
|
||||
id: EntityStorageTest
|
||||
name: box
|
||||
components:
|
||||
- type: EntityStorage
|
||||
- type: Damageable
|
||||
damageContainer: Inorganic
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
damage: 10
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: [ Destruction ]
|
||||
";
|
||||
|
||||
[Test]
|
||||
public async Task TestContainerDestruction()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
var map = await pair.CreateTestMap();
|
||||
|
||||
EntityUid box = default;
|
||||
EntityUid crowbar = default;
|
||||
await server.WaitPost(() => box = server.EntMan.SpawnEntity("EntityStorageTest", map.GridCoords));
|
||||
await server.WaitPost(() => crowbar = server.EntMan.SpawnEntity("Crowbar", map.GridCoords));
|
||||
|
||||
// Initially the crowbar is not in a contaienr.
|
||||
var sys = server.System<SharedContainerSystem>();
|
||||
Assert.That(sys.IsEntityInContainer(crowbar), Is.False);
|
||||
|
||||
// Open then close the storage entity
|
||||
var storage = server.System<EntityStorageSystem>();
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
storage.OpenStorage(box);
|
||||
storage.CloseStorage(box);
|
||||
});
|
||||
|
||||
// Crowbar is now in the box
|
||||
Assert.That(sys.IsEntityInContainer(crowbar));
|
||||
|
||||
// Damage the box
|
||||
var damage = new DamageSpecifier();
|
||||
damage.DamageDict.Add("Blunt", 100);
|
||||
await server.WaitPost(() => server.System<DamageableSystem>().TryChangeDamage(box, damage));
|
||||
|
||||
// Box has been destroyed, contents have been emptied. Destruction uses deffered deletion.
|
||||
Assert.That(server.EntMan.IsQueuedForDeletion(box));
|
||||
Assert.That(sys.IsEntityInContainer(crowbar), Is.False);
|
||||
|
||||
// Opening and closing the soon-to-be-deleted box should not re-insert the crowbar
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
storage.OpenStorage(box);
|
||||
storage.CloseStorage(box);
|
||||
});
|
||||
Assert.That(sys.IsEntityInContainer(crowbar), Is.False);
|
||||
|
||||
// Entity gets deleted after a few ticks
|
||||
await server.WaitRunTicks(5);
|
||||
Assert.That(server.EntMan.Deleted(box));
|
||||
Assert.That(server.EntMan.Deleted(crowbar), Is.False);
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
1761
Content.Server.Database/Migrations/Postgres/20240201091301_SpawnPriorityPreference.Designer.cs
generated
Normal file
1761
Content.Server.Database/Migrations/Postgres/20240201091301_SpawnPriorityPreference.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class SpawnPriorityPreference : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "spawn_priority",
|
||||
table: "profile",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "spawn_priority",
|
||||
table: "profile");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore;
|
|||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using NpgsqlTypes;
|
||||
|
||||
#nullable disable
|
||||
|
||||
|
|
@ -19,7 +20,7 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "7.0.4")
|
||||
.HasAnnotation("ProductVersion", "8.0.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
|
@ -803,6 +804,10 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
.HasColumnType("integer")
|
||||
.HasColumnName("slot");
|
||||
|
||||
b.Property<int>("SpawnPriority")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("spawn_priority");
|
||||
|
||||
b.Property<string>("Species")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
|
|
@ -879,7 +884,7 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<ValueTuple<IPAddress, int>?>("Address")
|
||||
b.Property<NpgsqlInet?>("Address")
|
||||
.HasColumnType("inet")
|
||||
.HasColumnName("address");
|
||||
|
||||
|
|
@ -1021,7 +1026,7 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<ValueTuple<IPAddress, int>?>("Address")
|
||||
b.Property<NpgsqlInet?>("Address")
|
||||
.HasColumnType("inet")
|
||||
.HasColumnName("address");
|
||||
|
||||
|
|
|
|||
1692
Content.Server.Database/Migrations/Sqlite/20240201091254_SpawnPriorityPreference.Designer.cs
generated
Normal file
1692
Content.Server.Database/Migrations/Sqlite/20240201091254_SpawnPriorityPreference.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Sqlite
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class SpawnPriorityPreference : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "spawn_priority",
|
||||
table: "profile",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "spawn_priority",
|
||||
table: "profile");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "7.0.4");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.0");
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Admin", b =>
|
||||
{
|
||||
|
|
@ -757,6 +757,10 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("slot");
|
||||
|
||||
b.Property<int>("SpawnPriority")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("spawn_priority");
|
||||
|
||||
b.Property<string>("Species")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
|
|
|
|||
|
|
@ -338,6 +338,7 @@ namespace Content.Server.Database
|
|||
public string SkinColor { get; set; } = null!;
|
||||
public string Clothing { get; set; } = null!;
|
||||
public string Backpack { get; set; } = null!;
|
||||
public int SpawnPriority { get; set; } = 0;
|
||||
public List<Job> Jobs { get; } = new();
|
||||
public List<Antag> Antags { get; } = new();
|
||||
public List<Trait> Traits { get; } = new();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using Content.Server.Access.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Interaction;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using Content.Server.Station.Systems;
|
||||
using Content.Server.StationRecords.Systems;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
|
|
@ -21,7 +20,6 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly StationRecordsSystem _record = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
|
||||
[Dependency] private readonly AccessSystem _access = default!;
|
||||
|
|
@ -85,10 +83,9 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||
var targetAccessComponent = EntityManager.GetComponent<AccessComponent>(targetId);
|
||||
|
||||
var jobProto = string.Empty;
|
||||
if (_station.GetOwningStation(uid) is { } station
|
||||
&& EntityManager.TryGetComponent<StationRecordKeyStorageComponent>(targetId, out var keyStorage)
|
||||
&& keyStorage.Key != null
|
||||
&& _record.TryGetRecord<GeneralStationRecord>(station, keyStorage.Key.Value, out var record))
|
||||
if (TryComp<StationRecordKeyStorageComponent>(targetId, out var keyStorage)
|
||||
&& keyStorage.Key is {} key
|
||||
&& _record.TryGetRecord<GeneralStationRecord>(key, out var record))
|
||||
{
|
||||
jobProto = record.JobPrototype;
|
||||
}
|
||||
|
|
@ -103,7 +100,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||
possibleAccess,
|
||||
jobProto,
|
||||
privilegedIdName,
|
||||
EntityManager.GetComponent<MetaDataComponent>(targetId).EntityName);
|
||||
Name(targetId));
|
||||
}
|
||||
|
||||
_userInterface.TrySetUiState(uid, IdCardConsoleUiKey.Key, newState);
|
||||
|
|
@ -184,7 +181,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||
if (!Resolve(uid, ref component))
|
||||
return true;
|
||||
|
||||
if (!EntityManager.TryGetComponent<AccessReaderComponent>(uid, out var reader))
|
||||
if (!TryComp<AccessReaderComponent>(uid, out var reader))
|
||||
return true;
|
||||
|
||||
var privilegedId = component.PrivilegedIdSlot.Item;
|
||||
|
|
@ -193,10 +190,9 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||
|
||||
private void UpdateStationRecord(EntityUid uid, EntityUid targetId, string newFullName, string newJobTitle, JobPrototype? newJobProto)
|
||||
{
|
||||
if (_station.GetOwningStation(uid) is not { } station
|
||||
|| !EntityManager.TryGetComponent<StationRecordKeyStorageComponent>(targetId, out var keyStorage)
|
||||
if (!TryComp<StationRecordKeyStorageComponent>(targetId, out var keyStorage)
|
||||
|| keyStorage.Key is not { } key
|
||||
|| !_record.TryGetRecord<GeneralStationRecord>(station, key, out var record))
|
||||
|| !_record.TryGetRecord<GeneralStationRecord>(key, out var record))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -210,6 +206,6 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||
record.JobIcon = newJobProto.Icon;
|
||||
}
|
||||
|
||||
_record.Synchronize(station);
|
||||
_record.Synchronize(key);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public sealed class AddPolymorphActionCommand : IConsoleCommand
|
|||
|
||||
var polySystem = _entityManager.EntitySysManager.GetEntitySystem<PolymorphSystem>();
|
||||
|
||||
_entityManager.EnsureComponent<PolymorphableComponent>(entityUid.Value);
|
||||
polySystem.CreatePolymorphAction(args[1], entityUid.Value);
|
||||
var polymorphable = _entityManager.EnsureComponent<PolymorphableComponent>(entityUid.Value);
|
||||
polySystem.CreatePolymorphAction(args[1], (entityUid.Value, polymorphable));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
namespace Content.Server.Administration.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class AdminMinigunComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Content.Server.Administration.Components;
|
||||
using Content.Shared.Weapons.Ranged.Events;
|
||||
|
||||
namespace Content.Server.Administration.Systems;
|
||||
|
||||
public sealed class AdminGunSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<AdminMinigunComponent, GunRefreshModifiersEvent>(OnGunRefreshModifiers);
|
||||
}
|
||||
|
||||
private void OnGunRefreshModifiers(Entity<AdminMinigunComponent> ent, ref GunRefreshModifiersEvent args)
|
||||
{
|
||||
args.FireRate = 15;
|
||||
}
|
||||
}
|
||||
|
|
@ -349,7 +349,7 @@ namespace Content.Server.Administration.Systems
|
|||
if (TryComp(item, out PdaComponent? pda) &&
|
||||
TryComp(pda.ContainedId, out StationRecordKeyStorageComponent? keyStorage) &&
|
||||
keyStorage.Key is { } key &&
|
||||
_stationRecords.TryGetRecord(key.OriginStation, key, out GeneralStationRecord? record))
|
||||
_stationRecords.TryGetRecord(key, out GeneralStationRecord? record))
|
||||
{
|
||||
if (TryComp(entity, out DnaComponent? dna) &&
|
||||
dna.DNA != record.DNA)
|
||||
|
|
@ -363,7 +363,7 @@ namespace Content.Server.Administration.Systems
|
|||
continue;
|
||||
}
|
||||
|
||||
_stationRecords.RemoveRecord(key.OriginStation, key);
|
||||
_stationRecords.RemoveRecord(key);
|
||||
Del(item);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using Content.Server.Power.EntitySystems;
|
|||
using Content.Server.Stack;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Server.Weapons.Ranged.Systems;
|
||||
using Content.Shared.Access;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
|
|
@ -52,6 +53,7 @@ public sealed partial class AdminVerbSystem
|
|||
[Dependency] private readonly BatterySystem _batterySystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
|
||||
[Dependency] private readonly GunSystem _gun = default!;
|
||||
|
||||
private void AddTricksVerbs(GetVerbsEvent<Verb> args)
|
||||
{
|
||||
|
|
@ -697,7 +699,8 @@ public sealed partial class AdminVerbSystem
|
|||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Weapons/Guns/HMGs/minigun.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
gun.FireRate = 15;
|
||||
EnsureComp<AdminMinigunComponent>(args.Target);
|
||||
_gun.RefreshModifiers((args.Target, gun));
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-minigun-fire-description"),
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Text.Json.Nodes;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Afk;
|
||||
using Content.Server.Discord;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Shared.Administration;
|
||||
|
|
@ -33,6 +34,7 @@ namespace Content.Server.Administration.Systems
|
|||
[Dependency] private readonly IPlayerLocator _playerLocator = default!;
|
||||
[Dependency] private readonly GameTicker _gameTicker = default!;
|
||||
[Dependency] private readonly SharedMindSystem _minds = default!;
|
||||
[Dependency] private readonly IAfkManager _afkManager = default!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
private readonly HttpClient _httpClient = new();
|
||||
|
|
@ -327,7 +329,7 @@ namespace Content.Server.Administration.Systems
|
|||
username += $" ({characterName})";
|
||||
|
||||
// If no admins are online, set embed color to red. Otherwise green
|
||||
var color = GetTargetAdmins().Count > 0 ? 0x41F097 : 0xFF0000;
|
||||
var color = GetNonAfkAdmins().Count > 0 ? 0x41F097 : 0xFF0000;
|
||||
|
||||
// Limit server name to 1500 characters, in case someone tries to be a little funny
|
||||
var serverName = _serverName[..Math.Min(_serverName.Length, 1500)];
|
||||
|
|
@ -471,7 +473,8 @@ namespace Content.Server.Administration.Systems
|
|||
{
|
||||
str = str[..(DescriptionMax - _maxAdditionalChars - unameLength)];
|
||||
}
|
||||
_messageQueues[msg.UserId].Enqueue(GenerateAHelpMessage(senderSession.Name, str, !personalChannel, _gameTicker.RoundDuration().ToString("hh\\:mm\\:ss"), _gameTicker.RunLevel, admins.Count == 0));
|
||||
var nonAfkAdmins = GetNonAfkAdmins();
|
||||
_messageQueues[msg.UserId].Enqueue(GenerateAHelpMessage(senderSession.Name, str, !personalChannel, _gameTicker.RoundDuration().ToString("hh\\:mm\\:ss"), _gameTicker.RunLevel, nonAfkAdmins.Count == 0));
|
||||
}
|
||||
|
||||
if (admins.Count != 0 || sendsWebhook)
|
||||
|
|
@ -483,19 +486,26 @@ namespace Content.Server.Administration.Systems
|
|||
RaiseNetworkEvent(starMuteMsg, senderSession.Channel);
|
||||
}
|
||||
|
||||
// Returns all online admins with AHelp access
|
||||
private IList<INetChannel> GetNonAfkAdmins()
|
||||
{
|
||||
return _adminManager.ActiveAdmins
|
||||
.Where(p => (_adminManager.GetAdminData(p)?.HasFlag(AdminFlags.Adminhelp) ?? false) && !_afkManager.IsAfk(p))
|
||||
.Select(p => p.Channel)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private IList<INetChannel> GetTargetAdmins()
|
||||
{
|
||||
return _adminManager.ActiveAdmins
|
||||
.Where(p => _adminManager.GetAdminData(p)?.HasFlag(AdminFlags.Adminhelp) ?? false)
|
||||
.Select(p => p.Channel)
|
||||
.ToList();
|
||||
.Where(p => _adminManager.GetAdminData(p)?.HasFlag(AdminFlags.Adminhelp) ?? false)
|
||||
.Select(p => p.Channel)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static string GenerateAHelpMessage(string username, string message, bool admin, string roundTime, GameRunLevel roundState, bool noReceivers = false)
|
||||
{
|
||||
var stringbuilder = new StringBuilder();
|
||||
|
||||
|
||||
if (admin)
|
||||
stringbuilder.Append(":outbox_tray:");
|
||||
else if (noReceivers)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared.CCVar;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Shared.CCVar;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
|
|
@ -38,6 +39,7 @@ namespace Content.Server.Afk
|
|||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||
|
||||
private readonly Dictionary<ICommonSession, TimeSpan> _lastActionTimes = new();
|
||||
|
||||
|
|
@ -61,10 +63,15 @@ namespace Content.Server.Afk
|
|||
public bool IsAfk(ICommonSession player)
|
||||
{
|
||||
if (!_lastActionTimes.TryGetValue(player, out var time))
|
||||
{
|
||||
// Some weird edge case like disconnected clients. Just say true I guess.
|
||||
return true;
|
||||
}
|
||||
|
||||
var timeOut = _adminManager.IsAdmin(player)
|
||||
? TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.AdminAfkTime))
|
||||
: TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.AfkTime));
|
||||
|
||||
var timeOut = TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.AfkTime));
|
||||
return _gameTiming.RealTime - time > timeOut;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ public sealed class AmeNodeGroup : BaseNodeGroup
|
|||
// Fuel is squared so more fuel vastly increases power and efficiency
|
||||
// We divide by the number of cores so a larger AME is less efficient at the same fuel settings
|
||||
// this results in all AMEs having the same efficiency at the same fuel-per-core setting
|
||||
return 20000f * fuel * fuel / cores;
|
||||
return 2000000f * fuel * fuel / cores;
|
||||
}
|
||||
|
||||
public int GetTotalStability()
|
||||
|
|
|
|||
|
|
@ -263,6 +263,7 @@ public sealed class AmeControllerSystem : EntitySystem
|
|||
{
|
||||
< 10 => AmeControllerState.Fuck,
|
||||
< 50 => AmeControllerState.Critical,
|
||||
< 80 => AmeControllerState.Warning,
|
||||
_ => AmeControllerState.On,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,8 @@ public sealed partial class AnomalySystem
|
|||
// don't spawn inside of solid objects
|
||||
var physQuery = GetEntityQuery<PhysicsComponent>();
|
||||
var valid = true;
|
||||
|
||||
// TODO: This should be using static lookup.
|
||||
foreach (var ent in gridComp.GetAnchoredEntities(tile))
|
||||
{
|
||||
if (!physQuery.TryGetComponent(ent, out var body))
|
||||
|
|
@ -143,9 +145,9 @@ public sealed partial class AnomalySystem
|
|||
if (antiXform.MapID != mapPos.MapId)
|
||||
continue;
|
||||
|
||||
var antiCoordinates = _transform.GetMapCoordinates(antiXform);
|
||||
var antiCoordinates = _transform.GetWorldPosition(antiXform);
|
||||
|
||||
var delta = antiCoordinates.Position - mapPos.Position;
|
||||
var delta = antiCoordinates - mapPos.Position;
|
||||
if (delta.LengthSquared() < zone.ZoneRadius * zone.ZoneRadius)
|
||||
{
|
||||
valid = false;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using Content.Shared.Anomaly.Effects;
|
|||
using Content.Shared.Anomaly.Effects.Components;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Anomaly.Effects;
|
||||
|
|
@ -13,10 +14,15 @@ public sealed class EntityAnomalySystem : SharedEntityAnomalySystem
|
|||
[Dependency] private readonly SharedAnomalySystem _anomaly = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
|
||||
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
|
||||
SubscribeLocalEvent<EntitySpawnAnomalyComponent, AnomalyPulseEvent>(OnPulse);
|
||||
SubscribeLocalEvent<EntitySpawnAnomalyComponent, AnomalySupercriticalEvent>(OnSupercritical);
|
||||
SubscribeLocalEvent<EntitySpawnAnomalyComponent, AnomalyStabilityChangedEvent>(OnStabilityChanged);
|
||||
|
|
@ -82,7 +88,7 @@ public sealed class EntityAnomalySystem : SharedEntityAnomalySystem
|
|||
private void SpawnEntities(Entity<EntitySpawnAnomalyComponent> anomaly, EntitySpawnSettingsEntry entry, float stability, float severity)
|
||||
{
|
||||
var xform = Transform(anomaly);
|
||||
if (!TryComp<MapGridComponent>(xform.GridUid, out var grid))
|
||||
if (!TryComp(xform.GridUid, out MapGridComponent? grid))
|
||||
return;
|
||||
|
||||
var tiles = _anomaly.GetSpawningPoints(anomaly, stability, severity, entry.Settings);
|
||||
|
|
@ -91,7 +97,7 @@ public sealed class EntityAnomalySystem : SharedEntityAnomalySystem
|
|||
|
||||
foreach (var tileref in tiles)
|
||||
{
|
||||
Spawn(_random.Pick(entry.Spawns), tileref.GridIndices.ToEntityCoordinates(xform.GridUid.Value, _map));
|
||||
Spawn(_random.Pick(entry.Spawns), _mapSystem.ToCenterCoordinates(tileref, grid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using Content.Server.Power.Components;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Arcade;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using Content.Server.Power.Components;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.UserInterface;
|
||||
using static Content.Shared.Arcade.SharedSpaceVillainArcadeComponent;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||
{
|
||||
if(_spaceWindSoundCooldown == 0 && !string.IsNullOrEmpty(SpaceWindSound))
|
||||
{
|
||||
var coordinates = tile.GridIndices.ToEntityCoordinates(tile.GridIndex, _mapManager);
|
||||
var coordinates = _mapSystem.ToCenterCoordinates(tile.GridIndex, tile.GridIndices);
|
||||
_audio.PlayPvs(SpaceWindSound, coordinates, AudioParams.Default.WithVariation(0.125f).WithVolume(MathHelper.Clamp(tile.PressureDifference / 10, 10, 100)));
|
||||
}
|
||||
}
|
||||
|
|
@ -163,7 +163,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||
gridAtmosphere.Comp.UpdateCounter,
|
||||
tile.PressureDifference,
|
||||
tile.PressureDirection, 0,
|
||||
tile.PressureSpecificTarget?.GridIndices.ToEntityCoordinates(tile.GridIndex, _mapManager) ?? EntityCoordinates.Invalid,
|
||||
tile.PressureSpecificTarget != null ? _mapSystem.ToCenterCoordinates(tile.GridIndex, tile.PressureSpecificTarget.GridIndices) : EntityCoordinates.Invalid,
|
||||
gridWorldRotation,
|
||||
xforms.GetComponent(entity),
|
||||
body);
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ namespace Content.Server.Atmos.EntitySystems
|
|||
{
|
||||
public sealed partial class AtmosphereSystem
|
||||
{
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
|
||||
private const int HotspotSoundCooldownCycles = 200;
|
||||
|
||||
private int _hotspotSoundCooldown = 0;
|
||||
|
|
@ -81,7 +79,8 @@ namespace Content.Server.Atmos.EntitySystems
|
|||
|
||||
if (_hotspotSoundCooldown++ == 0 && !string.IsNullOrEmpty(HotspotSound))
|
||||
{
|
||||
var coordinates = tile.GridIndices.ToEntityCoordinates(tile.GridIndex, _mapManager);
|
||||
var coordinates = _mapSystem.ToCenterCoordinates(tile.GridIndex, tile.GridIndices);
|
||||
|
||||
// A few details on the audio parameters for fire.
|
||||
// The greater the fire state, the lesser the pitch variation.
|
||||
// The greater the fire state, the greater the volume.
|
||||
|
|
|
|||
|
|
@ -25,12 +25,14 @@ public sealed partial class AtmosphereSystem : SharedAtmosphereSystem
|
|||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLog = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly InternalsSystem _internals = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containers = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly GasTileOverlaySystem _gasTileOverlaySystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly TileSystem _tile = default!;
|
||||
[Dependency] private readonly MapSystem _map = default!;
|
||||
[Dependency] public readonly PuddleSystem Puddle = default!;
|
||||
|
|
|
|||
|
|
@ -1,24 +1,26 @@
|
|||
using Content.Server.Atmos.Components;
|
||||
using Content.Shared.Actions.Events;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Atmos.EntitySystems;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Content.Shared.Abilities.Firestarter;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// Adds an action ability that will cause all flammable targets in a radius to ignite, also heals the owner
|
||||
/// of the component when used.
|
||||
/// </summary>
|
||||
namespace Content.Server.Abilities.Firestarter;
|
||||
|
||||
public sealed class FirestarterSystem : EntitySystem
|
||||
public sealed class FirestarterSystem : SharedFirestarterSystem
|
||||
{
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly FlammableSystem _flammable = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
|
||||
private readonly HashSet<Entity<FlammableComponent>> _flammables = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
|
@ -26,11 +28,10 @@ public sealed class FirestarterSystem : EntitySystem
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks Radius for igniting nearby flammable objects .
|
||||
/// Checks Radius for igniting nearby flammable objects
|
||||
/// </summary>
|
||||
private void OnStartFire(EntityUid uid, FirestarterComponent component, FireStarterActionEvent args)
|
||||
{
|
||||
|
||||
if (_container.IsEntityOrParentInContainer(uid))
|
||||
return;
|
||||
|
||||
|
|
@ -47,10 +48,10 @@ public sealed class FirestarterSystem : EntitySystem
|
|||
/// </summary>
|
||||
public void IgniteNearby(EntityUid uid, EntityCoordinates coordinates, float severity, float radius)
|
||||
{
|
||||
var flammables = new HashSet<Entity<FlammableComponent>>();
|
||||
_lookup.GetEntitiesInRange(coordinates, radius, flammables);
|
||||
_flammables.Clear();
|
||||
_lookup.GetEntitiesInRange(coordinates, radius, _flammables);
|
||||
|
||||
foreach (var flammable in flammables)
|
||||
foreach (var flammable in _flammables)
|
||||
{
|
||||
var ent = flammable.Owner;
|
||||
var stackAmount = 2 + (int) (severity / 0.15f);
|
||||
|
|
@ -402,7 +402,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||
if (TryComp(uid, out TemperatureComponent? temp))
|
||||
_temperatureSystem.ChangeHeat(uid, 12500 * damageScale, false, temp);
|
||||
|
||||
_damageableSystem.TryChangeDamage(uid, flammable.Damage * damageScale);
|
||||
_damageableSystem.TryChangeDamage(uid, flammable.Damage * damageScale, interruptsDoAfters: false);
|
||||
|
||||
AdjustFireStacks(uid, flammable.FirestackFade * (flammable.Resisting ? 10f : 1f), flammable);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ using Content.Server.Body.Components;
|
|||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Cargo.Systems;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Components;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ using Content.Shared.Atmos.Piping.Unary.Components;
|
|||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Examine;
|
||||
|
|
|
|||
|
|
@ -133,7 +133,8 @@ public sealed class RottingSystem : SharedRottingSystem
|
|||
return;
|
||||
}
|
||||
|
||||
var description = "perishable-" + stage;
|
||||
var isMob = HasComp<MobStateComponent>(perishable);
|
||||
var description = "perishable-" + stage + (!isMob ? "-nonmob" : string.Empty);
|
||||
args.PushMarkup(Loc.GetString(description, ("target", Identity.Entity(perishable, EntityManager))));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using Content.Server.Inventory;
|
|||
using Content.Server.Popups;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Bed.Cryostorage;
|
||||
using Content.Shared.Chat;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue