mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-11-08 12:04:16 +03:00
Merge pull request #1743 from space-syndicate/upstream-sync
Upstream sync
This commit is contained in:
commit
71b9b49760
1007 changed files with 603177 additions and 698018 deletions
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
# - name: Install dependencies
|
||||
# run: sudo apt-get install -y python3-paramiko
|
||||
# run: sudo apt-get install -y python3-paramiko python3-lxml
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ namespace Content.Benchmarks
|
|||
var componentFactory = new Mock<IComponentFactory>();
|
||||
componentFactory.Setup(p => p.GetComponent<DummyComponent>()).Returns(new DummyComponent());
|
||||
componentFactory.Setup(p => p.GetRegistration(It.IsAny<DummyComponent>())).Returns(dummyReg);
|
||||
componentFactory.Setup(p => p.GetAllRegistrations()).Returns(new[] { dummyReg });
|
||||
componentFactory.Setup(p => p.GetAllRefTypes()).Returns(new[] { CompIdx.Index<DummyComponent>() });
|
||||
|
||||
IoCManager.RegisterInstance<IComponentFactory>(componentFactory.Object);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using Content.Client.Atmos.EntitySystems;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Piping.Trinary.Components;
|
||||
using Content.Shared.Localizations;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Atmos.UI
|
||||
{
|
||||
|
|
@ -50,7 +50,7 @@ namespace Content.Client.Atmos.UI
|
|||
|
||||
private void OnFilterTransferRatePressed(string value)
|
||||
{
|
||||
float rate = float.TryParse(value, out var parsed) ? parsed : 0f;
|
||||
var rate = UserInputParser.TryFloat(value, out var parsed) ? parsed : 0f;
|
||||
|
||||
SendMessage(new GasFilterChangeRateMessage(rate));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Label Text="{Loc comp-gas-filter-ui-filter-transfer-rate}"/>
|
||||
<LineEdit Name="FilterTransferRateInput" MinSize="60 0" />
|
||||
<LineEdit Name="FilterTransferRateInput" MinSize="70 0" />
|
||||
<Button Name="SetFilterRate" Text="{Loc comp-gas-filter-ui-filter-set-rate}" Disabled="True"/>
|
||||
</BoxContainer>
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ namespace Content.Client.Atmos.UI
|
|||
|
||||
public void SetTransferRate(float rate)
|
||||
{
|
||||
FilterTransferRateInput.Text = rate.ToString(CultureInfo.InvariantCulture);
|
||||
FilterTransferRateInput.Text = rate.ToString(CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
public void SetFilterStatus(bool enabled)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Piping.Trinary.Components;
|
||||
using Content.Shared.Localizations;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
|
|
@ -47,7 +48,7 @@ namespace Content.Client.Atmos.UI
|
|||
|
||||
private void OnMixerOutputPressurePressed(string value)
|
||||
{
|
||||
var pressure = float.TryParse(value, out var parsed) ? parsed : 0f;
|
||||
var pressure = UserInputParser.TryFloat(value, out var parsed) ? parsed : 0f;
|
||||
if (pressure > MaxPressure)
|
||||
pressure = MaxPressure;
|
||||
|
||||
|
|
@ -57,7 +58,7 @@ namespace Content.Client.Atmos.UI
|
|||
private void OnMixerSetPercentagePressed(string value)
|
||||
{
|
||||
// We don't need to send both nodes because it's just 100.0f - node
|
||||
var node = float.TryParse(value, out var parsed) ? parsed : 1.0f;
|
||||
var node = UserInputParser.TryFloat(value, out var parsed) ? parsed : 1.0f;
|
||||
|
||||
node = Math.Clamp(node, 0f, 100.0f);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Label Text="{Loc comp-gas-mixer-ui-mixer-output-pressure}"/>
|
||||
<Control MinSize="5 0" />
|
||||
<LineEdit Name="MixerPressureOutputInput" MinSize="60 0" />
|
||||
<LineEdit Name="MixerPressureOutputInput" MinSize="70 0" />
|
||||
<Control MinSize="5 0" />
|
||||
<Button Name="SetMaxPressureButton" Text="{Loc comp-gas-mixer-ui-mixer-max}" />
|
||||
<Control MinSize="5 0" />
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ namespace Content.Client.Atmos.UI
|
|||
|
||||
SetMaxPressureButton.OnPressed += _ =>
|
||||
{
|
||||
MixerPressureOutputInput.Text = Atmospherics.MaxOutputPressure.ToString(CultureInfo.InvariantCulture);
|
||||
MixerPressureOutputInput.Text = Atmospherics.MaxOutputPressure.ToString(CultureInfo.CurrentCulture);
|
||||
SetOutputPressureButton.Disabled = false;
|
||||
};
|
||||
|
||||
|
|
@ -69,16 +69,16 @@ namespace Content.Client.Atmos.UI
|
|||
|
||||
public void SetOutputPressure(float pressure)
|
||||
{
|
||||
MixerPressureOutputInput.Text = pressure.ToString(CultureInfo.InvariantCulture);
|
||||
MixerPressureOutputInput.Text = pressure.ToString(CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
public void SetNodePercentages(float nodeOne)
|
||||
{
|
||||
nodeOne *= 100.0f;
|
||||
MixerNodeOneInput.Text = nodeOne.ToString("0.##", CultureInfo.InvariantCulture);
|
||||
MixerNodeOneInput.Text = nodeOne.ToString("0.##", CultureInfo.CurrentCulture);
|
||||
|
||||
float nodeTwo = 100.0f - nodeOne;
|
||||
MixerNodeTwoInput.Text = nodeTwo.ToString("0.##", CultureInfo.InvariantCulture);
|
||||
MixerNodeTwoInput.Text = nodeTwo.ToString("0.##", CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
public void SetMixerStatus(bool enabled)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Piping.Binary.Components;
|
||||
using Content.Shared.Localizations;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
|
|
@ -46,7 +47,7 @@ namespace Content.Client.Atmos.UI
|
|||
|
||||
private void OnPumpOutputPressurePressed(string value)
|
||||
{
|
||||
float pressure = float.TryParse(value, out var parsed) ? parsed : 0f;
|
||||
var pressure = UserInputParser.TryFloat(value, out var parsed) ? parsed : 0f;
|
||||
if (pressure > MaxPressure) pressure = MaxPressure;
|
||||
|
||||
SendMessage(new GasPressurePumpChangeOutputPressureMessage(pressure));
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Label Text="{Loc comp-gas-pump-ui-pump-output-pressure}"/>
|
||||
<Control MinSize="5 0" />
|
||||
<LineEdit Name="PumpPressureOutputInput" MinSize="60 0" />
|
||||
<LineEdit Name="PumpPressureOutputInput" MinSize="70 0" />
|
||||
<Control MinSize="5 0" />
|
||||
<Button Name="SetMaxPressureButton" Text="{Loc comp-gas-pump-ui-pump-set-max}" />
|
||||
<Control MinSize="5 0" />
|
||||
|
|
|
|||
|
|
@ -39,14 +39,14 @@ namespace Content.Client.Atmos.UI
|
|||
|
||||
SetMaxPressureButton.OnPressed += _ =>
|
||||
{
|
||||
PumpPressureOutputInput.Text = Atmospherics.MaxOutputPressure.ToString(CultureInfo.InvariantCulture);
|
||||
PumpPressureOutputInput.Text = Atmospherics.MaxOutputPressure.ToString(CultureInfo.CurrentCulture);
|
||||
SetOutputPressureButton.Disabled = false;
|
||||
};
|
||||
}
|
||||
|
||||
public void SetOutputPressure(float pressure)
|
||||
{
|
||||
PumpPressureOutputInput.Text = pressure.ToString(CultureInfo.InvariantCulture);
|
||||
PumpPressureOutputInput.Text = pressure.ToString(CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
public void SetPumpStatus(bool enabled)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public sealed partial class GasThermomachineWindow : DefaultWindow
|
|||
RobustXamlLoader.Load(this);
|
||||
|
||||
SpinboxHBox.AddChild(
|
||||
TemperatureSpinbox = new FloatSpinBox(.1f, 2) { MaxWidth = 150, HorizontalExpand = true }
|
||||
TemperatureSpinbox = new FloatSpinBox(.1f, 2) { MinWidth = 150, HorizontalExpand = true }
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Piping.Binary.Components;
|
||||
using Content.Shared.Localizations;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
|
|
@ -46,7 +47,7 @@ namespace Content.Client.Atmos.UI
|
|||
|
||||
private void OnPumpTransferRatePressed(string value)
|
||||
{
|
||||
var rate = float.TryParse(value, out var parsed) ? parsed : 0f;
|
||||
var rate = UserInputParser.TryFloat(value, out var parsed) ? parsed : 0f;
|
||||
if (rate > MaxTransferRate)
|
||||
rate = MaxTransferRate;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Label Text="{Loc comp-gas-pump-ui-pump-transfer-rate}"/>
|
||||
<Control MinSize="5 0" />
|
||||
<LineEdit Name="PumpTransferRateInput" MinSize="60 0" />
|
||||
<LineEdit Name="PumpTransferRateInput" MinSize="70 0" />
|
||||
<Control MinSize="5 0" />
|
||||
<Button Name="SetMaxRateButton" Text="{Loc comp-gas-pump-ui-pump-set-max}" />
|
||||
<Control MinSize="5 0" />
|
||||
|
|
|
|||
|
|
@ -39,14 +39,14 @@ namespace Content.Client.Atmos.UI
|
|||
|
||||
SetMaxRateButton.OnPressed += _ =>
|
||||
{
|
||||
PumpTransferRateInput.Text = Atmospherics.MaxTransferRate.ToString(CultureInfo.InvariantCulture);
|
||||
PumpTransferRateInput.Text = Atmospherics.MaxTransferRate.ToString(CultureInfo.CurrentCulture);
|
||||
SetTransferRateButton.Disabled = false;
|
||||
};
|
||||
}
|
||||
|
||||
public void SetTransferRate(float rate)
|
||||
{
|
||||
PumpTransferRateInput.Text = rate.ToString(CultureInfo.InvariantCulture);
|
||||
PumpTransferRateInput.Text = rate.ToString(CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
public void SetPumpStatus(bool enabled)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ public sealed partial class ContentAudioSystem
|
|||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly IResourceCache _resource = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IStateManager _state = default!;
|
||||
[Dependency] private readonly RulesSystem _rules = default!;
|
||||
|
|
@ -153,8 +152,7 @@ public sealed partial class ContentAudioSystem
|
|||
// Update still runs in lobby so just ignore it.
|
||||
if (_state.CurrentState is not GameplayState)
|
||||
{
|
||||
Audio.Stop(_ambientMusicStream);
|
||||
_ambientMusicStream = null;
|
||||
_ambientMusicStream = Audio.Stop(_ambientMusicStream);
|
||||
_musicProto = null;
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,8 @@ public sealed partial class ContentAudioSystem : SharedContentAudioSystem
|
|||
}
|
||||
|
||||
var volume = component.Volume - change * frameTime;
|
||||
component.Volume = MathF.Max(MinVolume, volume);
|
||||
volume = MathF.Max(MinVolume, volume);
|
||||
_audio.SetVolume(stream, volume, component);
|
||||
|
||||
if (component.Volume.Equals(MinVolume))
|
||||
{
|
||||
|
|
@ -145,7 +146,8 @@ public sealed partial class ContentAudioSystem : SharedContentAudioSystem
|
|||
}
|
||||
|
||||
var volume = component.Volume + change * frameTime;
|
||||
component.Volume = MathF.Min(target, volume);
|
||||
volume = MathF.Max(target, volume);
|
||||
_audio.SetVolume(stream, volume, component);
|
||||
|
||||
if (component.Volume.Equals(target))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using Content.Shared.Weapons.Ranged.Components;
|
|||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Serialization;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Graphics;
|
||||
|
|
@ -26,6 +27,7 @@ public sealed class CombatModeIndicatorsOverlay : Overlay
|
|||
private readonly HandsSystem _hands = default!;
|
||||
|
||||
private readonly Texture _gunSight;
|
||||
private readonly Texture _gunBoltSight;
|
||||
private readonly Texture _meleeSight;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||
|
|
@ -46,6 +48,8 @@ public sealed class CombatModeIndicatorsOverlay : Overlay
|
|||
var spriteSys = _entMan.EntitySysManager.GetEntitySystem<SpriteSystem>();
|
||||
_gunSight = spriteSys.Frame0(new SpriteSpecifier.Rsi(new ResPath("/Textures/Interface/Misc/crosshair_pointers.rsi"),
|
||||
"gun_sight"));
|
||||
_gunBoltSight = spriteSys.Frame0(new SpriteSpecifier.Rsi(new ResPath("/Textures/Interface/Misc/crosshair_pointers.rsi"),
|
||||
"gun_bolt_sight"));
|
||||
_meleeSight = spriteSys.Frame0(new SpriteSpecifier.Rsi(new ResPath("/Textures/Interface/Misc/crosshair_pointers.rsi"),
|
||||
"melee_sight"));
|
||||
}
|
||||
|
|
@ -67,12 +71,16 @@ public sealed class CombatModeIndicatorsOverlay : Overlay
|
|||
|
||||
var handEntity = _hands.GetActiveHandEntity();
|
||||
var isHandGunItem = _entMan.HasComponent<GunComponent>(handEntity);
|
||||
var isGunBolted = true;
|
||||
if (_entMan.TryGetComponent(handEntity, out ChamberMagazineAmmoProviderComponent? chamber))
|
||||
isGunBolted = chamber.BoltClosed ?? true;
|
||||
|
||||
|
||||
var mousePos = mouseScreenPosition.Position;
|
||||
var uiScale = (args.ViewportControl as Control)?.UIScale ?? 1f;
|
||||
var limitedScale = uiScale > 1.25f ? 1.25f : uiScale;
|
||||
|
||||
var sight = isHandGunItem ? _gunSight : _meleeSight;
|
||||
var sight = isHandGunItem ? (isGunBolted ? _gunSight : _gunBoltSight) : _meleeSight;
|
||||
DrawSight(sight, args.ScreenHandle, mousePos, limitedScale * Scale);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,27 +81,30 @@ namespace Content.Client.Construction
|
|||
|
||||
private void HandleConstructionGhostExamined(EntityUid uid, ConstructionGhostComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (component.Prototype == null) return;
|
||||
|
||||
args.PushMarkup(Loc.GetString(
|
||||
"construction-ghost-examine-message",
|
||||
("name", component.Prototype.Name)));
|
||||
|
||||
if (!_prototypeManager.TryIndex(component.Prototype.Graph, out ConstructionGraphPrototype? graph))
|
||||
if (component.Prototype == null)
|
||||
return;
|
||||
|
||||
var startNode = graph.Nodes[component.Prototype.StartNode];
|
||||
|
||||
if (!graph.TryPath(component.Prototype.StartNode, component.Prototype.TargetNode, out var path) ||
|
||||
!startNode.TryGetEdge(path[0].Name, out var edge))
|
||||
using (args.PushGroup(nameof(ConstructionGhostComponent)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
args.PushMarkup(Loc.GetString(
|
||||
"construction-ghost-examine-message",
|
||||
("name", component.Prototype.Name)));
|
||||
|
||||
foreach (ConstructionGraphStep step in edge.Steps)
|
||||
{
|
||||
args.Message.PushNewline();
|
||||
step.DoExamine(args);
|
||||
if (!_prototypeManager.TryIndex(component.Prototype.Graph, out ConstructionGraphPrototype? graph))
|
||||
return;
|
||||
|
||||
var startNode = graph.Nodes[component.Prototype.StartNode];
|
||||
|
||||
if (!graph.TryPath(component.Prototype.StartNode, component.Prototype.TargetNode, out var path) ||
|
||||
!startNode.TryGetEdge(path[0].Name, out var edge))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var step in edge.Steps)
|
||||
{
|
||||
step.DoExamine(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,20 +8,21 @@
|
|||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" VerticalExpand="True" Margin="10">
|
||||
<BoxContainer SizeFlagsStretchRatio="2" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<SpriteView Name="MachineSprite" Scale="4 4" HorizontalAlignment="Center" MinSize="128 128"/>
|
||||
<SpriteView Name="MachineSprite" Scale="4 4" HorizontalAlignment="Center" VerticalExpand="True" MinSize="128 128"/>
|
||||
<RichTextLabel Name="MachineNameLabel" HorizontalAlignment="Center" StyleClasses="LabelKeyText"/>
|
||||
</BoxContainer>
|
||||
<Control MinHeight="10"/>
|
||||
<Button Name="PackButton" Text="{Loc 'flatpacker-ui-pack-button'}" MaxWidth="150" Margin="0 0 0 10"/>
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" RectClipContent="True">
|
||||
<Label Name="CostHeaderLabel" Text="{Loc 'flatpacker-ui-cost-label'}" HorizontalAlignment="Left"/>
|
||||
<PanelContainer VerticalExpand="True"
|
||||
HorizontalExpand="True">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True">
|
||||
<RichTextLabel Name="CostLabel" HorizontalAlignment="Center"/>
|
||||
<BoxContainer Orientation="Vertical" VerticalAlignment="Center">
|
||||
<RichTextLabel Name="CostLabel" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<RichTextLabel Name="InsertLabel" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
|||
|
||||
private readonly EntityUid _owner;
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
public const string NoBoardEffectId = "FlatpackerNoBoardEffect";
|
||||
|
||||
private EntityUid? _currentBoard = EntityUid.Invalid;
|
||||
private EntityUid? _machinePreview;
|
||||
|
||||
|
|
@ -47,6 +50,7 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
|||
PackButton.OnPressed += _ => PackButtonPressed?.Invoke();
|
||||
|
||||
MaterialStorageControl.SetOwner(uid);
|
||||
InsertLabel.SetMarkup(Loc.GetString("flatpacker-ui-insert-board"));
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
|
|
@ -63,15 +67,15 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
|||
!_itemSlots.TryGetSlot(_owner, flatpacker.SlotId, out var itemSlot))
|
||||
return;
|
||||
|
||||
MachineBoardComponent? machineBoardComp = null;
|
||||
if (flatpacker.Packing)
|
||||
{
|
||||
PackButton.Disabled = true;
|
||||
}
|
||||
else if (_currentBoard != null)
|
||||
{
|
||||
//todo double trycomp is kinda stinky.
|
||||
Dictionary<string, int> cost;
|
||||
if (_entityManager.TryGetComponent<MachineBoardComponent>(_currentBoard, out var machineBoardComp) &&
|
||||
if (_entityManager.TryGetComponent(_currentBoard, out machineBoardComp) &&
|
||||
machineBoardComp.Prototype is not null)
|
||||
cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), (_currentBoard.Value, machineBoardComp));
|
||||
else
|
||||
|
|
@ -88,16 +92,17 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
|||
|
||||
_currentBoard = itemSlot.Item;
|
||||
CostHeaderLabel.Visible = _currentBoard != null;
|
||||
InsertLabel.Visible = _currentBoard == null;
|
||||
|
||||
if (_currentBoard is not null)
|
||||
{
|
||||
string? prototype = null;
|
||||
Dictionary<string, int>? cost = null;
|
||||
|
||||
if (_entityManager.TryGetComponent<MachineBoardComponent>(_currentBoard, out var machineBoard))
|
||||
if (machineBoardComp != null || _entityManager.TryGetComponent(_currentBoard, out machineBoardComp))
|
||||
{
|
||||
prototype = machineBoard.Prototype;
|
||||
cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), (_currentBoard.Value, machineBoard));
|
||||
prototype = machineBoardComp.Prototype;
|
||||
cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), (_currentBoard.Value, machineBoardComp));
|
||||
}
|
||||
else if (_entityManager.TryGetComponent<ComputerBoardComponent>(_currentBoard, out var computerBoard))
|
||||
{
|
||||
|
|
@ -116,22 +121,23 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
|||
}
|
||||
else
|
||||
{
|
||||
_machinePreview = null;
|
||||
MachineNameLabel.SetMessage(" ");
|
||||
_machinePreview = _entityManager.Spawn(NoBoardEffectId);
|
||||
CostLabel.SetMessage(Loc.GetString("flatpacker-ui-no-board-label"));
|
||||
MachineNameLabel.SetMessage(" ");
|
||||
PackButton.Disabled = true;
|
||||
}
|
||||
|
||||
MachineSprite.SetEntity(_machinePreview);
|
||||
}
|
||||
|
||||
//todo beautify
|
||||
private string GetCostString(Dictionary<string, int> costs)
|
||||
{
|
||||
var orderedCosts = costs.OrderBy(p => p.Value);
|
||||
var orderedCosts = costs.OrderBy(p => p.Value).ToArray();
|
||||
var msg = new FormattedMessage();
|
||||
foreach (var (mat, amount) in orderedCosts)
|
||||
for (var i = 0; i < orderedCosts.Length; i++)
|
||||
{
|
||||
var (mat, amount) = orderedCosts[i];
|
||||
|
||||
var matProto = _prototypeManager.Index<MaterialPrototype>(mat);
|
||||
|
||||
var sheetVolume = _materialStorage.GetSheetVolume(matProto);
|
||||
|
|
@ -144,9 +150,10 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
|||
("material", Loc.GetString(matProto.Name)));
|
||||
|
||||
msg.AddMarkup(text);
|
||||
msg.PushNewline();
|
||||
|
||||
if (i != orderedCosts.Length - 1)
|
||||
msg.PushNewline();
|
||||
}
|
||||
msg.Pop();
|
||||
|
||||
return msg.ToMarkup();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ public sealed class ColorFlashEffectSystem : SharedColorFlashEffectSystem
|
|||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly AnimationPlayerSystem _animation = default!;
|
||||
[Dependency] private readonly IComponentFactory _factory = default!;
|
||||
|
||||
/// <summary>
|
||||
/// It's a little on the long side but given we use multiple colours denoting what happened it makes it easier to register.
|
||||
|
|
@ -86,8 +87,13 @@ public sealed class ColorFlashEffectSystem : SharedColorFlashEffectSystem
|
|||
continue;
|
||||
}
|
||||
|
||||
var player = EnsureComp<AnimationPlayerComponent>(ent);
|
||||
player.NetSyncEnabled = false;
|
||||
if (!TryComp(ent, out AnimationPlayerComponent? player))
|
||||
{
|
||||
player = (AnimationPlayerComponent) _factory.GetComponent(typeof(AnimationPlayerComponent));
|
||||
player.Owner = ent;
|
||||
player.NetSyncEnabled = false;
|
||||
AddComp(ent, player);
|
||||
}
|
||||
|
||||
// Need to stop the existing animation first to ensure the sprite color is fixed.
|
||||
// Otherwise we might lerp to a red colour instead.
|
||||
|
|
@ -111,8 +117,14 @@ public sealed class ColorFlashEffectSystem : SharedColorFlashEffectSystem
|
|||
if (animation == null)
|
||||
continue;
|
||||
|
||||
var comp = EnsureComp<ColorFlashEffectComponent>(ent);
|
||||
comp.NetSyncEnabled = false;
|
||||
if (!TryComp(ent, out ColorFlashEffectComponent? comp))
|
||||
{
|
||||
comp = (ColorFlashEffectComponent) _factory.GetComponent(typeof(ColorFlashEffectComponent));
|
||||
comp.Owner = ent;
|
||||
comp.NetSyncEnabled = false;
|
||||
AddComp(ent, comp);
|
||||
}
|
||||
|
||||
comp.Color = sprite.Color;
|
||||
_animation.Play((ent, player), animation, AnimationKey);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -366,13 +366,14 @@ namespace Content.Client.Examine
|
|||
var canSeeClearly = !HasComp<BlurryVisionComponent>(playerEnt);
|
||||
|
||||
OpenTooltip(playerEnt.Value, entity, centeredOnCursor, false, knowTarget: canSeeClearly);
|
||||
if (IsClientSide(entity)
|
||||
|| _client.RunLevel == ClientRunLevel.SinglePlayerGame) // i.e. a replay
|
||||
{
|
||||
message = GetExamineText(entity, playerEnt);
|
||||
UpdateTooltipInfo(playerEnt.Value, entity, message);
|
||||
}
|
||||
else
|
||||
|
||||
// Always update tooltip info from client first.
|
||||
// If we get it wrong, server will correct us later anyway.
|
||||
// This will usually be correct (barring server-only components, which generally only adds, not replaces text)
|
||||
message = GetExamineText(entity, playerEnt);
|
||||
UpdateTooltipInfo(playerEnt.Value, entity, message);
|
||||
|
||||
if (!IsClientSide(entity))
|
||||
{
|
||||
// Ask server for extra examine info.
|
||||
if (entity != _lastExaminedEntity)
|
||||
|
|
@ -383,7 +384,6 @@ namespace Content.Client.Examine
|
|||
}
|
||||
|
||||
RaiseLocalEvent(entity, new ClientExaminedEvent(entity, playerEnt.Value));
|
||||
|
||||
_lastExaminedEntity = entity;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ namespace Content.Client.GameTicking.Managers
|
|||
[ViewVariables] public bool AreWeReady { get; private set; }
|
||||
[ViewVariables] public bool IsGameStarted { get; private set; }
|
||||
[ViewVariables] public string? LobbySong { get; private set; }
|
||||
[ViewVariables] public string? RestartSound { get; private set; }
|
||||
[ViewVariables] public string? LobbyBackground { get; private set; }
|
||||
[ViewVariables] public bool DisallowedLateJoin { get; private set; }
|
||||
[ViewVariables] public string? ServerInfoBlob { get; private set; }
|
||||
|
|
@ -58,6 +57,7 @@ namespace Content.Client.GameTicking.Managers
|
|||
|
||||
SubscribeNetworkEvent<TickerJoinLobbyEvent>(JoinLobby);
|
||||
SubscribeNetworkEvent<TickerJoinGameEvent>(JoinGame);
|
||||
SubscribeNetworkEvent<TickerConnectionStatusEvent>(ConnectionStatus);
|
||||
SubscribeNetworkEvent<TickerLobbyStatusEvent>(LobbyStatus);
|
||||
SubscribeNetworkEvent<TickerLobbyInfoEvent>(LobbyInfo);
|
||||
SubscribeNetworkEvent<TickerLobbyCountdownEvent>(LobbyCountdown);
|
||||
|
|
@ -68,7 +68,6 @@ namespace Content.Client.GameTicking.Managers
|
|||
});
|
||||
SubscribeNetworkEvent<TickerLateJoinStatusEvent>(LateJoinStatus);
|
||||
SubscribeNetworkEvent<TickerJobsAvailableEvent>(UpdateJobsAvailable);
|
||||
SubscribeNetworkEvent<RoundRestartCleanupEvent>(RoundRestartCleanup);
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
|
@ -112,6 +111,11 @@ namespace Content.Client.GameTicking.Managers
|
|||
_stateManager.RequestStateChange<LobbyState>();
|
||||
}
|
||||
|
||||
private void ConnectionStatus(TickerConnectionStatusEvent message)
|
||||
{
|
||||
RoundStartTimeSpan = message.RoundStartTimeSpan;
|
||||
}
|
||||
|
||||
private void LobbyStatus(TickerLobbyStatusEvent message)
|
||||
{
|
||||
StartTime = message.StartTime;
|
||||
|
|
@ -147,7 +151,6 @@ namespace Content.Client.GameTicking.Managers
|
|||
{
|
||||
// Force an update in the event of this song being the same as the last.
|
||||
SetLobbySong(message.LobbySong, true);
|
||||
RestartSound = message.RestartSound;
|
||||
|
||||
// Don't open duplicate windows (mainly for replays).
|
||||
if (_window?.RoundId == message.RoundId)
|
||||
|
|
@ -156,22 +159,5 @@ namespace Content.Client.GameTicking.Managers
|
|||
//This is not ideal at all, but I don't see an immediately better fit anywhere else.
|
||||
_window = new RoundEndSummaryWindow(message.GamemodeTitle, message.RoundEndText, message.RoundDuration, message.RoundId, message.AllPlayersEndInfo, _entityManager);
|
||||
}
|
||||
|
||||
private void RoundRestartCleanup(RoundRestartCleanupEvent ev)
|
||||
{
|
||||
if (string.IsNullOrEmpty(RestartSound))
|
||||
return;
|
||||
|
||||
if (!_configManager.GetCVar(CCVars.RestartSoundsEnabled))
|
||||
{
|
||||
RestartSound = null;
|
||||
return;
|
||||
}
|
||||
|
||||
_audio.PlayGlobal(RestartSound, Filter.Local(), false);
|
||||
|
||||
// Cleanup the sound, we only want it to play when the round restarts after it ends normally.
|
||||
RestartSound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
using System;
|
||||
using Robust.Client;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Client.Launcher
|
||||
{
|
||||
|
|
@ -13,6 +16,9 @@ namespace Content.Client.Launcher
|
|||
[Dependency] private readonly IClientNetManager _clientNetManager = default!;
|
||||
[Dependency] private readonly IGameController _gameController = default!;
|
||||
[Dependency] private readonly IBaseClient _baseClient = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
private LauncherConnectingGui? _control;
|
||||
|
||||
|
|
@ -51,7 +57,7 @@ namespace Content.Client.Launcher
|
|||
|
||||
protected override void Startup()
|
||||
{
|
||||
_control = new LauncherConnectingGui(this);
|
||||
_control = new LauncherConnectingGui(this, _random, _prototypeManager, _cfg);
|
||||
|
||||
_userInterfaceManager.StateRoot.AddChild(_control);
|
||||
|
||||
|
|
|
|||
|
|
@ -54,4 +54,17 @@
|
|||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
<!-- Bottom window for tips -->
|
||||
<PanelContainer Name="LoginTips" StyleClasses="AngleRect" Margin="0 10" MaxWidth="600" VerticalExpand="True" VerticalAlignment="Bottom">
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True">
|
||||
<controls:StripeBack>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<Label Name="LoginTipTitle" Text="Tip" StyleClasses="LabelHeading" Align="Center"/>
|
||||
</BoxContainer>
|
||||
</controls:StripeBack>
|
||||
<BoxContainer Orientation="Vertical" Margin="5 5 5 5" >
|
||||
<RichTextLabel Name="LoginTip" VerticalExpand="True" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</Control>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,18 @@
|
|||
using System.Linq;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Dataset;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Client.Launcher
|
||||
{
|
||||
|
|
@ -15,11 +21,19 @@ namespace Content.Client.Launcher
|
|||
{
|
||||
private const float RedialWaitTimeSeconds = 15f;
|
||||
private readonly LauncherConnecting _state;
|
||||
private readonly IRobustRandom _random;
|
||||
private readonly IPrototypeManager _prototype;
|
||||
private readonly IConfigurationManager _cfg;
|
||||
|
||||
private float _redialWaitTime = RedialWaitTimeSeconds;
|
||||
|
||||
public LauncherConnectingGui(LauncherConnecting state)
|
||||
public LauncherConnectingGui(LauncherConnecting state, IRobustRandom random,
|
||||
IPrototypeManager prototype, IConfigurationManager config)
|
||||
{
|
||||
_state = state;
|
||||
_random = random;
|
||||
_prototype = prototype;
|
||||
_cfg = config;
|
||||
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
|
|
@ -27,6 +41,7 @@ namespace Content.Client.Launcher
|
|||
|
||||
Stylesheet = IoCManager.Resolve<IStylesheetManager>().SheetSpace;
|
||||
|
||||
ChangeLoginTip();
|
||||
ReconnectButton.OnPressed += _ => _state.RetryConnect();
|
||||
// Redial shouldn't fail, but if it does, try a reconnect (maybe we're being run from debug)
|
||||
RedialButton.OnPressed += _ =>
|
||||
|
|
@ -67,6 +82,29 @@ namespace Content.Client.Launcher
|
|||
ReconnectButton.Visible = !redialFlag;
|
||||
}
|
||||
|
||||
private void ChangeLoginTip()
|
||||
{
|
||||
var tipsDataset = _cfg.GetCVar(CCVars.LoginTipsDataset);
|
||||
var loginTipsEnabled = _prototype.TryIndex<DatasetPrototype>(tipsDataset, out var tips);
|
||||
|
||||
LoginTips.Visible = loginTipsEnabled;
|
||||
if (!loginTipsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var tipList = tips!.Values;
|
||||
|
||||
if (tipList.Count == 0)
|
||||
return;
|
||||
|
||||
var randomIndex = _random.Next(tipList.Count);
|
||||
var tip = tipList[randomIndex];
|
||||
LoginTip.SetMessage(tip);
|
||||
|
||||
LoginTipTitle.Text = Loc.GetString("connecting-window-tip", ("numberTip", randomIndex));
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
|
|
|||
|
|
@ -148,7 +148,8 @@ namespace Content.Client.Lobby.UI
|
|||
foreach (var slot in slots)
|
||||
{
|
||||
var itemType = gear.GetGear(slot.Name, profile);
|
||||
if (invSystem.TryUnequip(dummy, slot.Name, out var unequippedItem, true, true))
|
||||
|
||||
if (invSystem.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
|
||||
{
|
||||
entMan.DeleteEntity(unequippedItem.Value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ public sealed class MagicMirrorBoundUserInterface : BoundUserInterface
|
|||
_window.OnFacialHairSlotAdded += delegate () { AddSlot(MagicMirrorCategory.FacialHair); };
|
||||
_window.OnFacialHairSlotRemoved += args => RemoveSlot(MagicMirrorCategory.FacialHair, args);
|
||||
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
|
|
@ -53,17 +54,18 @@ public sealed class MagicMirrorBoundUserInterface : BoundUserInterface
|
|||
SendMessage(new MagicMirrorAddSlotMessage(category));
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.ReceiveMessage(message);
|
||||
base.UpdateState(state);
|
||||
|
||||
if (message is not MagicMirrorUiData data || _window == null)
|
||||
if (state is not MagicMirrorUiState data || _window == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_window.UpdateState(data);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@ public sealed partial class MagicMirrorWindow : DefaultWindow
|
|||
public Action<int>? OnFacialHairSlotRemoved;
|
||||
public Action? OnFacialHairSlotAdded;
|
||||
|
||||
private bool _noHair;
|
||||
|
||||
public MagicMirrorWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
|
@ -38,15 +36,14 @@ public sealed partial class MagicMirrorWindow : DefaultWindow
|
|||
FacialHairPicker.OnSlotAdd += delegate { OnFacialHairSlotAdded!(); };
|
||||
}
|
||||
|
||||
public void UpdateState(MagicMirrorUiData state)
|
||||
public void UpdateState(MagicMirrorUiState state)
|
||||
{
|
||||
HairPicker.UpdateData(state.Hair, state.Species, state.HairSlotTotal);
|
||||
FacialHairPicker.UpdateData(state.FacialHair, state.Species, state.FacialHairSlotTotal);
|
||||
|
||||
if (!HairPicker.Visible && !FacialHairPicker.Visible && !_noHair)
|
||||
if (!HairPicker.Visible && !FacialHairPicker.Visible)
|
||||
{
|
||||
AddChild(new Label { Text = Loc.GetString("magic-mirror-component-activate-user-has-no-hair") });
|
||||
_noHair = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,20 +28,24 @@
|
|||
HorizontalExpand="True"
|
||||
MinSize="50 50"
|
||||
Margin="8">
|
||||
<RichTextLabel Name="PdaOwnerLabel" Access="Public" />
|
||||
<RichTextLabel Name="IdInfoLabel"
|
||||
Access="Public"
|
||||
HorizontalExpand="True" />
|
||||
<PanelContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<RichTextLabel Name="StationNameLabel"
|
||||
Access="Public"
|
||||
HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
<RichTextLabel Name="StationAlertLevelLabel" Access="Public" />
|
||||
<RichTextLabel Name="StationTimeLabel" Access="Public" />
|
||||
<RichTextLabel Name="StationAlertLevelInstructions" Access="Public" />
|
||||
<ContainerButton Name="PdaOwnerButton">
|
||||
<RichTextLabel Name="PdaOwnerLabel" Access="Public"/>
|
||||
</ContainerButton>
|
||||
<ContainerButton Name="IdInfoButton">
|
||||
<RichTextLabel Name="IdInfoLabel" Access="Public"/>
|
||||
</ContainerButton>
|
||||
<ContainerButton Name="StationNameButton">
|
||||
<RichTextLabel Name="StationNameLabel" Access="Public"/>
|
||||
</ContainerButton>
|
||||
<ContainerButton Name="StationAlertLevelButton">
|
||||
<RichTextLabel Name="StationAlertLevelLabel" Access="Public"/>
|
||||
</ContainerButton>
|
||||
<ContainerButton Name="StationTimeButton">
|
||||
<RichTextLabel Name="StationTimeLabel" Access="Public"/>
|
||||
</ContainerButton>
|
||||
<ContainerButton Name="StationAlertLevelInstructionsButton">
|
||||
<RichTextLabel Name="StationAlertLevelInstructions" Access="Public"/>
|
||||
</ContainerButton>
|
||||
</BoxContainer>
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" HScrollEnabled="True">
|
||||
<BoxContainer Orientation="Vertical"
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ namespace Content.Client.PDA
|
|||
[GenerateTypedNameReferences]
|
||||
public sealed partial class PdaMenu : PdaWindow
|
||||
{
|
||||
[Dependency] private readonly IClipboardManager _clipboard = null!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||
private readonly ClientGameTicker _gameTicker;
|
||||
|
|
@ -24,6 +25,15 @@ namespace Content.Client.PDA
|
|||
public const int SettingsView = 2;
|
||||
public const int ProgramContentView = 3;
|
||||
|
||||
|
||||
private string _pdaOwner = Loc.GetString("comp-pda-ui-unknown");
|
||||
private string _owner = Loc.GetString("comp-pda-ui-unknown");
|
||||
private string _jobTitle = Loc.GetString("comp-pda-ui-unassigned");
|
||||
private string _stationName = Loc.GetString("comp-pda-ui-unknown");
|
||||
private string _alertLevel = Loc.GetString("comp-pda-ui-unknown");
|
||||
private string _instructions = Loc.GetString("comp-pda-ui-unknown");
|
||||
|
||||
|
||||
private int _currentView;
|
||||
|
||||
public event Action<EntityUid>? OnProgramItemPressed;
|
||||
|
|
@ -41,7 +51,7 @@ namespace Content.Client.PDA
|
|||
FlashLightToggleButton.IconTexture = new SpriteSpecifier.Texture(new("/Textures/Interface/light.png"));
|
||||
EjectPenButton.IconTexture = new SpriteSpecifier.Texture(new("/Textures/Interface/pencil.png"));
|
||||
EjectIdButton.IconTexture = new SpriteSpecifier.Texture(new("/Textures/Interface/eject.png"));
|
||||
EjectPaiButton.IconTexture = new SpriteSpecifier.Texture(new("/Textures/Interface/PAI.png"));
|
||||
EjectPaiButton.IconTexture = new SpriteSpecifier.Texture(new("/Textures/Interface/pai.png"));
|
||||
ProgramCloseButton.IconTexture = new SpriteSpecifier.Texture(new("/Textures/Interface/Nano/cross.svg.png"));
|
||||
|
||||
|
||||
|
|
@ -84,6 +94,39 @@ namespace Content.Client.PDA
|
|||
ToHomeScreen();
|
||||
};
|
||||
|
||||
PdaOwnerButton.OnPressed += _ =>
|
||||
{
|
||||
_clipboard.SetText(_pdaOwner);
|
||||
};
|
||||
|
||||
IdInfoButton.OnPressed += _ =>
|
||||
{
|
||||
_clipboard.SetText(_owner + ", " + _jobTitle);
|
||||
};
|
||||
|
||||
StationNameButton.OnPressed += _ =>
|
||||
{
|
||||
_clipboard.SetText(_stationName);
|
||||
};
|
||||
|
||||
StationAlertLevelButton.OnPressed += _ =>
|
||||
{
|
||||
_clipboard.SetText(_alertLevel);
|
||||
};
|
||||
|
||||
StationTimeButton.OnPressed += _ =>
|
||||
{
|
||||
var stationTime = _gameTiming.CurTime.Subtract(_gameTicker.RoundStartTimeSpan);
|
||||
_clipboard.SetText((stationTime.ToString("hh\\:mm\\:ss")));
|
||||
};
|
||||
|
||||
StationAlertLevelInstructionsButton.OnPressed += _ =>
|
||||
{
|
||||
_clipboard.SetText(_instructions);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
HideAllViews();
|
||||
ToHomeScreen();
|
||||
|
|
@ -95,24 +138,29 @@ namespace Content.Client.PDA
|
|||
|
||||
if (state.PdaOwnerInfo.ActualOwnerName != null)
|
||||
{
|
||||
_pdaOwner = state.PdaOwnerInfo.ActualOwnerName;
|
||||
PdaOwnerLabel.SetMarkup(Loc.GetString("comp-pda-ui-owner",
|
||||
("actualOwnerName", state.PdaOwnerInfo.ActualOwnerName)));
|
||||
("actualOwnerName", _pdaOwner)));
|
||||
}
|
||||
|
||||
|
||||
if (state.PdaOwnerInfo.IdOwner != null || state.PdaOwnerInfo.JobTitle != null)
|
||||
{
|
||||
_owner = state.PdaOwnerInfo.IdOwner ?? Loc.GetString("comp-pda-ui-unknown");
|
||||
_jobTitle = state.PdaOwnerInfo.JobTitle ?? Loc.GetString("comp-pda-ui-unassigned");
|
||||
IdInfoLabel.SetMarkup(Loc.GetString("comp-pda-ui",
|
||||
("owner", state.PdaOwnerInfo.IdOwner ?? Loc.GetString("comp-pda-ui-unknown")),
|
||||
("jobTitle", state.PdaOwnerInfo.JobTitle ?? Loc.GetString("comp-pda-ui-unassigned"))));
|
||||
("owner", _owner),
|
||||
("jobTitle", _jobTitle)));
|
||||
}
|
||||
else
|
||||
{
|
||||
IdInfoLabel.SetMarkup(Loc.GetString("comp-pda-ui-blank"));
|
||||
}
|
||||
|
||||
_stationName = state.StationName ?? Loc.GetString("comp-pda-ui-unknown");
|
||||
StationNameLabel.SetMarkup(Loc.GetString("comp-pda-ui-station",
|
||||
("station", state.StationName ?? Loc.GetString("comp-pda-ui-unknown"))));
|
||||
("station", _stationName)));
|
||||
|
||||
|
||||
var stationTime = _gameTiming.CurTime.Subtract(_gameTicker.RoundStartTimeSpan);
|
||||
|
||||
|
|
@ -122,16 +170,17 @@ namespace Content.Client.PDA
|
|||
var alertLevel = state.PdaOwnerInfo.StationAlertLevel;
|
||||
var alertColor = state.PdaOwnerInfo.StationAlertColor;
|
||||
var alertLevelKey = alertLevel != null ? $"alert-level-{alertLevel}" : "alert-level-unknown";
|
||||
_alertLevel = Loc.GetString(alertLevelKey);
|
||||
|
||||
StationAlertLevelLabel.SetMarkup(Loc.GetString(
|
||||
"comp-pda-ui-station-alert-level",
|
||||
("color", alertColor),
|
||||
("level", Loc.GetString(alertLevelKey))
|
||||
("level", _alertLevel)
|
||||
));
|
||||
|
||||
_instructions = Loc.GetString($"{alertLevelKey}-instructions");
|
||||
StationAlertLevelInstructions.SetMarkup(Loc.GetString(
|
||||
"comp-pda-ui-station-alert-level-instructions",
|
||||
("instructions", Loc.GetString($"{alertLevelKey}-instructions")))
|
||||
("instructions", _instructions))
|
||||
);
|
||||
|
||||
AddressLabel.Text = state.Address?.ToUpper() ?? " - ";
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ public sealed class NavMapBeaconBoundUserInterface : BoundUserInterface
|
|||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new NavMapBeaconWindow(Owner);
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Pinpointer.UI;
|
||||
|
||||
public sealed class UntrackedStationMapBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
private StationMapWindow? _window;
|
||||
|
||||
public UntrackedStationMapBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_window?.Close();
|
||||
EntityUid? gridUid = null;
|
||||
|
||||
if (EntMan.TryGetComponent<TransformComponent>(Owner, out var xform))
|
||||
{
|
||||
gridUid = xform.GridUid;
|
||||
}
|
||||
|
||||
_window = new StationMapWindow(gridUid, null);
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'salvage-expedition-window-title'}"
|
||||
MinSize="800 360">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal" Name="ProgressionBox" Visible="False">
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ public sealed class SalvageExpeditionConsoleBoundUserInterface : BoundUserInterf
|
|||
{
|
||||
base.Open();
|
||||
_window = new OfferingWindow();
|
||||
_window.Title = Loc.GetString("salvage-expedition-window-title");
|
||||
_window.OnClose += Close;
|
||||
_window?.OpenCenteredLeft();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ public sealed class SalvageMagnetBoundUserInterface : BoundUserInterface
|
|||
{
|
||||
base.Open();
|
||||
_window = new OfferingWindow();
|
||||
_window.Title = Loc.GetString("salvage-magnet-window-title");
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCenteredLeft();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -267,16 +267,14 @@ public sealed class StorageContainer : BaseWindow
|
|||
|
||||
var currentPosition = new Vector2i(x, y);
|
||||
|
||||
foreach (var item in storageComp.StoredItems)
|
||||
foreach (var (itemEnt, itemPos) in storageComp.StoredItems)
|
||||
{
|
||||
if (item.Value.Position != currentPosition)
|
||||
if (itemPos.Position != currentPosition)
|
||||
continue;
|
||||
|
||||
var itemEnt = _entity.GetEntity(item.Key);
|
||||
|
||||
if (_entity.TryGetComponent<ItemComponent>(itemEnt, out var itemEntComponent))
|
||||
{
|
||||
var gridPiece = new ItemGridPiece((itemEnt, itemEntComponent), item.Value, _entity)
|
||||
var gridPiece = new ItemGridPiece((itemEnt, itemEntComponent), itemPos, _entity)
|
||||
{
|
||||
MinSize = size,
|
||||
Marked = itemEnt == lastEntity
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
|||
var useDown = _inputSystem.CmdStates.GetState(EngineKeyFunctions.Use);
|
||||
var altDown = _inputSystem.CmdStates.GetState(EngineKeyFunctions.UseSecondary);
|
||||
|
||||
if (useDown != BoundKeyState.Down && altDown != BoundKeyState.Down)
|
||||
if (weapon.AutoAttack || useDown != BoundKeyState.Down && altDown != BoundKeyState.Down)
|
||||
{
|
||||
if (weapon.Attacking)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||
[Dependency] private readonly AnimationPlayerSystem _animPlayer = default!;
|
||||
[Dependency] private readonly InputSystem _inputSystem = default!;
|
||||
[Dependency] private readonly SharedCameraRecoilSystem _recoil = default!;
|
||||
[Dependency] private readonly IComponentFactory _factory = default!;
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
public const string HitscanProto = "HitscanEffect";
|
||||
|
|
@ -309,8 +310,14 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||
};
|
||||
|
||||
_animPlayer.Play(ent, anim, "muzzle-flash");
|
||||
var light = EnsureComp<PointLightComponent>(uid);
|
||||
light.NetSyncEnabled = false;
|
||||
if (!TryComp(uid, out PointLightComponent? light))
|
||||
{
|
||||
light = (PointLightComponent) _factory.GetComponent(typeof(PointLightComponent));
|
||||
light.Owner = uid;
|
||||
light.NetSyncEnabled = false;
|
||||
AddComp(uid, light);
|
||||
}
|
||||
|
||||
Lights.SetEnabled(uid, true, light);
|
||||
Lights.SetRadius(uid, 2f, light);
|
||||
Lights.SetColor(uid, Color.FromHex("#cc8e2b"), light);
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public sealed class ComputerConstruction : InteractionTest
|
|||
Screw,
|
||||
Pry,
|
||||
Wrench,
|
||||
Weld);
|
||||
Screw);
|
||||
|
||||
// construction finished, entity no longer exists.
|
||||
AssertDeleted();
|
||||
|
|
|
|||
|
|
@ -56,34 +56,5 @@ public sealed class MachineConstruction : InteractionTest
|
|||
await Interact(Bin1, Bin1, Bin1, Manipulator1, Glass, Screw);
|
||||
AssertPrototype("Autolathe");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpgradeLathe()
|
||||
{
|
||||
// Partially deconstruct a protolathe.
|
||||
await SpawnTarget(Protolathe);
|
||||
var serverTarget = SEntMan.GetEntity(Target!.Value);
|
||||
|
||||
// Initially has all quality-1 parts.
|
||||
foreach (var part in SConstruction.GetAllParts(serverTarget))
|
||||
{
|
||||
Assert.That(part.Rating, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
// Partially deconstruct lathe
|
||||
await Interact(Screw, Pry, Pry);
|
||||
AssertPrototype(MachineFrame);
|
||||
|
||||
// Reconstruct with better parts.
|
||||
await Interact(ProtolatheBoard, Bin4, Bin4, Manipulator4, Manipulator4, Beaker, Beaker);
|
||||
await Interact(Screw);
|
||||
AssertPrototype(Protolathe);
|
||||
|
||||
// Query now returns higher quality parts.
|
||||
foreach (var part in SConstruction.GetAllParts(SEntMan.GetEntity(Target!.Value)))
|
||||
{
|
||||
Assert.That(part.Rating, Is.EqualTo(4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
namespace Content.IntegrationTests.Tests.Interaction;
|
||||
|
||||
// This partial class contains various constant prototype IDs common to interaction tests.
|
||||
|
|
@ -28,12 +27,8 @@ public abstract partial class InteractionTest
|
|||
|
||||
// Parts
|
||||
protected const string Bin1 = "MatterBinStockPart";
|
||||
protected const string Bin4 = "BluespaceMatterBinStockPart";
|
||||
protected const string Cap1 = "CapacitorStockPart";
|
||||
protected const string Cap4 = "QuadraticCapacitorStockPart";
|
||||
protected const string Manipulator1 = "MicroManipulatorStockPart";
|
||||
protected const string Manipulator4 = "FemtoManipulatorStockPart";
|
||||
protected const string Battery1 = "PowerCellSmall";
|
||||
protected const string Battery4 = "PowerCellHyper";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ namespace Content.IntegrationTests.Tests
|
|||
"CentComm",
|
||||
"Box",
|
||||
"Europa",
|
||||
"Barratry",
|
||||
"Saltern",
|
||||
"Core",
|
||||
"Marathon",
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ using Robust.Shared.Network;
|
|||
|
||||
namespace Content.Server.Administration;
|
||||
|
||||
public sealed class BanPanelEui : BaseEui, IPostInjectInit
|
||||
public sealed class BanPanelEui : BaseEui
|
||||
{
|
||||
[Dependency] private readonly IBanManager _banManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entities = default!;
|
||||
|
|
@ -23,7 +23,7 @@ public sealed class BanPanelEui : BaseEui, IPostInjectInit
|
|||
[Dependency] private readonly IChatManager _chat = default!;
|
||||
[Dependency] private readonly IAdminManager _admins = default!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
private readonly ISawmill _sawmill;
|
||||
|
||||
private NetUserId? PlayerId { get; set; }
|
||||
private string PlayerName { get; set; } = string.Empty;
|
||||
|
|
@ -35,6 +35,8 @@ public sealed class BanPanelEui : BaseEui, IPostInjectInit
|
|||
public BanPanelEui()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_sawmill = _log.GetSawmill("admin.bans_eui");
|
||||
}
|
||||
|
||||
public override EuiStateBase GetNewState()
|
||||
|
|
@ -185,9 +187,4 @@ public sealed class BanPanelEui : BaseEui, IPostInjectInit
|
|||
|
||||
StateDirty();
|
||||
}
|
||||
|
||||
public void PostInject()
|
||||
{
|
||||
_sawmill = _log.GetSawmill("admin.bans_eui");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,17 @@ namespace Content.Server.Advertise
|
|||
_chat.TrySendInGameICMessage(uid, Loc.GetString(_random.Pick(advertisements.Advertisements)), InGameICChatType.Speak, true);
|
||||
}
|
||||
|
||||
public void SayThankYou(EntityUid uid, AdvertiseComponent? advertise = null)
|
||||
{
|
||||
if (!Resolve(uid, ref advertise))
|
||||
return;
|
||||
|
||||
if (_prototypeManager.TryIndex(advertise.PackPrototypeId, out AdvertisementsPackPrototype? advertisements))
|
||||
{
|
||||
_chat.TrySendInGameICMessage(uid, Loc.GetString(_random.Pick(advertisements.ThankYous), ("name", Name(uid))), InGameICChatType.Speak, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetEnabled(EntityUid uid, bool enable, AdvertiseComponent? advertise = null)
|
||||
{
|
||||
if (!Resolve(uid, ref advertise))
|
||||
|
|
|
|||
|
|
@ -11,5 +11,8 @@ namespace Content.Server.Advertisements
|
|||
|
||||
[DataField("advertisements")]
|
||||
public List<string> Advertisements { get; private set; } = new();
|
||||
|
||||
[DataField("thankyous")]
|
||||
public List<string> ThankYous { get; private set; } = new();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ using Robust.Server.Player;
|
|||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
|
|
@ -35,7 +34,6 @@ namespace Content.Server.Afk
|
|||
[UsedImplicitly]
|
||||
public sealed class AfkManager : IAfkManager
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
|
|
|||
21
Content.Server/Antag/Mimic/MobReplacementRuleComponent.cs
Normal file
21
Content.Server/Antag/Mimic/MobReplacementRuleComponent.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Antag.Mimic;
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the relevant entities with mobs when the game rule is started.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class MobReplacementRuleComponent : Component
|
||||
{
|
||||
// If you want more components use generics, using a whitelist would probably kill the server iterating every single entity.
|
||||
|
||||
[DataField]
|
||||
public EntProtoId Proto = "MobMimic";
|
||||
|
||||
/// <summary>
|
||||
/// Chance per-entity.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float Chance = 0.001f;
|
||||
}
|
||||
37
Content.Server/Antag/MobReplacementRuleSystem.cs
Normal file
37
Content.Server/Antag/MobReplacementRuleSystem.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
using Content.Server.Antag.Mimic;
|
||||
using Content.Server.GameTicking.Rules;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Shared.VendingMachines;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Antag;
|
||||
|
||||
public sealed class MobReplacementRuleSystem : GameRuleSystem<MobReplacementRuleComponent>
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
protected override void Started(EntityUid uid, MobReplacementRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
|
||||
var query = AllEntityQuery<VendingMachineComponent, TransformComponent>();
|
||||
var spawns = new List<(EntityUid Entity, EntityCoordinates Coordinates)>();
|
||||
|
||||
while (query.MoveNext(out var vendingUid, out _, out var xform))
|
||||
{
|
||||
if (!_random.Prob(component.Chance))
|
||||
continue;
|
||||
|
||||
spawns.Add((vendingUid, xform.Coordinates));
|
||||
}
|
||||
|
||||
foreach (var entity in spawns)
|
||||
{
|
||||
var coordinates = entity.Coordinates;
|
||||
Del(entity.Entity);
|
||||
|
||||
Spawn(component.Proto, coordinates);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,4 +13,8 @@ public sealed partial class IgniteOnCollideComponent : Component
|
|||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("fireStacks")]
|
||||
public float FireStacks;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("fixtureId")]
|
||||
public string FixtureId = "ignition";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ using Content.Shared.Database;
|
|||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Projectiles;
|
||||
using Content.Shared.Rejuvenate;
|
||||
using Content.Shared.Temperature;
|
||||
using Content.Shared.Throwing;
|
||||
|
|
@ -102,6 +103,12 @@ namespace Content.Server.Atmos.EntitySystems
|
|||
if (!EntityManager.TryGetComponent(otherEnt, out FlammableComponent? flammable))
|
||||
return;
|
||||
|
||||
//Only ignite when the colliding fixture is projectile or ignition.
|
||||
if (args.OurFixtureId != component.FixtureId && args.OurFixtureId != SharedProjectileSystem.ProjectileFixture)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
flammable.FireStacks += component.FireStacks;
|
||||
Ignite(otherEnt, uid, flammable);
|
||||
component.Count--;
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||
|
||||
private void OnExamined(EntityUid uid, GasTankComponent component, ExaminedEvent args)
|
||||
{
|
||||
using(args.PushGroup(nameof(GasTankComponent)));
|
||||
if (args.IsInDetailsRange)
|
||||
args.PushMarkup(Loc.GetString("comp-gas-tank-examine", ("pressure", Math.Round(component.Air?.Pressure ?? 0))));
|
||||
if (component.IsConnected)
|
||||
|
|
|
|||
|
|
@ -51,20 +51,23 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
|||
return;
|
||||
}
|
||||
|
||||
if (comp.Reacting)
|
||||
using (args.PushGroup(nameof(GasRecyclerComponent)))
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("gas-recycler-reacting"));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (inlet.Air.Pressure < comp.MinPressure)
|
||||
if (comp.Reacting)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("gas-recycler-low-pressure"));
|
||||
args.PushMarkup(Loc.GetString("gas-recycler-reacting"));
|
||||
}
|
||||
|
||||
if (inlet.Air.Temperature < comp.MinTemp)
|
||||
else
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("gas-recycler-low-temperature"));
|
||||
if (inlet.Air.Pressure < comp.MinPressure)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("gas-recycler-low-pressure"));
|
||||
}
|
||||
|
||||
if (inlet.Air.Temperature < comp.MinTemp)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("gas-recycler-low-temperature"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Piping.Unary.Components;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Unary.Components
|
||||
{
|
||||
|
|
@ -15,24 +12,16 @@ namespace Content.Server.Atmos.Piping.Unary.Components
|
|||
/// Current electrical power consumption, in watts. Increasing power increases the ability of the
|
||||
/// thermomachine to heat or cool air.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float HeatCapacity = 10000;
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float HeatCapacity = 5000;
|
||||
|
||||
/// <summary>
|
||||
/// Base heat capacity of the device. Actual heat capacity is calculated by taking this number and doubling
|
||||
/// it for every matter bin quality tier above one.
|
||||
/// </summary>
|
||||
[DataField("baseHeatCapacity")]
|
||||
public float BaseHeatCapacity = 5000;
|
||||
|
||||
[DataField("targetTemperature")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float TargetTemperature = Atmospherics.T20C;
|
||||
|
||||
/// <summary>
|
||||
/// Tolerance for temperature setpoint hysteresis.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
||||
public float TemperatureTolerance = 2f;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -40,7 +29,7 @@ namespace Content.Server.Atmos.Piping.Unary.Components
|
|||
/// If true, add Sign(Cp)*TemperatureTolerance to the temperature setpoint.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public bool HysteresisState = false;
|
||||
public bool HysteresisState;
|
||||
|
||||
/// <summary>
|
||||
/// Coefficient of performance. Output power / input power.
|
||||
|
|
@ -51,68 +40,29 @@ namespace Content.Server.Atmos.Piping.Unary.Components
|
|||
public float Cp = 0.9f; // output power / input power, positive is heat
|
||||
|
||||
/// <summary>
|
||||
/// Current minimum temperature, calculated from <see cref="InitialMinTemperature"/> and <see
|
||||
/// cref="MinTemperatureDelta"/>.
|
||||
/// Current minimum temperature
|
||||
/// Ignored if heater.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float MinTemperature;
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float MinTemperature = 73.15f;
|
||||
|
||||
/// <summary>
|
||||
/// Current maximum temperature, calculated from <see cref="InitialMaxTemperature"/> and <see
|
||||
/// cref="MaxTemperatureDelta"/>.
|
||||
/// Current maximum temperature
|
||||
/// Ignored if freezer.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float MaxTemperature;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum temperature the device can reach with a 0 total capacitor quality. Usually the quality will be at
|
||||
/// least 1.
|
||||
/// </summary>
|
||||
[DataField("baseMinTemperature")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseMinTemperature = 96.625f; // Selected so that tier-1 parts can reach 73.15k
|
||||
|
||||
/// <summary>
|
||||
/// Maximum temperature the device can reach with a 0 total capacitor quality. Usually the quality will be at
|
||||
/// least 1.
|
||||
/// </summary>
|
||||
[DataField("baseMaxTemperature")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseMaxTemperature = Atmospherics.T20C;
|
||||
|
||||
/// <summary>
|
||||
/// Decrease in minimum temperature, per unit machine part quality.
|
||||
/// </summary>
|
||||
[DataField("minTemperatureDelta")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float MinTemperatureDelta = 23.475f; // selected so that tier-4 parts can reach TCMB
|
||||
|
||||
/// <summary>
|
||||
/// Change in maximum temperature, per unit machine part quality.
|
||||
/// </summary>
|
||||
[DataField("maxTemperatureDelta")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float MaxTemperatureDelta = 300;
|
||||
|
||||
/// <summary>
|
||||
/// The machine part that affects the heat capacity.
|
||||
/// </summary>
|
||||
[DataField("machinePartHeatCapacity", customTypeSerializer: typeof(PrototypeIdSerializer<MachinePartPrototype>))]
|
||||
public string MachinePartHeatCapacity = "MatterBin";
|
||||
|
||||
/// <summary>
|
||||
/// The machine part that affects the temperature range.
|
||||
/// </summary>
|
||||
[DataField("machinePartTemperature", customTypeSerializer: typeof(PrototypeIdSerializer<MachinePartPrototype>))]
|
||||
public string MachinePartTemperature = "Capacitor";
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float MaxTemperature = 593.15f;
|
||||
|
||||
/// <summary>
|
||||
/// Last amount of energy added/removed from the attached pipe network
|
||||
/// </summary>
|
||||
[DataField("lastEnergyDelta")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float LastEnergyDelta;
|
||||
|
||||
/// <summary>
|
||||
/// An percentage of the energy change that is leaked into the surrounding environment rather than the inlet pipe.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float EnergyLeakPercentage;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ using Content.Server.Atmos.EntitySystems;
|
|||
using Content.Server.Atmos.Monitor.Systems;
|
||||
using Content.Server.Atmos.Piping.Components;
|
||||
using Content.Server.Atmos.Piping.Unary.Components;
|
||||
using Content.Server.Construction;
|
||||
using Content.Server.DeviceNetwork;
|
||||
using Content.Server.DeviceNetwork.Components;
|
||||
using Content.Server.DeviceNetwork.Systems;
|
||||
|
|
@ -15,6 +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.Examine;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
||||
|
|
@ -28,17 +28,15 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
|||
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
|
||||
[Dependency] private readonly DeviceNetworkSystem _deviceNetwork = default!;
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<GasThermoMachineComponent, AtmosDeviceUpdateEvent>(OnThermoMachineUpdated);
|
||||
SubscribeLocalEvent<GasThermoMachineComponent, RefreshPartsEvent>(OnGasThermoRefreshParts);
|
||||
SubscribeLocalEvent<GasThermoMachineComponent, UpgradeExamineEvent>(OnGasThermoUpgradeExamine);
|
||||
SubscribeLocalEvent<GasThermoMachineComponent, ExaminedEvent>(OnExamined);
|
||||
|
||||
// UI events
|
||||
SubscribeLocalEvent<GasThermoMachineComponent, BeforeActivatableUIOpenEvent>(OnBeforeOpened);
|
||||
SubscribeLocalEvent<GasThermoMachineComponent, GasThermomachineToggleMessage>(OnToggleMessage);
|
||||
SubscribeLocalEvent<GasThermoMachineComponent, GasThermomachineChangeTemperatureMessage>(OnChangeTemperature);
|
||||
|
||||
|
|
@ -46,6 +44,11 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
|||
SubscribeLocalEvent<GasThermoMachineComponent, DeviceNetworkPacketEvent>(OnPacketRecv);
|
||||
}
|
||||
|
||||
private void OnBeforeOpened(Entity<GasThermoMachineComponent> ent, ref BeforeActivatableUIOpenEvent args)
|
||||
{
|
||||
DirtyUI(ent, ent.Comp);
|
||||
}
|
||||
|
||||
private void OnThermoMachineUpdated(EntityUid uid, GasThermoMachineComponent thermoMachine, ref AtmosDeviceUpdateEvent args)
|
||||
{
|
||||
if (!(_power.IsPowered(uid) && TryComp<ApcPowerReceiverComponent>(uid, out var receiver))
|
||||
|
|
@ -90,7 +93,13 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
|||
thermoMachine.HysteresisState = false; // turn off
|
||||
}
|
||||
float dQActual = dQ * scale;
|
||||
_atmosphereSystem.AddHeat(inlet.Air, dQActual);
|
||||
float dQLeak = dQActual * thermoMachine.EnergyLeakPercentage;
|
||||
float dQPipe = dQActual - dQLeak;
|
||||
_atmosphereSystem.AddHeat(inlet.Air, dQPipe);
|
||||
|
||||
if (_atmosphereSystem.GetContainingMixture(uid) is { } containingMixture)
|
||||
_atmosphereSystem.AddHeat(containingMixture, dQLeak);
|
||||
|
||||
receiver.Load = thermoMachine.HeatCapacity;// * scale; // we're not ready for dynamic load yet, see note above
|
||||
}
|
||||
|
||||
|
|
@ -99,41 +108,6 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
|||
return comp.Cp >= 0;
|
||||
}
|
||||
|
||||
private void OnGasThermoRefreshParts(EntityUid uid, GasThermoMachineComponent thermoMachine, RefreshPartsEvent args)
|
||||
{
|
||||
var heatCapacityPartRating = args.PartRatings[thermoMachine.MachinePartHeatCapacity];
|
||||
thermoMachine.HeatCapacity = thermoMachine.BaseHeatCapacity * MathF.Pow(heatCapacityPartRating, 2);
|
||||
|
||||
var temperatureRangePartRating = args.PartRatings[thermoMachine.MachinePartTemperature];
|
||||
if (IsHeater(thermoMachine))
|
||||
{
|
||||
// 593.15K with stock parts.
|
||||
thermoMachine.MaxTemperature = thermoMachine.BaseMaxTemperature + thermoMachine.MaxTemperatureDelta * temperatureRangePartRating;
|
||||
thermoMachine.MinTemperature = Atmospherics.T20C;
|
||||
}
|
||||
else {
|
||||
// 73.15K with stock parts.
|
||||
thermoMachine.MinTemperature = MathF.Max(
|
||||
thermoMachine.BaseMinTemperature - thermoMachine.MinTemperatureDelta * temperatureRangePartRating, Atmospherics.TCMB);
|
||||
thermoMachine.MaxTemperature = Atmospherics.T20C;
|
||||
}
|
||||
|
||||
DirtyUI(uid, thermoMachine);
|
||||
}
|
||||
|
||||
private void OnGasThermoUpgradeExamine(EntityUid uid, GasThermoMachineComponent thermoMachine, UpgradeExamineEvent args)
|
||||
{
|
||||
if (IsHeater(thermoMachine))
|
||||
{
|
||||
args.AddPercentageUpgrade("gas-thermo-component-upgrade-heating", thermoMachine.MaxTemperature / (thermoMachine.BaseMaxTemperature + thermoMachine.MaxTemperatureDelta));
|
||||
}
|
||||
else
|
||||
{
|
||||
args.AddPercentageUpgrade("gas-thermo-component-upgrade-cooling", thermoMachine.MinTemperature / (thermoMachine.BaseMinTemperature - thermoMachine.MinTemperatureDelta));
|
||||
}
|
||||
args.AddPercentageUpgrade("gas-thermo-component-upgrade-heat-capacity", thermoMachine.HeatCapacity / thermoMachine.BaseHeatCapacity);
|
||||
}
|
||||
|
||||
private void OnToggleMessage(EntityUid uid, GasThermoMachineComponent thermoMachine, GasThermomachineToggleMessage args)
|
||||
{
|
||||
_power.TogglePower(uid);
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ public sealed class RottingSystem : EntitySystem
|
|||
var ev = new IsRottingEvent();
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
|
||||
return ev.Handled;
|
||||
return !ev.Handled;
|
||||
}
|
||||
|
||||
public bool IsRotten(EntityUid uid, RottingComponent? rotting = null)
|
||||
|
|
@ -154,7 +154,7 @@ public sealed class RottingSystem : EntitySystem
|
|||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
args.Handled = component.CurrentTemperature > Atmospherics.T0C + 0.85f;
|
||||
args.Handled = component.CurrentTemperature < Atmospherics.T0C + 0.85f;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ public sealed class BodySystem : SharedBodySystem
|
|||
|
||||
SubscribeLocalEvent<BodyComponent, MoveInputEvent>(OnRelayMoveInput);
|
||||
SubscribeLocalEvent<BodyComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
|
||||
SubscribeLocalEvent<BodyComponent, BeingMicrowavedEvent>(OnBeingMicrowaved);
|
||||
}
|
||||
|
||||
private void OnRelayMoveInput(EntityUid uid, BodyComponent component, ref MoveInputEvent args)
|
||||
|
|
@ -65,19 +64,6 @@ public sealed class BodySystem : SharedBodySystem
|
|||
}
|
||||
}
|
||||
|
||||
private void OnBeingMicrowaved(EntityUid uid, BodyComponent component, BeingMicrowavedEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
// Don't microwave animals, kids
|
||||
SharedTransform.AttachToGridOrMap(uid);
|
||||
_appearance.SetData(args.Microwave, MicrowaveVisualState.Bloody, true);
|
||||
GibBody(uid, false, component);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
protected override void AddPart(
|
||||
EntityUid bodyUid,
|
||||
EntityUid partUid,
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ namespace Content.Server.Body.Systems
|
|||
// still remove reagents
|
||||
if (EntityManager.TryGetComponent<MobStateComponent>(solutionEntityUid.Value, out var state))
|
||||
{
|
||||
if (_mobStateSystem.IsDead(solutionEntityUid.Value, state))
|
||||
if (!proto.WorksOnTheDead && _mobStateSystem.IsDead(solutionEntityUid.Value, state))
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,10 +90,13 @@ public sealed partial class BotanySystem : EntitySystem
|
|||
if (!TryGetSeed(component, out var seed))
|
||||
return;
|
||||
|
||||
var name = Loc.GetString(seed.DisplayName);
|
||||
args.PushMarkup(Loc.GetString($"seed-component-description", ("seedName", name)));
|
||||
args.PushMarkup(Loc.GetString($"seed-component-plant-yield-text", ("seedYield", seed.Yield)));
|
||||
args.PushMarkup(Loc.GetString($"seed-component-plant-potency-text", ("seedPotency", seed.Potency)));
|
||||
using (args.PushGroup(nameof(SeedComponent)))
|
||||
{
|
||||
var name = Loc.GetString(seed.DisplayName);
|
||||
args.PushMarkup(Loc.GetString($"seed-component-description", ("seedName", name)));
|
||||
args.PushMarkup(Loc.GetString($"seed-component-plant-yield-text", ("seedYield", seed.Yield)));
|
||||
args.PushMarkup(Loc.GetString($"seed-component-plant-potency-text", ("seedPotency", seed.Potency)));
|
||||
}
|
||||
}
|
||||
|
||||
#region SeedPrototype prototype stuff
|
||||
|
|
|
|||
|
|
@ -77,59 +77,62 @@ public sealed class PlantHolderSystem : EntitySystem
|
|||
|
||||
var (_, component) = entity;
|
||||
|
||||
if (component.Seed == null)
|
||||
using (args.PushGroup(nameof(PlantHolderComponent)))
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-nothing-planted-message"));
|
||||
}
|
||||
else if (!component.Dead)
|
||||
{
|
||||
var displayName = Loc.GetString(component.Seed.DisplayName);
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-something-already-growing-message",
|
||||
if (component.Seed == null)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-nothing-planted-message"));
|
||||
}
|
||||
else if (!component.Dead)
|
||||
{
|
||||
var displayName = Loc.GetString(component.Seed.DisplayName);
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-something-already-growing-message",
|
||||
("seedName", displayName),
|
||||
("toBeForm", displayName.EndsWith('s') ? "are" : "is")));
|
||||
|
||||
if (component.Health <= component.Seed.Endurance / 2)
|
||||
if (component.Health <= component.Seed.Endurance / 2)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString(
|
||||
"plant-holder-component-something-already-growing-low-health-message",
|
||||
("healthState",
|
||||
Loc.GetString(component.Age > component.Seed.Lifespan
|
||||
? "plant-holder-component-plant-old-adjective"
|
||||
: "plant-holder-component-plant-unhealthy-adjective"))));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
args.PushMarkup(Loc.GetString(
|
||||
"plant-holder-component-something-already-growing-low-health-message",
|
||||
("healthState",
|
||||
Loc.GetString(component.Age > component.Seed.Lifespan
|
||||
? "plant-holder-component-plant-old-adjective"
|
||||
: "plant-holder-component-plant-unhealthy-adjective"))));
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-dead-plant-matter-message"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-dead-plant-matter-message"));
|
||||
}
|
||||
|
||||
if (component.WeedLevel >= 5)
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-weed-high-level-message"));
|
||||
if (component.WeedLevel >= 5)
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-weed-high-level-message"));
|
||||
|
||||
if (component.PestLevel >= 5)
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-pest-high-level-message"));
|
||||
if (component.PestLevel >= 5)
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-pest-high-level-message"));
|
||||
|
||||
args.PushMarkup(Loc.GetString($"plant-holder-component-water-level-message",
|
||||
("waterLevel", (int) component.WaterLevel)));
|
||||
args.PushMarkup(Loc.GetString($"plant-holder-component-nutrient-level-message",
|
||||
("nutritionLevel", (int) component.NutritionLevel)));
|
||||
args.PushMarkup(Loc.GetString($"plant-holder-component-water-level-message",
|
||||
("waterLevel", (int) component.WaterLevel)));
|
||||
args.PushMarkup(Loc.GetString($"plant-holder-component-nutrient-level-message",
|
||||
("nutritionLevel", (int) component.NutritionLevel)));
|
||||
|
||||
if (component.DrawWarnings)
|
||||
{
|
||||
if (component.Toxins > 40f)
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-toxins-high-warning"));
|
||||
if (component.DrawWarnings)
|
||||
{
|
||||
if (component.Toxins > 40f)
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-toxins-high-warning"));
|
||||
|
||||
if (component.ImproperLight)
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-light-improper-warning"));
|
||||
if (component.ImproperLight)
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-light-improper-warning"));
|
||||
|
||||
if (component.ImproperHeat)
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-heat-improper-warning"));
|
||||
if (component.ImproperHeat)
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-heat-improper-warning"));
|
||||
|
||||
if (component.ImproperPressure)
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-pressure-improper-warning"));
|
||||
if (component.ImproperPressure)
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-pressure-improper-warning"));
|
||||
|
||||
if (component.MissingGas > 0)
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-gas-missing-warning"));
|
||||
if (component.MissingGas > 0)
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-gas-missing-warning"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
29
Content.Server/Buckle/Systems/AntiRotOnBuckleSystem.cs
Normal file
29
Content.Server/Buckle/Systems/AntiRotOnBuckleSystem.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using Content.Server.Power.Components;
|
||||
using Content.Shared.Atmos.Rotting;
|
||||
using Content.Shared.Buckle.Components;
|
||||
|
||||
namespace Content.Server.Buckle.Systems;
|
||||
|
||||
public sealed class AntiRotOnBuckleSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<BuckleComponent, IsRottingEvent>(OnIsRotting);
|
||||
SubscribeLocalEvent<AntiRotOnBuckleComponent, PowerChangedEvent>(OnPowerChanged);
|
||||
}
|
||||
|
||||
private void OnIsRotting(EntityUid uid, BuckleComponent buckle, ref IsRottingEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
args.Handled = buckle is { Buckled: true, BuckledTo: not null } &&
|
||||
TryComp<AntiRotOnBuckleComponent>(buckle.BuckledTo.Value, out var antiRot) &&
|
||||
antiRot.Enabled;
|
||||
}
|
||||
|
||||
private void OnPowerChanged(EntityUid uid, AntiRotOnBuckleComponent component, ref PowerChangedEvent args)
|
||||
{
|
||||
component.Enabled = !component.RequiresPower || args.Powered;
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ public sealed class PriceGunSystem : EntitySystem
|
|||
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||
[Dependency] private readonly PricingSystem _pricingSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly CargoSystem _bountySystem = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
|
|
@ -55,9 +56,17 @@ public sealed class PriceGunSystem : EntitySystem
|
|||
if (!TryComp(uid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((uid, useDelay)))
|
||||
return;
|
||||
|
||||
var price = _pricingSystem.GetPrice(args.Target.Value);
|
||||
// Check if we're scanning a bounty crate
|
||||
if (_bountySystem.IsBountyComplete(args.Target.Value, (EntityUid?) null, out _))
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("price-gun-bounty-complete"), args.User, args.User);
|
||||
}
|
||||
else // Otherwise appraise the price
|
||||
{
|
||||
double price = _pricingSystem.GetPrice(args.Target.Value);
|
||||
_popupSystem.PopupEntity(Loc.GetString("price-gun-pricing-result", ("object", Identity.Entity(args.Target.Value, EntityManager)), ("price", $"{price:F2}")), args.User, args.User);
|
||||
}
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("price-gun-pricing-result", ("object", Identity.Entity(args.Target.Value, EntityManager)), ("price", $"{price:F2}")), args.User, args.User);
|
||||
_useDelay.TryResetDelay((uid, useDelay));
|
||||
args.Handled = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace Content.Server.Communications
|
|||
/// Remaining cooldown between making announcements.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public float AnnouncementCooldownRemaining;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -37,6 +38,13 @@ namespace Content.Server.Communications
|
|||
[DataField]
|
||||
public int Delay = 90;
|
||||
|
||||
/// <summary>
|
||||
/// Time in seconds of announcement cooldown when a new console is created on a per-console basis
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public int InitialDelay = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Can call or recall the shuttle
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@ namespace Content.Server.Communications
|
|||
SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleAnnounceMessage>(OnAnnounceMessage);
|
||||
SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleCallEmergencyShuttleMessage>(OnCallShuttleMessage);
|
||||
SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleRecallEmergencyShuttleMessage>(OnRecallShuttleMessage);
|
||||
|
||||
// On console init, set cooldown
|
||||
SubscribeLocalEvent<CommunicationsConsoleComponent, MapInitEvent>(OnCommunicationsConsoleMapInit);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
|
|
@ -79,6 +82,11 @@ namespace Content.Server.Communications
|
|||
base.Update(frameTime);
|
||||
}
|
||||
|
||||
public void OnCommunicationsConsoleMapInit(EntityUid uid, CommunicationsConsoleComponent comp, MapInitEvent args)
|
||||
{
|
||||
comp.AnnouncementCooldownRemaining = comp.InitialDelay;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the UI of every comms console.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ namespace Content.Server.Construction.Conditions
|
|||
if (entityManager.EntitySysManager.GetEntitySystem<MachineFrameSystem>().IsComplete(machineFrame))
|
||||
return false;
|
||||
|
||||
args.Message.AddMarkup(Loc.GetString("construction-condition-machine-frame-requirement-label") + "\n");
|
||||
args.PushMarkup(Loc.GetString("construction-condition-machine-frame-requirement-label"));
|
||||
foreach (var (part, required) in machineFrame.Requirements)
|
||||
{
|
||||
var amount = required - machineFrame.Progress[part];
|
||||
|
|
@ -54,10 +54,9 @@ namespace Content.Server.Construction.Conditions
|
|||
if(amount == 0)
|
||||
continue;
|
||||
|
||||
args.Message.AddMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry",
|
||||
args.PushMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry",
|
||||
("amount", amount),
|
||||
("elementName", Loc.GetString(part)))
|
||||
+ "\n");
|
||||
("elementName", Loc.GetString(part))));
|
||||
}
|
||||
|
||||
foreach (var (material, required) in machineFrame.MaterialRequirements)
|
||||
|
|
@ -67,10 +66,9 @@ namespace Content.Server.Construction.Conditions
|
|||
if(amount == 0)
|
||||
continue;
|
||||
|
||||
args.Message.AddMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry",
|
||||
args.PushMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry",
|
||||
("amount", amount),
|
||||
("elementName", Loc.GetString(material)))
|
||||
+ "\n");
|
||||
("elementName", Loc.GetString(material))));
|
||||
}
|
||||
|
||||
foreach (var (compName, info) in machineFrame.ComponentRequirements)
|
||||
|
|
@ -80,10 +78,9 @@ namespace Content.Server.Construction.Conditions
|
|||
if(amount == 0)
|
||||
continue;
|
||||
|
||||
args.Message.AddMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry",
|
||||
args.PushMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry",
|
||||
("amount", info.Amount),
|
||||
("elementName", Loc.GetString(info.ExamineName)))
|
||||
+ "\n");
|
||||
("elementName", Loc.GetString(info.ExamineName))));
|
||||
}
|
||||
|
||||
foreach (var (tagName, info) in machineFrame.TagRequirements)
|
||||
|
|
@ -93,10 +90,10 @@ namespace Content.Server.Construction.Conditions
|
|||
if(amount == 0)
|
||||
continue;
|
||||
|
||||
args.Message.AddMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry",
|
||||
("amount", info.Amount),
|
||||
("elementName", Loc.GetString(info.ExamineName)))
|
||||
+ "\n");
|
||||
args.PushMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry",
|
||||
("amount", info.Amount),
|
||||
("elementName", Loc.GetString(info.ExamineName)))
|
||||
+ "\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -67,46 +67,50 @@ namespace Content.Server.Construction
|
|||
|
||||
private void HandleConstructionExamined(EntityUid uid, ConstructionComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (GetTargetNode(uid, component) is {} target)
|
||||
using (args.PushGroup(nameof(ConstructionComponent)))
|
||||
{
|
||||
if (target.Name == component.DeconstructionNode)
|
||||
if (GetTargetNode(uid, component) is {} target)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("deconstruction-header-text") + "\n");
|
||||
if (target.Name == component.DeconstructionNode)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("deconstruction-header-text") + "\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
args.PushMarkup(Loc.GetString(
|
||||
"construction-component-to-create-header",
|
||||
("targetName", target.Name)) + "\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (component.EdgeIndex == null && GetTargetEdge(uid, component) is {} targetEdge)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString(
|
||||
"construction-component-to-create-header",
|
||||
("targetName", target.Name)) + "\n");
|
||||
var preventStepExamine = false;
|
||||
|
||||
foreach (var condition in targetEdge.Conditions)
|
||||
{
|
||||
preventStepExamine |= condition.DoExamine(args);
|
||||
}
|
||||
|
||||
if (!preventStepExamine)
|
||||
targetEdge.Steps[0].DoExamine(args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetCurrentEdge(uid, component) is {} edge)
|
||||
{
|
||||
var preventStepExamine = false;
|
||||
|
||||
foreach (var condition in edge.Conditions)
|
||||
{
|
||||
preventStepExamine |= condition.DoExamine(args);
|
||||
}
|
||||
|
||||
if (!preventStepExamine && component.StepIndex < edge.Steps.Count)
|
||||
edge.Steps[component.StepIndex].DoExamine(args);
|
||||
}
|
||||
}
|
||||
|
||||
if (component.EdgeIndex == null && GetTargetEdge(uid, component) is {} targetEdge)
|
||||
{
|
||||
var preventStepExamine = false;
|
||||
|
||||
foreach (var condition in targetEdge.Conditions)
|
||||
{
|
||||
preventStepExamine |= condition.DoExamine(args);
|
||||
}
|
||||
|
||||
if (!preventStepExamine)
|
||||
targetEdge.Steps[0].DoExamine(args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetCurrentEdge(uid, component) is {} edge)
|
||||
{
|
||||
var preventStepExamine = false;
|
||||
|
||||
foreach (var condition in edge.Conditions)
|
||||
{
|
||||
preventStepExamine |= condition.DoExamine(args);
|
||||
}
|
||||
|
||||
if (!preventStepExamine && component.StepIndex < edge.Steps.Count)
|
||||
edge.Steps[component.StepIndex].DoExamine(args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ using Content.Shared.Construction;
|
|||
using Content.Shared.Construction.Components;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Robust.Shared.Timing;
|
||||
using YamlDotNet.Serialization.NodeTypeResolvers;
|
||||
|
||||
namespace Content.Server.Construction;
|
||||
|
||||
|
|
@ -37,7 +36,7 @@ public sealed class FlatpackSystem : SharedFlatpackSystem
|
|||
Dictionary<string, int>? cost = null;
|
||||
if (TryComp<MachineBoardComponent>(machineBoard, out var machineBoardComponent))
|
||||
cost = GetFlatpackCreationCost(ent, (machineBoard, machineBoardComponent));
|
||||
if (TryComp<ComputerBoardComponent>(machineBoard, out var computerBoardComponent))
|
||||
if (HasComp<ComputerBoardComponent>(machineBoard))
|
||||
cost = GetFlatpackCreationCost(ent);
|
||||
|
||||
if (cost is null)
|
||||
|
|
@ -78,7 +77,7 @@ public sealed class FlatpackSystem : SharedFlatpackSystem
|
|||
Dictionary<string, int>? cost = null;
|
||||
if (TryComp<MachineBoardComponent>(machineBoard, out var machineBoardComponent))
|
||||
cost = GetFlatpackCreationCost(ent, (machineBoard, machineBoardComponent));
|
||||
if (TryComp<ComputerBoardComponent>(machineBoard, out var computerBoardComponent))
|
||||
if (HasComp<ComputerBoardComponent>(machineBoard))
|
||||
cost = GetFlatpackCreationCost(ent);
|
||||
|
||||
if (cost is null)
|
||||
|
|
@ -89,6 +88,7 @@ public sealed class FlatpackSystem : SharedFlatpackSystem
|
|||
|
||||
var flatpack = Spawn(comp.BaseFlatpackPrototype, Transform(ent).Coordinates);
|
||||
SetupFlatpack(flatpack, machineBoard);
|
||||
Del(machineBoard);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Robust.Shared.Prototypes;
|
|||
using Content.Shared.Construction;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Content.Shared.Construction.Steps;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.GuideGenerator;
|
||||
|
||||
|
|
@ -65,7 +66,7 @@ public sealed class HeatableRecipeEntry
|
|||
{
|
||||
var nodeStep = (TemperatureConstructionGraphStep)nodeStepRaw;
|
||||
graphID = nodeEdge.Target; // required to check when we need to leave second loop; this is the best solution, because nodeEdge.Target is marked as required datafield and cannot be null
|
||||
EntityManager em = new();
|
||||
ServerEntityManager em = new();
|
||||
MinTemp = nodeStep.MinTemperature.HasValue ? nodeStep.MinTemperature.Value : 0;
|
||||
Result = nodeStep.MinTemperature.HasValue ? constructionProto.Nodes[nodeEdge.Target].Entity.GetId(null, null, new GraphNodeEntityArgs(em)) : null;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Robust.Shared.Prototypes;
|
|||
using Content.Shared.Construction;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Content.Shared.Construction.Steps;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.GuideGenerator;
|
||||
|
||||
|
|
@ -68,7 +69,7 @@ public sealed class ToolRecipeEntry // because of https://github.com/space-wizar
|
|||
{
|
||||
var nodeStep = (ToolConstructionGraphStep)nodeStepRaw;
|
||||
graphID = nodeEdge.Target; // required to check when we need to leave second loop; this is the best solution, because nodeEdge.Target is marked as required datafield and cannot be null
|
||||
EntityManager em = new();
|
||||
ServerEntityManager em = new();
|
||||
Tool = nodeStep.Tool;
|
||||
Result = constructionProto.Nodes[nodeEdge.Target].Entity.GetId(null, null, new GraphNodeEntityArgs(em));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ using Content.Shared.Effects;
|
|||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Throwing;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Damage.Systems
|
||||
|
|
@ -39,7 +38,11 @@ namespace Content.Server.Damage.Systems
|
|||
if (dmg != null && HasComp<MobStateComponent>(args.Target))
|
||||
_adminLogger.Add(LogType.ThrowHit, $"{ToPrettyString(args.Target):target} received {dmg.Total:damage} damage from collision");
|
||||
|
||||
_color.RaiseEffect(Color.Red, new List<EntityUid>() { args.Target }, Filter.Pvs(args.Target, entityManager: EntityManager));
|
||||
if (dmg is { Empty: false })
|
||||
{
|
||||
_color.RaiseEffect(Color.Red, new List<EntityUid>() { args.Target }, Filter.Pvs(args.Target, entityManager: EntityManager));
|
||||
}
|
||||
|
||||
_guns.PlayImpactSound(args.Target, dmg, null, false);
|
||||
if (TryComp<PhysicsComponent>(uid, out var body) && body.LinearVelocity.LengthSquared() > 0f)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -66,26 +66,29 @@ public sealed class DefusableSystem : SharedDefusableSystem
|
|||
if (!args.IsInDetailsRange)
|
||||
return;
|
||||
|
||||
if (!comp.Usable)
|
||||
using (args.PushGroup(nameof(DefusableComponent)))
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("defusable-examine-defused", ("name", uid)));
|
||||
}
|
||||
else if (comp.Activated && TryComp<ActiveTimerTriggerComponent>(uid, out var activeComp))
|
||||
{
|
||||
if (comp.DisplayTime)
|
||||
if (!comp.Usable)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("defusable-examine-live", ("name", uid),
|
||||
("time", MathF.Floor(activeComp.TimeRemaining))));
|
||||
args.PushMarkup(Loc.GetString("defusable-examine-defused", ("name", uid)));
|
||||
}
|
||||
else if (comp.Activated && TryComp<ActiveTimerTriggerComponent>(uid, out var activeComp))
|
||||
{
|
||||
if (comp.DisplayTime)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("defusable-examine-live", ("name", uid),
|
||||
("time", MathF.Floor(activeComp.TimeRemaining))));
|
||||
}
|
||||
else
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("defusable-examine-live-display-off", ("name", uid)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("defusable-examine-live-display-off", ("name", uid)));
|
||||
args.PushMarkup(Loc.GetString("defusable-examine-inactive", ("name", uid)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("defusable-examine-inactive", ("name", uid)));
|
||||
}
|
||||
|
||||
args.PushMarkup(Loc.GetString("defusable-examine-bolts", ("down", comp.Bolted)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Content.Shared.DeviceLinking;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.DeviceLinking.Components;
|
||||
|
|
@ -7,36 +8,40 @@ namespace Content.Server.DeviceLinking.Components;
|
|||
[RegisterComponent]
|
||||
public sealed partial class SignalTimerComponent : Component
|
||||
{
|
||||
[DataField("delay"), ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public double Delay = 5;
|
||||
|
||||
/// <summary>
|
||||
/// This shows the Label: text box in the UI.
|
||||
/// </summary>
|
||||
[DataField("canEditLabel"), ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool CanEditLabel = true;
|
||||
|
||||
/// <summary>
|
||||
/// The label, used for TextScreen visuals currently.
|
||||
/// </summary>
|
||||
[DataField("label"), ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public string Label = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The port that gets signaled when the timer triggers.
|
||||
/// </summary>
|
||||
[DataField("triggerPort", customTypeSerializer: typeof(PrototypeIdSerializer<SourcePortPrototype>)), ViewVariables(VVAccess.ReadWrite)]
|
||||
public string TriggerPort = "Timer";
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public ProtoId<SourcePortPrototype> TriggerPort = "Timer";
|
||||
|
||||
/// <summary>
|
||||
/// The port that gets signaled when the timer starts.
|
||||
/// </summary>
|
||||
[DataField("startPort", customTypeSerializer: typeof(PrototypeIdSerializer<SourcePortPrototype>)), ViewVariables(VVAccess.ReadWrite)]
|
||||
public string StartPort = "Start";
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public ProtoId<SourcePortPrototype> StartPort = "Start";
|
||||
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public ProtoId<SinkPortPrototype> Trigger = "Trigger";
|
||||
|
||||
/// <summary>
|
||||
/// If not null, this timer will play this sound when done.
|
||||
/// </summary>
|
||||
[DataField("doneSound"), ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public SoundSpecifier? DoneSound;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Server.DeviceLinking.Components;
|
||||
using Content.Server.DeviceLinking.Events;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.MachineLinking;
|
||||
|
|
@ -19,6 +20,11 @@ public sealed class SignalTimerSystem : EntitySystem
|
|||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Per-tick timer cache.
|
||||
/// </summary>
|
||||
private List<Entity<SignalTimerComponent>> _timers = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
|
@ -29,11 +35,13 @@ public sealed class SignalTimerSystem : EntitySystem
|
|||
SubscribeLocalEvent<SignalTimerComponent, SignalTimerTextChangedMessage>(OnTextChangedMessage);
|
||||
SubscribeLocalEvent<SignalTimerComponent, SignalTimerDelayChangedMessage>(OnDelayChangedMessage);
|
||||
SubscribeLocalEvent<SignalTimerComponent, SignalTimerStartMessage>(OnTimerStartMessage);
|
||||
SubscribeLocalEvent<SignalTimerComponent, SignalReceivedEvent>(OnSignalReceived);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, SignalTimerComponent component, ComponentInit args)
|
||||
{
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, component.Label);
|
||||
_signalSystem.EnsureSinkPorts(uid, component.Trigger);
|
||||
}
|
||||
|
||||
private void OnAfterActivatableUIOpen(EntityUid uid, SignalTimerComponent component, AfterActivatableUIOpenEvent args)
|
||||
|
|
@ -58,11 +66,13 @@ public sealed class SignalTimerSystem : EntitySystem
|
|||
public void Trigger(EntityUid uid, SignalTimerComponent signalTimer)
|
||||
{
|
||||
RemComp<ActiveSignalTimerComponent>(uid);
|
||||
|
||||
if (TryComp<AppearanceComponent>(uid, out var appearance))
|
||||
{
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, new string?[] { signalTimer.Label }, appearance);
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, new[] { signalTimer.Label }, appearance);
|
||||
}
|
||||
|
||||
_audio.PlayPvs(signalTimer.DoneSound, uid);
|
||||
_signalSystem.InvokePort(uid, signalTimer.TriggerPort);
|
||||
|
||||
if (_ui.TryGetUi(uid, SignalTimerUiKey.Key, out var bui))
|
||||
|
|
@ -80,16 +90,29 @@ public sealed class SignalTimerSystem : EntitySystem
|
|||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
UpdateTimer();
|
||||
}
|
||||
|
||||
private void UpdateTimer()
|
||||
{
|
||||
_timers.Clear();
|
||||
|
||||
var query = EntityQueryEnumerator<ActiveSignalTimerComponent, SignalTimerComponent>();
|
||||
while (query.MoveNext(out var uid, out var active, out var timer))
|
||||
{
|
||||
if (active.TriggerTime > _gameTiming.CurTime)
|
||||
continue;
|
||||
|
||||
Trigger(uid, timer);
|
||||
_timers.Add((uid, timer));
|
||||
}
|
||||
|
||||
if (timer.DoneSound != null)
|
||||
_audio.PlayPvs(timer.DoneSound, uid);
|
||||
foreach (var timer in _timers)
|
||||
{
|
||||
// Exploded or the likes.
|
||||
if (!Exists(timer.Owner))
|
||||
continue;
|
||||
|
||||
Trigger(timer.Owner, timer.Comp);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,7 +167,19 @@ public sealed class SignalTimerSystem : EntitySystem
|
|||
{
|
||||
if (!IsMessageValid(uid, args))
|
||||
return;
|
||||
OnStartTimer(uid, component);
|
||||
}
|
||||
|
||||
private void OnSignalReceived(EntityUid uid, SignalTimerComponent component, ref SignalReceivedEvent args)
|
||||
{
|
||||
if (args.Port == component.Trigger)
|
||||
{
|
||||
OnStartTimer(uid, component);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnStartTimer(EntityUid uid, SignalTimerComponent component)
|
||||
{
|
||||
TryComp<AppearanceComponent>(uid, out var appearance);
|
||||
var timer = EnsureComp<ActiveSignalTimerComponent>(uid);
|
||||
timer.TriggerTime = _gameTiming.CurTime + TimeSpan.FromSeconds(component.Delay);
|
||||
|
|
|
|||
|
|
@ -65,10 +65,7 @@ namespace Content.Server.Entry
|
|||
factory.DoAutoRegistrations();
|
||||
factory.IgnoreMissingComponents("Visuals");
|
||||
|
||||
foreach (var ignoreName in IgnoredComponents.List)
|
||||
{
|
||||
factory.RegisterIgnore(ignoreName);
|
||||
}
|
||||
factory.RegisterIgnore(IgnoredComponents.List);
|
||||
|
||||
prototypes.RegisterIgnore("parallax");
|
||||
prototypes.RegisterIgnore("guideEntry");
|
||||
|
|
|
|||
|
|
@ -14,12 +14,10 @@ using Content.Shared.Database;
|
|||
using Content.Shared.Explosion;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Projectiles;
|
||||
using Content.Shared.Throwing;
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
|
|
@ -252,7 +250,9 @@ public sealed partial class ExplosionSystem : EntitySystem
|
|||
var pos = Transform(uid);
|
||||
|
||||
|
||||
QueueExplosion(pos.MapPosition, typeId, totalIntensity, slope, maxTileIntensity, tileBreakScale, maxTileBreak, canCreateVacuum, addLog: false);
|
||||
var coordinates = _transformSystem.GetMapCoordinates(pos);
|
||||
|
||||
QueueExplosion(coordinates, typeId, totalIntensity, slope, maxTileIntensity, tileBreakScale, maxTileBreak, canCreateVacuum, addLog: false);
|
||||
|
||||
if (!addLog)
|
||||
return;
|
||||
|
|
@ -260,15 +260,15 @@ public sealed partial class ExplosionSystem : EntitySystem
|
|||
if (user == null)
|
||||
{
|
||||
_adminLogger.Add(LogType.Explosion, LogImpact.High,
|
||||
$"{ToPrettyString(uid):entity} exploded ({typeId}) at {pos.Coordinates:coordinates} with intensity {totalIntensity} slope {slope}");
|
||||
$"{ToPrettyString(uid):entity} exploded ({typeId}) at {coordinates:coordinates} with intensity {totalIntensity} slope {slope}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_adminLogger.Add(LogType.Explosion, LogImpact.High,
|
||||
$"{ToPrettyString(user.Value):user} caused {ToPrettyString(uid):entity} to explode ({typeId}) at {pos.Coordinates:coordinates} with intensity {totalIntensity} slope {slope}");
|
||||
$"{ToPrettyString(user.Value):user} caused {ToPrettyString(uid):entity} to explode ({typeId}) at {coordinates:coordinates} with intensity {totalIntensity} slope {slope}");
|
||||
var alertMinExplosionIntensity = _cfg.GetCVar(CCVars.AdminAlertExplosionMinIntensity);
|
||||
if (alertMinExplosionIntensity > -1 && totalIntensity >= alertMinExplosionIntensity)
|
||||
_chat.SendAdminAlert(user.Value, $"caused {ToPrettyString(uid)} to explode ({typeId}:{totalIntensity}) at {pos.Coordinates:coordinates}");
|
||||
_chat.SendAdminAlert(user.Value, $"caused {ToPrettyString(uid)} to explode ({typeId}:{totalIntensity}) at {coordinates:coordinates}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ public sealed class DrainSystem : SharedDrainSystem
|
|||
var text = drainSolution.AvailableVolume != 0
|
||||
? Loc.GetString("drain-component-examine-volume", ("volume", drainSolution.AvailableVolume))
|
||||
: Loc.GetString("drain-component-examine-hint-full");
|
||||
args.Message.AddMarkup($"\n\n{text}");
|
||||
args.PushMarkup(text);
|
||||
}
|
||||
|
||||
private void OnInteract(Entity<DrainComponent> entity, ref AfterInteractUsingEvent args)
|
||||
|
|
|
|||
|
|
@ -43,10 +43,13 @@ public sealed partial class PuddleSystem
|
|||
|
||||
private void OnExamined(Entity<SpillableComponent> entity, ref ExaminedEvent args)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("spill-examine-is-spillable"));
|
||||
using (args.PushGroup(nameof(SpillableComponent)))
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("spill-examine-is-spillable"));
|
||||
|
||||
if (HasComp<MeleeWeaponComponent>(entity))
|
||||
args.PushMarkup(Loc.GetString("spill-examine-spillable-weapon"));
|
||||
if (HasComp<MeleeWeaponComponent>(entity))
|
||||
args.PushMarkup(Loc.GetString("spill-examine-spillable-weapon"));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnOverflow(Entity<SpillableComponent> entity, ref SolutionContainerOverflowEvent args)
|
||||
|
|
|
|||
|
|
@ -361,23 +361,27 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
|
|||
|
||||
private void HandlePuddleExamined(Entity<PuddleComponent> entity, ref ExaminedEvent args)
|
||||
{
|
||||
if (TryComp<StepTriggerComponent>(entity, out var slippery) && slippery.Active)
|
||||
using (args.PushGroup(nameof(PuddleComponent)))
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("puddle-component-examine-is-slipper-text"));
|
||||
}
|
||||
if (TryComp<StepTriggerComponent>(entity, out var slippery) && slippery.Active)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("puddle-component-examine-is-slipper-text"));
|
||||
}
|
||||
|
||||
if (HasComp<EvaporationComponent>(entity) &&
|
||||
_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution))
|
||||
{
|
||||
if (CanFullyEvaporate(solution))
|
||||
args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating"));
|
||||
else if (solution.GetTotalPrototypeQuantity(EvaporationReagents) > FixedPoint2.Zero)
|
||||
args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-partial"));
|
||||
if (HasComp<EvaporationComponent>(entity) &&
|
||||
_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName,
|
||||
ref entity.Comp.Solution, out var solution))
|
||||
{
|
||||
if (CanFullyEvaporate(solution))
|
||||
args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating"));
|
||||
else if (solution.GetTotalPrototypeQuantity(EvaporationReagents) > FixedPoint2.Zero)
|
||||
args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-partial"));
|
||||
else
|
||||
args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-no"));
|
||||
}
|
||||
else
|
||||
args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-no"));
|
||||
}
|
||||
else
|
||||
args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-no"));
|
||||
}
|
||||
|
||||
private void OnAnchorChanged(Entity<PuddleComponent> entity, ref AnchorStateChangedEvent args)
|
||||
|
|
|
|||
|
|
@ -18,15 +18,6 @@ namespace Content.Server.GameTicking
|
|||
[ViewVariables]
|
||||
public bool DisallowLateJoin { get; private set; } = false;
|
||||
|
||||
[ViewVariables]
|
||||
public bool StationOffset { get; private set; } = false;
|
||||
|
||||
[ViewVariables]
|
||||
public bool StationRotation { get; private set; } = false;
|
||||
|
||||
[ViewVariables]
|
||||
public float MaxStationOffset { get; private set; } = 0f;
|
||||
|
||||
[ViewVariables]
|
||||
public string? ServerName { get; private set; }
|
||||
|
||||
|
|
@ -57,9 +48,6 @@ namespace Content.Server.GameTicking
|
|||
_configurationManager.OnValueChanged(CCVars.GameLobbyDuration, value => LobbyDuration = TimeSpan.FromSeconds(value), true);
|
||||
_configurationManager.OnValueChanged(CCVars.GameDisallowLateJoins,
|
||||
value => { DisallowLateJoin = value; UpdateLateJoinStatus(); }, true);
|
||||
_configurationManager.OnValueChanged(CCVars.StationOffset, value => StationOffset = value, true);
|
||||
_configurationManager.OnValueChanged(CCVars.StationRotation, value => StationRotation = value, true);
|
||||
_configurationManager.OnValueChanged(CCVars.MaxStationOffset, value => MaxStationOffset = value, true);
|
||||
_configurationManager.OnValueChanged(CCVars.AdminLogsServerName, value =>
|
||||
{
|
||||
// TODO why tf is the server name on admin logs
|
||||
|
|
|
|||
|
|
@ -86,10 +86,15 @@ namespace Content.Server.GameTicking
|
|||
("desc", desc));
|
||||
}
|
||||
|
||||
private TickerConnectionStatusEvent GetConnectionStatusMsg()
|
||||
{
|
||||
return new TickerConnectionStatusEvent(RoundStartTimeSpan);
|
||||
}
|
||||
|
||||
private TickerLobbyStatusEvent GetStatusMsg(ICommonSession session)
|
||||
{
|
||||
_playerGameStatuses.TryGetValue(session.UserId, out var status);
|
||||
return new TickerLobbyStatusEvent(RunLevel != GameRunLevel.PreRoundLobby, LobbySong, LobbyBackground,status == PlayerGameStatus.ReadyToPlay, _roundStartTime, RoundPreloadTime, RoundStartTimeSpan, Paused);
|
||||
return new TickerLobbyStatusEvent(RunLevel != GameRunLevel.PreRoundLobby, LobbySong, LobbyBackground, status == PlayerGameStatus.ReadyToPlay, _roundStartTime, RoundPreloadTime, RoundStartTimeSpan, Paused);
|
||||
}
|
||||
|
||||
private void SendStatusToAll()
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ namespace Content.Server.GameTicking
|
|||
? Loc.GetString("player-first-join-message", ("name", args.Session.Name))
|
||||
: Loc.GetString("player-join-message", ("name", args.Session.Name)));
|
||||
|
||||
RaiseNetworkEvent(GetConnectionStatusMsg(), session.Channel);
|
||||
|
||||
if (LobbyEnabled && _roundStartCountdownHasNotStartedYetDueToNoPlayers)
|
||||
{
|
||||
_roundStartCountdownHasNotStartedYetDueToNoPlayers = false;
|
||||
|
|
|
|||
|
|
@ -388,8 +388,7 @@ namespace Content.Server.GameTicking
|
|||
var listOfPlayerInfoFinal = listOfPlayerInfo.OrderBy(pi => pi.PlayerOOCName).ToArray();
|
||||
|
||||
RaiseNetworkEvent(new RoundEndMessageEvent(gamemodeTitle, roundEndText, roundDuration, RoundId,
|
||||
listOfPlayerInfoFinal.Length, listOfPlayerInfoFinal, LobbySong,
|
||||
new SoundCollectionSpecifier("RoundEnd").GetSound()));
|
||||
listOfPlayerInfoFinal.Length, listOfPlayerInfoFinal, LobbySong));
|
||||
RaiseLocalEvent(new RoundEndedEvent(RoundId, roundDuration)); // Corvax
|
||||
}
|
||||
|
||||
|
|
@ -525,8 +524,11 @@ namespace Content.Server.GameTicking
|
|||
_playerGameStatuses.Clear();
|
||||
foreach (var session in _playerManager.Sessions)
|
||||
{
|
||||
_playerGameStatuses[session.UserId] = LobbyEnabled ? PlayerGameStatus.NotReadyToPlay : PlayerGameStatus.ReadyToPlay;
|
||||
_playerGameStatuses[session.UserId] = LobbyEnabled ? PlayerGameStatus.NotReadyToPlay : PlayerGameStatus.ReadyToPlay;
|
||||
}
|
||||
|
||||
// Put a bangin' donk on it.
|
||||
_audio.PlayGlobal(_audio.GetSound(new SoundCollectionSpecifier("RoundEnd")), Filter.Broadcast(), true);
|
||||
}
|
||||
|
||||
public bool DelayStart(TimeSpan time)
|
||||
|
|
@ -540,7 +542,7 @@ namespace Content.Server.GameTicking
|
|||
|
||||
RaiseNetworkEvent(new TickerLobbyCountdownEvent(_roundStartTime, Paused));
|
||||
|
||||
_chatManager.DispatchServerAnnouncement(Loc.GetString("game-ticker-delay-start", ("seconds",time.TotalSeconds)));
|
||||
_chatManager.DispatchServerAnnouncement(Loc.GetString("game-ticker-delay-start", ("seconds", time.TotalSeconds)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ namespace Content.Server.GameTicking
|
|||
|
||||
private void GetStatusResponse(JsonNode jObject)
|
||||
{
|
||||
var preset = CurrentPreset ?? Preset;
|
||||
|
||||
// This method is raised from another thread, so this better be thread safe!
|
||||
lock (_statusShellLock)
|
||||
{
|
||||
|
|
@ -52,6 +54,8 @@ namespace Content.Server.GameTicking
|
|||
jObject["soft_max_players"] = _cfg.GetCVar(CCVars.SoftMaxPlayers);
|
||||
jObject["panic_bunker"] = _cfg.GetCVar(CCVars.PanicBunkerEnabled);
|
||||
jObject["run_level"] = (int) _runLevel;
|
||||
if (preset != null)
|
||||
jObject["preset"] = Loc.GetString(preset.ModeTitle);
|
||||
if (_runLevel >= GameRunLevel.InRound)
|
||||
{
|
||||
jObject["round_start_time"] = _roundStartDateTime.ToString("o");
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server.Administration.Commands;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Communications;
|
||||
|
|
@ -28,6 +29,7 @@ using Content.Server.Station.Systems;
|
|||
using Content.Server.Store.Components;
|
||||
using Content.Server.Store.Systems;
|
||||
using Content.Shared.CombatMode.Pacification;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Dataset;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
|
|
@ -53,6 +55,7 @@ using Robust.Shared.Prototypes;
|
|||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules;
|
||||
|
||||
|
|
@ -81,6 +84,8 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
|||
[Dependency] private readonly StationSpawningSystem _stationSpawning = default!;
|
||||
[Dependency] private readonly StoreSystem _store = default!;
|
||||
[Dependency] private readonly TagSystem _tag = default!;
|
||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly WarDeclaratorSystem _warDeclarator = default!;
|
||||
|
||||
|
||||
|
|
@ -715,6 +720,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
|||
{
|
||||
nukeOp = _random.PickAndTake(medPrefList);
|
||||
everyone.Remove(nukeOp);
|
||||
prefList.Remove(nukeOp);
|
||||
Logger.InfoS("preset", "Insufficient preferred nukeop commanders, picking an agent");
|
||||
}
|
||||
|
||||
|
|
@ -963,6 +969,10 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
|||
_mind.SetUserId(newMind, session.UserId);
|
||||
_roles.MindAddRole(newMind, new NukeopsRoleComponent { PrototypeId = spawnDetails.Role });
|
||||
|
||||
// Automatically de-admin players who are being made nukeops
|
||||
if (_cfg.GetCVar(CCVars.AdminDeadminOnJoin) && _adminManager.IsAdmin(session))
|
||||
_adminManager.DeAdmin(session);
|
||||
|
||||
_mind.TransferTo(newMind, mob);
|
||||
}
|
||||
else if (addSpawnPoints)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ using Content.Server.GameTicking.Presets;
|
|||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Shared.Random;
|
||||
using Content.Shared.Random.Helpers;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules;
|
||||
|
||||
|
|
@ -11,6 +13,7 @@ public sealed class SecretRuleSystem : GameRuleSystem<SecretRuleComponent>
|
|||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
|
||||
protected override void Started(EntityUid uid, SecretRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
|
|
@ -32,7 +35,8 @@ public sealed class SecretRuleSystem : GameRuleSystem<SecretRuleComponent>
|
|||
{
|
||||
// TODO: This doesn't consider what can't start due to minimum player count,
|
||||
// but currently there's no way to know anyway as they use cvars.
|
||||
var preset = _prototypeManager.Index<WeightedRandomPrototype>("Secret").Pick(_random);
|
||||
var presetString = _configurationManager.GetCVar(CCVars.SecretWeightPrototype);
|
||||
var preset = _prototypeManager.Index<WeightedRandomPrototype>(presetString).Pick(_random);
|
||||
Logger.InfoS("gamepreset", $"Selected {preset} for secret.");
|
||||
|
||||
var rules = _prototypeManager.Index<GamePresetPrototype>(preset).Rules;
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ public sealed partial class GatewayGeneratorComponent : Component
|
|||
/// </summary>
|
||||
public List<ProtoId<BiomeMarkerLayerPrototype>> LootLayers = new()
|
||||
{
|
||||
"OreTin",
|
||||
"OreIron",
|
||||
"OreQuartz",
|
||||
"OreGold",
|
||||
"OreSilver",
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public sealed class ToggleableGhostRoleSystem : EntitySystem
|
|||
{
|
||||
if (!args.IsInDetailsRange)
|
||||
return;
|
||||
|
||||
|
||||
if (TryComp<MindContainerComponent>(uid, out var mind) && mind.HasMind)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString(component.ExamineTextMindPresent));
|
||||
|
|
|
|||
|
|
@ -44,8 +44,7 @@ public sealed class RandomGiftSystem : EntitySystem
|
|||
return;
|
||||
|
||||
var name = _prototype.Index<EntityPrototype>(component.SelectedEntity).Name;
|
||||
args.Message.PushNewline();
|
||||
args.Message.AddText(Loc.GetString("gift-packin-contains", ("name", name)));
|
||||
args.PushText(Loc.GetString("gift-packin-contains", ("name", name)));
|
||||
}
|
||||
|
||||
private void OnUseInHand(EntityUid uid, RandomGiftComponent component, UseInHandEvent args)
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@ namespace Content.Server.Holiday
|
|||
|
||||
public override void Initialize()
|
||||
{
|
||||
_configManager.OnValueChanged(CCVars.HolidaysEnabled, OnHolidaysEnableChange, true);
|
||||
|
||||
_configManager.OnValueChanged(CCVars.HolidaysEnabled, OnHolidaysEnableChange);
|
||||
SubscribeLocalEvent<GameRunLevelChangedEvent>(OnRunLevelChanged);
|
||||
SubscribeLocalEvent<HolidayVisualsComponent, ComponentInit>(OnVisualsInit);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,11 +25,14 @@ public sealed class HolosignSystem : EntitySystem
|
|||
var charges = UsesRemaining(component, battery);
|
||||
var maxCharges = MaxUses(component, battery);
|
||||
|
||||
args.PushMarkup(Loc.GetString("limited-charges-charges-remaining", ("charges", charges)));
|
||||
|
||||
if (charges > 0 && charges == maxCharges)
|
||||
using (args.PushGroup(nameof(HolosignProjectorComponent)))
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("limited-charges-max-charges"));
|
||||
args.PushMarkup(Loc.GetString("limited-charges-charges-remaining", ("charges", charges)));
|
||||
|
||||
if (charges > 0 && charges == maxCharges)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("limited-charges-max-charges"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ namespace Content.Server.Interaction
|
|||
|
||||
private void HandleUserInterfaceRangeCheck(ref BoundUserInterfaceCheckRangeEvent ev)
|
||||
{
|
||||
if (ev.Player.AttachedEntity is not { } user)
|
||||
if (ev.Player.AttachedEntity is not { } user || ev.Result == BoundUserInterfaceRangeResult.Fail)
|
||||
return;
|
||||
|
||||
if (InRangeUnobstructed(user, ev.Target, ev.UserInterface.InteractionRange))
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ namespace Content.Server.Kitchen.Components
|
|||
public string MachinePartCookTimeMultiplier = "Capacitor";
|
||||
[DataField("cookTimeScalingConstant")]
|
||||
public float CookTimeScalingConstant = 0.5f;
|
||||
[DataField("baseHeatMultiplier"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseHeatMultiplier = 100;
|
||||
[DataField("objectHeatMultiplier"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public float ObjectHeatMultiplier = 100;
|
||||
|
||||
[DataField("failureResult", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string BadRecipeEntityId = "FoodBadRecipe";
|
||||
|
|
@ -66,6 +70,9 @@ namespace Content.Server.Kitchen.Components
|
|||
public int CurrentCookTimeButtonIndex;
|
||||
|
||||
public Container Storage = default!;
|
||||
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public int Capacity = 10;
|
||||
}
|
||||
|
||||
public sealed class BeingMicrowavedEvent : HandledEntityEventArgs
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ using Robust.Shared.Audio.Systems;
|
|||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Player;
|
||||
using System.Linq;
|
||||
using Content.Shared.Access.Components;
|
||||
|
||||
namespace Content.Server.Kitchen.EntitySystems
|
||||
{
|
||||
|
|
@ -104,11 +105,11 @@ namespace Content.Server.Kitchen.EntitySystems
|
|||
/// <param name="time">The time on the microwave, in seconds.</param>
|
||||
private void AddTemperature(MicrowaveComponent component, float time)
|
||||
{
|
||||
var heatToAdd = time * 100;
|
||||
var heatToAdd = time * component.BaseHeatMultiplier;
|
||||
foreach (var entity in component.Storage.ContainedEntities)
|
||||
{
|
||||
if (TryComp<TemperatureComponent>(entity, out var tempComp))
|
||||
_temperature.ChangeHeat(entity, heatToAdd, false, tempComp);
|
||||
_temperature.ChangeHeat(entity, heatToAdd * component.ObjectHeatMultiplier, false, tempComp);
|
||||
|
||||
if (!TryComp<SolutionContainerManagerComponent>(entity, out var solutions))
|
||||
continue;
|
||||
|
|
@ -260,6 +261,12 @@ namespace Content.Server.Kitchen.EntitySystems
|
|||
return;
|
||||
}
|
||||
|
||||
if (ent.Comp.Storage.Count >= ent.Comp.Capacity)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("microwave-component-interact-full"), ent, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
_handsSystem.TryDropIntoContainer(args.User, args.Used, ent.Comp.Storage);
|
||||
UpdateUserInterfaceState(ent, ent.Comp);
|
||||
|
|
@ -475,10 +482,13 @@ namespace Content.Server.Kitchen.EntitySystems
|
|||
//check if there's still cook time left
|
||||
active.CookTimeRemaining -= frameTime;
|
||||
if (active.CookTimeRemaining > 0)
|
||||
{
|
||||
AddTemperature(microwave, frameTime);
|
||||
continue;
|
||||
}
|
||||
|
||||
//this means the microwave has finished cooking.
|
||||
AddTemperature(microwave, active.TotalTime);
|
||||
AddTemperature(microwave, Math.Max(frameTime + active.CookTimeRemaining, 0)); //Though there's still a little bit more heat to pump out
|
||||
|
||||
if (active.PortionedRecipe.Item1 != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -100,25 +100,28 @@ namespace Content.Server.Labels
|
|||
if (comp.LabelSlot.Item is not {Valid: true} item)
|
||||
return;
|
||||
|
||||
if (!args.IsInDetailsRange)
|
||||
using (args.PushGroup(nameof(PaperLabelComponent)))
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("comp-paper-label-has-label-cant-read"));
|
||||
return;
|
||||
if (!args.IsInDetailsRange)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("comp-paper-label-has-label-cant-read"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityManager.TryGetComponent(item, out PaperComponent? paper))
|
||||
// Assuming yaml has the correct entity whitelist, this should not happen.
|
||||
return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(paper.Content))
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("comp-paper-label-has-label-blank"));
|
||||
return;
|
||||
}
|
||||
|
||||
args.PushMarkup(Loc.GetString("comp-paper-label-has-label"));
|
||||
var text = paper.Content;
|
||||
args.PushMarkup(text.TrimEnd());
|
||||
}
|
||||
|
||||
if (!EntityManager.TryGetComponent(item, out PaperComponent? paper))
|
||||
// Assuming yaml has the correct entity whitelist, this should not happen.
|
||||
return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(paper.Content))
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("comp-paper-label-has-label-blank"));
|
||||
return;
|
||||
}
|
||||
|
||||
args.PushMarkup(Loc.GetString("comp-paper-label-has-label"));
|
||||
var text = paper.Content;
|
||||
args.PushMarkup(text.TrimEnd());
|
||||
}
|
||||
|
||||
private void OnContainerModified(EntityUid uid, PaperLabelComponent label, ContainerModifiedMessage args)
|
||||
|
|
|
|||
|
|
@ -47,28 +47,31 @@ public sealed class EmergencyLightSystem : SharedEmergencyLightSystem
|
|||
|
||||
private void OnEmergencyExamine(EntityUid uid, EmergencyLightComponent component, ExaminedEvent args)
|
||||
{
|
||||
args.PushMarkup(
|
||||
Loc.GetString("emergency-light-component-on-examine",
|
||||
("batteryStateText",
|
||||
Loc.GetString(component.BatteryStateText[component.State]))));
|
||||
using (args.PushGroup(nameof(EmergencyLightComponent)))
|
||||
{
|
||||
args.PushMarkup(
|
||||
Loc.GetString("emergency-light-component-on-examine",
|
||||
("batteryStateText",
|
||||
Loc.GetString(component.BatteryStateText[component.State]))));
|
||||
|
||||
// Show alert level on the light itself.
|
||||
if (!TryComp<AlertLevelComponent>(_station.GetOwningStation(uid), out var alerts))
|
||||
return;
|
||||
// Show alert level on the light itself.
|
||||
if (!TryComp<AlertLevelComponent>(_station.GetOwningStation(uid), out var alerts))
|
||||
return;
|
||||
|
||||
if (alerts.AlertLevels == null)
|
||||
return;
|
||||
if (alerts.AlertLevels == null)
|
||||
return;
|
||||
|
||||
var name = alerts.CurrentLevel;
|
||||
var name = alerts.CurrentLevel;
|
||||
|
||||
var color = Color.White;
|
||||
if (alerts.AlertLevels.Levels.TryGetValue(alerts.CurrentLevel, out var details))
|
||||
color = details.Color;
|
||||
var color = Color.White;
|
||||
if (alerts.AlertLevels.Levels.TryGetValue(alerts.CurrentLevel, out var details))
|
||||
color = details.Color;
|
||||
|
||||
args.PushMarkup(
|
||||
Loc.GetString("emergency-light-component-on-examine-alert",
|
||||
("color", color.ToHex()),
|
||||
("level", name)));
|
||||
args.PushMarkup(
|
||||
Loc.GetString("emergency-light-component-on-examine-alert",
|
||||
("color", color.ToHex()),
|
||||
("level", name)));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEmergencyLightEvent(EntityUid uid, EmergencyLightComponent component, EmergencyLightEvent args)
|
||||
|
|
|
|||
|
|
@ -33,23 +33,27 @@ public sealed class LightReplacerSystem : EntitySystem
|
|||
|
||||
private void OnExamined(EntityUid uid, LightReplacerComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (!component.InsertedBulbs.ContainedEntities.Any())
|
||||
using (args.PushGroup(nameof(LightReplacerComponent)))
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("comp-light-replacer-no-lights"));
|
||||
return;
|
||||
}
|
||||
args.PushMarkup(Loc.GetString("comp-light-replacer-has-lights"));
|
||||
var groups = new Dictionary<string, int>();
|
||||
var metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
foreach (var bulb in component.InsertedBulbs.ContainedEntities)
|
||||
{
|
||||
var metaData = metaQuery.GetComponent(bulb);
|
||||
groups[metaData.EntityName] = groups.GetValueOrDefault(metaData.EntityName) + 1;
|
||||
}
|
||||
if (!component.InsertedBulbs.ContainedEntities.Any())
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("comp-light-replacer-no-lights"));
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (name, amount) in groups)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("comp-light-replacer-light-listing", ("amount", amount), ("name", name)));
|
||||
args.PushMarkup(Loc.GetString("comp-light-replacer-has-lights"));
|
||||
var groups = new Dictionary<string, int>();
|
||||
var metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
foreach (var bulb in component.InsertedBulbs.ContainedEntities)
|
||||
{
|
||||
var metaData = metaQuery.GetComponent(bulb);
|
||||
groups[metaData.EntityName] = groups.GetValueOrDefault(metaData.EntityName) + 1;
|
||||
}
|
||||
|
||||
foreach (var (name, amount) in groups)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("comp-light-replacer-light-listing", ("amount", amount), ("name", name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,23 +3,45 @@ using Robust.Shared.Audio;
|
|||
|
||||
namespace Content.Server.MagicMirror;
|
||||
|
||||
/// <summary>
|
||||
/// Allows humanoids to change their appearance mid-round.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class MagicMirrorComponent : Component
|
||||
{
|
||||
public Entity<HumanoidAppearanceComponent>? Target;
|
||||
/// <summary>
|
||||
/// Magic mirror target, used for validating UI messages.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid? Target;
|
||||
|
||||
/// <summary>
|
||||
/// doafter time required to add a new slot
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float AddSlotTime = 5f;
|
||||
public TimeSpan AddSlotTime = TimeSpan.FromSeconds(5);
|
||||
|
||||
/// <summary>
|
||||
/// doafter time required to remove a existing slot
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float RemoveSlotTime = 2f;
|
||||
public TimeSpan RemoveSlotTime = TimeSpan.FromSeconds(2);
|
||||
|
||||
/// <summary>
|
||||
/// doafter time required to change slot
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float SelectSlotTime = 3f;
|
||||
public TimeSpan SelectSlotTime = TimeSpan.FromSeconds(3);
|
||||
|
||||
/// <summary>
|
||||
/// doafter time required to recolor slot
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float ChangeSlotTime = 1f;
|
||||
public TimeSpan ChangeSlotTime = TimeSpan.FromSeconds(1);
|
||||
|
||||
/// <summary>
|
||||
/// Sound emitted when slots are changed
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public SoundSpecifier ChangeHairSound = new SoundPathSpecifier("/Audio/Items/scissors.ogg");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,20 +13,23 @@ using Robust.Shared.Player;
|
|||
|
||||
namespace Content.Server.MagicMirror;
|
||||
|
||||
/// <summary>
|
||||
/// Allows humanoids to change their appearance mid-round.
|
||||
/// </summary>
|
||||
public sealed class MagicMirrorSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly MarkingManager _markings = default!;
|
||||
[Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<MagicMirrorComponent, ActivatableUIOpenAttemptEvent>(OnOpenUIAttempt);
|
||||
SubscribeLocalEvent<MagicMirrorComponent, AfterActivatableUIOpenEvent>(AfterUIOpen);
|
||||
SubscribeLocalEvent<MagicMirrorComponent, BoundUIClosedEvent>(OnUIClosed);
|
||||
SubscribeLocalEvent<MagicMirrorComponent, MagicMirrorSelectMessage>(OnMagicMirrorSelect);
|
||||
SubscribeLocalEvent<MagicMirrorComponent, MagicMirrorChangeColorMessage>(OnTryMagicMirrorChangeColor);
|
||||
SubscribeLocalEvent<MagicMirrorComponent, MagicMirrorAddSlotMessage>(OnTryMagicMirrorAddSlot);
|
||||
|
|
@ -34,21 +37,34 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||
|
||||
SubscribeLocalEvent<MagicMirrorComponent, AfterInteractEvent>(OnMagicMirrorInteract);
|
||||
|
||||
SubscribeLocalEvent<MagicMirrorComponent, SelectDoAfterEvent>(OnSelectSlotDoAfter);
|
||||
SubscribeLocalEvent<MagicMirrorComponent, ChangeColorDoAfterEvent>(OnChangeColorDoAfter);
|
||||
SubscribeLocalEvent<MagicMirrorComponent, RemoveSlotDoAfterEvent>(OnRemoveSlotDoAfter);
|
||||
SubscribeLocalEvent<MagicMirrorComponent, AddSlotDoAfterEvent>(OnAddSlotDoAfter);
|
||||
SubscribeLocalEvent<MagicMirrorComponent, MagicMirrorSelectDoAfterEvent>(OnSelectSlotDoAfter);
|
||||
SubscribeLocalEvent<MagicMirrorComponent, MagicMirrorChangeColorDoAfterEvent>(OnChangeColorDoAfter);
|
||||
SubscribeLocalEvent<MagicMirrorComponent, MagicMirrorRemoveSlotDoAfterEvent>(OnRemoveSlotDoAfter);
|
||||
SubscribeLocalEvent<MagicMirrorComponent, MagicMirrorAddSlotDoAfterEvent>(OnAddSlotDoAfter);
|
||||
|
||||
SubscribeLocalEvent<MagicMirrorComponent, BoundUserInterfaceCheckRangeEvent>(OnMirrorRangeCheck);
|
||||
}
|
||||
|
||||
private void OnMirrorRangeCheck(EntityUid uid, MagicMirrorComponent component, ref BoundUserInterfaceCheckRangeEvent args)
|
||||
{
|
||||
if (!Exists(component.Target) || !_interaction.InRangeUnobstructed(uid, component.Target.Value))
|
||||
{
|
||||
args.Result = BoundUserInterfaceRangeResult.Fail;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMagicMirrorInteract(Entity<MagicMirrorComponent> mirror, ref AfterInteractEvent args)
|
||||
{
|
||||
if (!TryComp<ActorComponent>(args.User, out var actor)) return;
|
||||
if (TryComp<HumanoidAppearanceComponent>(args.Target, out var humanoid))
|
||||
{
|
||||
mirror.Comp.Target = new Entity<HumanoidAppearanceComponent>(args.Target.Value, humanoid);
|
||||
UpdateInterface(mirror.Owner, mirror.Comp.Target.Value.Owner, actor.PlayerSession);
|
||||
Log.Debug($"Target {mirror.Comp.Target}!");
|
||||
};
|
||||
if (!args.CanReach || args.Target == null)
|
||||
return;
|
||||
|
||||
if (!TryComp<ActorComponent>(args.User, out var actor))
|
||||
return;
|
||||
|
||||
if (!_uiSystem.TryOpen(mirror.Owner, MagicMirrorUiKey.Key, actor.PlayerSession))
|
||||
return;
|
||||
|
||||
UpdateInterface(mirror.Owner, args.Target.Value, mirror.Comp);
|
||||
}
|
||||
|
||||
private void OnOpenUIAttempt(EntityUid uid, MagicMirrorComponent mirror, ActivatableUIOpenAttemptEvent args)
|
||||
|
|
@ -59,12 +75,19 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||
|
||||
private void OnMagicMirrorSelect(EntityUid uid, MagicMirrorComponent component, MagicMirrorSelectMessage message)
|
||||
{
|
||||
if (component.Target == null) return;
|
||||
if (message.Session.AttachedEntity == null) return;
|
||||
if (component.Target is not { } target || message.Session.AttachedEntity is not { } user)
|
||||
return;
|
||||
|
||||
var doAfter = new SelectDoAfterEvent(message);
|
||||
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Session.AttachedEntity.Value, component.SelectSlotTime, doAfter, uid, target: component.Target.Value.Owner, used: uid)
|
||||
var doAfter = new MagicMirrorSelectDoAfterEvent()
|
||||
{
|
||||
Category = message.Category,
|
||||
Slot = message.Slot,
|
||||
Marking = message.Marking,
|
||||
};
|
||||
|
||||
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.SelectSlotTime, doAfter, uid, target: target, used: uid)
|
||||
{
|
||||
DistanceThreshold = SharedInteractionSystem.InteractionRange,
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnDamage = true,
|
||||
BreakOnHandChange = false,
|
||||
|
|
@ -75,14 +98,18 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||
|
||||
_audio.PlayPvs(component.ChangeHairSound, uid);
|
||||
}
|
||||
private void OnSelectSlotDoAfter(EntityUid uid, MagicMirrorComponent component, SelectDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Args.Target == null || args.Cancelled)
|
||||
return;
|
||||
if (component.Target == null) return;
|
||||
|
||||
var category = MarkingCategories.Hair;
|
||||
switch (args.Message.Category)
|
||||
private void OnSelectSlotDoAfter(EntityUid uid, MagicMirrorComponent component, MagicMirrorSelectDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Target == null || args.Cancelled)
|
||||
return;
|
||||
|
||||
if (component.Target != args.Target)
|
||||
return;
|
||||
|
||||
MarkingCategories category;
|
||||
|
||||
switch (args.Category)
|
||||
{
|
||||
case MagicMirrorCategory.Hair:
|
||||
category = MarkingCategories.Hair;
|
||||
|
|
@ -94,18 +121,24 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||
return;
|
||||
}
|
||||
|
||||
_humanoid.SetMarkingId(component.Target.Value.Owner, category, args.Message.Slot, args.Message.Marking);
|
||||
_humanoid.SetMarkingId(component.Target.Value, category, args.Slot, args.Marking);
|
||||
|
||||
UpdateInterface(uid, component.Target.Value.Owner, args.Message.Session);
|
||||
UpdateInterface(uid, component.Target.Value, component);
|
||||
}
|
||||
|
||||
private void OnTryMagicMirrorChangeColor(EntityUid uid, MagicMirrorComponent component, MagicMirrorChangeColorMessage message)
|
||||
{
|
||||
if (component.Target == null) return;
|
||||
if (message.Session.AttachedEntity == null) return;
|
||||
if (component.Target is not { } target || message.Session.AttachedEntity is not { } user)
|
||||
return;
|
||||
|
||||
var doAfter = new ChangeColorDoAfterEvent(message);
|
||||
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Session.AttachedEntity.Value, component.ChangeSlotTime, doAfter, uid, target: component.Target.Value.Owner, used: uid)
|
||||
var doAfter = new MagicMirrorChangeColorDoAfterEvent()
|
||||
{
|
||||
Category = message.Category,
|
||||
Slot = message.Slot,
|
||||
Colors = message.Colors,
|
||||
};
|
||||
|
||||
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.ChangeSlotTime, doAfter, uid, target: target, used: uid)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnDamage = true,
|
||||
|
|
@ -115,14 +148,16 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||
NeedHand = true
|
||||
});
|
||||
}
|
||||
private void OnChangeColorDoAfter(EntityUid uid, MagicMirrorComponent component, ChangeColorDoAfterEvent args)
|
||||
private void OnChangeColorDoAfter(EntityUid uid, MagicMirrorComponent component, MagicMirrorChangeColorDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Args.Target == null || args.Cancelled)
|
||||
if (args.Handled || args.Target == null || args.Cancelled)
|
||||
return;
|
||||
if (component.Target == null) return;
|
||||
|
||||
var category = MarkingCategories.Hair;
|
||||
switch (args.Message.Category)
|
||||
if (component.Target != args.Target)
|
||||
return;
|
||||
|
||||
MarkingCategories category;
|
||||
switch (args.Category)
|
||||
{
|
||||
case MagicMirrorCategory.Hair:
|
||||
category = MarkingCategories.Hair;
|
||||
|
|
@ -134,20 +169,27 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||
return;
|
||||
}
|
||||
|
||||
_humanoid.SetMarkingColor(component.Target.Value.Owner, category, args.Message.Slot, args.Message.Colors);
|
||||
_humanoid.SetMarkingColor(component.Target.Value, category, args.Slot, args.Colors);
|
||||
|
||||
// using this makes the UI feel like total ass
|
||||
// que
|
||||
// UpdateInterface(uid, component.Target, message.Session);
|
||||
}
|
||||
|
||||
private void OnTryMagicMirrorRemoveSlot(EntityUid uid, MagicMirrorComponent component, MagicMirrorRemoveSlotMessage message)
|
||||
{
|
||||
if (component.Target == null) return;
|
||||
if (message.Session.AttachedEntity == null) return;
|
||||
if (component.Target is not { } target || message.Session.AttachedEntity is not { } user)
|
||||
return;
|
||||
|
||||
var doAfter = new RemoveSlotDoAfterEvent(message);
|
||||
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Session.AttachedEntity.Value, component.RemoveSlotTime, doAfter, uid, target: component.Target.Value.Owner, used: uid)
|
||||
var doAfter = new MagicMirrorRemoveSlotDoAfterEvent()
|
||||
{
|
||||
Category = message.Category,
|
||||
Slot = message.Slot,
|
||||
};
|
||||
|
||||
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.RemoveSlotTime, doAfter, uid, target: target, used: uid)
|
||||
{
|
||||
DistanceThreshold = SharedInteractionSystem.InteractionRange,
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnDamage = true,
|
||||
BreakOnHandChange = false,
|
||||
|
|
@ -158,15 +200,18 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||
|
||||
_audio.PlayPvs(component.ChangeHairSound, uid);
|
||||
}
|
||||
private void OnRemoveSlotDoAfter(EntityUid uid, MagicMirrorComponent component, RemoveSlotDoAfterEvent args)
|
||||
|
||||
private void OnRemoveSlotDoAfter(EntityUid uid, MagicMirrorComponent component, MagicMirrorRemoveSlotDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Args.Target == null || args.Cancelled)
|
||||
if (args.Handled || args.Target == null || args.Cancelled)
|
||||
return;
|
||||
|
||||
if (component.Target == null) return;
|
||||
if (component.Target != args.Target)
|
||||
return;
|
||||
|
||||
var category = MarkingCategories.Hair;
|
||||
switch (args.Message.Category)
|
||||
MarkingCategories category;
|
||||
|
||||
switch (args.Category)
|
||||
{
|
||||
case MagicMirrorCategory.Hair:
|
||||
category = MarkingCategories.Hair;
|
||||
|
|
@ -178,18 +223,25 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||
return;
|
||||
}
|
||||
|
||||
_humanoid.RemoveMarking(component.Target.Value.Owner, category, args.Message.Slot);
|
||||
_humanoid.RemoveMarking(component.Target.Value, category, args.Slot);
|
||||
|
||||
UpdateInterface(uid, component.Target.Value.Owner, args.Message.Session);
|
||||
UpdateInterface(uid, component.Target.Value, component);
|
||||
}
|
||||
|
||||
private void OnTryMagicMirrorAddSlot(EntityUid uid, MagicMirrorComponent component, MagicMirrorAddSlotMessage message)
|
||||
{
|
||||
if (component.Target == null) return;
|
||||
if (message.Session.AttachedEntity == null) return;
|
||||
if (component.Target == null)
|
||||
return;
|
||||
|
||||
var doAfter = new AddSlotDoAfterEvent(message);
|
||||
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Session.AttachedEntity.Value, component.AddSlotTime, doAfter, uid, target: component.Target.Value.Owner, used: uid)
|
||||
if (message.Session.AttachedEntity == null)
|
||||
return;
|
||||
|
||||
var doAfter = new MagicMirrorAddSlotDoAfterEvent()
|
||||
{
|
||||
Category = message.Category,
|
||||
};
|
||||
|
||||
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Session.AttachedEntity.Value, component.AddSlotTime, doAfter, uid, target: component.Target.Value, used: uid)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnDamage = true,
|
||||
|
|
@ -198,17 +250,17 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||
BreakOnWeightlessMove = false,
|
||||
NeedHand = true
|
||||
});
|
||||
|
||||
_audio.PlayPvs(component.ChangeHairSound, uid);
|
||||
}
|
||||
private void OnAddSlotDoAfter(EntityUid uid, MagicMirrorComponent component, AddSlotDoAfterEvent args)
|
||||
private void OnAddSlotDoAfter(EntityUid uid, MagicMirrorComponent component, MagicMirrorAddSlotDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Args.Target == null || args.Cancelled)
|
||||
if (args.Handled || args.Target == null || args.Cancelled || !TryComp(component.Target, out HumanoidAppearanceComponent? humanoid))
|
||||
return;
|
||||
|
||||
if (component.Target == null) return;
|
||||
MarkingCategories category;
|
||||
|
||||
var category = MarkingCategories.Hair;
|
||||
switch (args.Message.Category)
|
||||
switch (args.Category)
|
||||
{
|
||||
case MagicMirrorCategory.Hair:
|
||||
category = MarkingCategories.Hair;
|
||||
|
|
@ -220,21 +272,21 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||
return;
|
||||
}
|
||||
|
||||
var marking = _markings.MarkingsByCategoryAndSpecies(category, component.Target.Value.Comp.Species).Keys.FirstOrDefault();
|
||||
var marking = _markings.MarkingsByCategoryAndSpecies(category, humanoid.Species).Keys.FirstOrDefault();
|
||||
|
||||
if (string.IsNullOrEmpty(marking))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_humanoid.AddMarking(component.Target.Value.Owner, marking, Color.Black);
|
||||
_humanoid.AddMarking(component.Target.Value, marking, Color.Black);
|
||||
|
||||
UpdateInterface(uid, component.Target.Value, component);
|
||||
|
||||
UpdateInterface(uid, component.Target.Value.Owner, args.Message.Session);
|
||||
}
|
||||
|
||||
private void UpdateInterface(EntityUid uid, EntityUid playerUid, ICommonSession session)
|
||||
private void UpdateInterface(EntityUid mirrorUid, EntityUid targetUid, MagicMirrorComponent component)
|
||||
{
|
||||
if (!TryComp<HumanoidAppearanceComponent>(playerUid, out var humanoid)) return;
|
||||
if (session is not { } player) return;
|
||||
if (!TryComp<HumanoidAppearanceComponent>(targetUid, out var humanoid))
|
||||
return;
|
||||
|
||||
var hair = humanoid.MarkingSet.TryGetCategory(MarkingCategories.Hair, out var hairMarkings)
|
||||
? new List<Marking>(hairMarkings)
|
||||
|
|
@ -244,21 +296,19 @@ public sealed class MagicMirrorSystem : EntitySystem
|
|||
? new List<Marking>(facialHairMarkings)
|
||||
: new();
|
||||
|
||||
var msg = new MagicMirrorUiData(
|
||||
var state = new MagicMirrorUiState(
|
||||
humanoid.Species,
|
||||
hair,
|
||||
humanoid.MarkingSet.PointsLeft(MarkingCategories.Hair) + hair.Count,
|
||||
facialHair,
|
||||
humanoid.MarkingSet.PointsLeft(MarkingCategories.FacialHair) + facialHair.Count);
|
||||
|
||||
_uiSystem.TrySendUiMessage(uid, MagicMirrorUiKey.Key, msg, player);
|
||||
component.Target = targetUid;
|
||||
_uiSystem.TrySetUiState(mirrorUid, MagicMirrorUiKey.Key, state);
|
||||
}
|
||||
|
||||
private void AfterUIOpen(EntityUid uid, MagicMirrorComponent component, AfterActivatableUIOpenEvent args)
|
||||
private void OnUIClosed(Entity<MagicMirrorComponent> ent, ref BoundUIClosedEvent args)
|
||||
{
|
||||
if (!TryComp<HumanoidAppearanceComponent>(args.User, out var humanoid)) return;
|
||||
component.Target = new Entity<HumanoidAppearanceComponent>(args.User, humanoid);
|
||||
|
||||
UpdateInterface(uid, args.User, args.Session);
|
||||
ent.Comp.Target = null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -208,10 +208,13 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem
|
|||
var container = _itemSlotsSystem.GetItemOrNull(entity.Owner, entity.Comp.SolutionContainerName);
|
||||
if (args.IsInDetailsRange && container != null && _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out _, out var containerSolution))
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("cryo-pod-examine", ("beaker", Name(container.Value))));
|
||||
if (containerSolution.Volume == 0)
|
||||
using (args.PushGroup(nameof(CryoPodComponent)))
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("cryo-pod-empty-beaker"));
|
||||
args.PushMarkup(Loc.GetString("cryo-pod-examine", ("beaker", Name(container.Value))));
|
||||
if (containerSolution.Volume == 0)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("cryo-pod-empty-beaker"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue