mirror of
https://github.com/corvax-team/ss14-wl.git
synced 2026-02-15 03:31:38 +01:00
Merge remote-tracking branch 'upstream/master' into upstream-sync
# Conflicts: # Content.Server/Nuke/NukeComponent.cs # Resources/Prototypes/Voice/disease_emotes.yml
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -305,3 +305,4 @@ Resources/MapImages
|
||||
/Content.Docfx/api/
|
||||
/Content.Docfx/*site
|
||||
|
||||
*.bak
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Client.Eui;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Eui;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -12,7 +13,7 @@ namespace Content.Client.Administration.UI
|
||||
public AdminAnnounceEui()
|
||||
{
|
||||
_window = new AdminAnnounceWindow();
|
||||
_window.OnClose += () => SendMessage(new AdminAnnounceEuiMsg.Close());
|
||||
_window.OnClose += () => SendMessage(new CloseEuiMessage());
|
||||
_window.AnnounceButton.OnPressed += AnnounceButtonOnOnPressed;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Client.Eui;
|
||||
using Content.Shared.Administration.BanList;
|
||||
using Content.Shared.Eui;
|
||||
using Content.Shared.Ghost.Roles;
|
||||
|
||||
namespace Content.Client.Administration.UI.BanList;
|
||||
|
||||
@@ -9,9 +10,21 @@ public sealed class BanListEui : BaseEui
|
||||
public BanListEui()
|
||||
{
|
||||
BanWindow = new BanListWindow();
|
||||
BanWindow.OnClose += OnClosed;
|
||||
BanControl = BanWindow.BanList;
|
||||
}
|
||||
|
||||
private void OnClosed()
|
||||
{
|
||||
SendMessage(new CloseEuiMessage());
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
base.Closed();
|
||||
BanWindow.Close();
|
||||
}
|
||||
|
||||
private BanListWindow BanWindow { get; }
|
||||
|
||||
private BanListControl BanControl { get; }
|
||||
|
||||
@@ -19,6 +19,7 @@ public sealed class AdminLogsEui : BaseEui
|
||||
public AdminLogsEui()
|
||||
{
|
||||
LogsWindow = new AdminLogsWindow();
|
||||
LogsWindow.OnClose += OnCloseWindow;
|
||||
LogsControl = LogsWindow.Logs;
|
||||
|
||||
LogsControl.LogSearch.OnTextEntered += _ => RequestLogs();
|
||||
@@ -39,7 +40,13 @@ public sealed class AdminLogsEui : BaseEui
|
||||
|
||||
private void OnRequestClosed(WindowRequestClosedEventArgs args)
|
||||
{
|
||||
SendMessage(new Close());
|
||||
SendMessage(new CloseEuiMessage());
|
||||
}
|
||||
|
||||
private void OnCloseWindow()
|
||||
{
|
||||
if (ClydeWindow == null)
|
||||
SendMessage(new CloseEuiMessage());
|
||||
}
|
||||
|
||||
private void RequestLogs()
|
||||
@@ -74,10 +81,6 @@ public sealed class AdminLogsEui : BaseEui
|
||||
return;
|
||||
}
|
||||
|
||||
LogsControl.Orphan();
|
||||
LogsWindow.Dispose();
|
||||
LogsWindow = null;
|
||||
|
||||
var monitor = _clyde.EnumerateMonitors().First();
|
||||
|
||||
ClydeWindow = _clyde.CreateWindow(new WindowCreateParameters
|
||||
@@ -89,6 +92,10 @@ public sealed class AdminLogsEui : BaseEui
|
||||
Height = 400
|
||||
});
|
||||
|
||||
LogsControl.Orphan();
|
||||
LogsWindow.Dispose();
|
||||
LogsWindow = null;
|
||||
|
||||
ClydeWindow.RequestClosed += OnRequestClosed;
|
||||
ClydeWindow.DisposeOnClose = true;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Content.Client.Administration.UI.ManageSolutions
|
||||
public EditSolutionsEui()
|
||||
{
|
||||
_window = new EditSolutionsWindow();
|
||||
_window.OnClose += () => SendMessage(new EditSolutionsEuiMsg.Close());
|
||||
_window.OnClose += () => SendMessage(new CloseEuiMessage());
|
||||
}
|
||||
|
||||
public override void Opened()
|
||||
@@ -28,7 +28,6 @@ namespace Content.Client.Administration.UI.ManageSolutions
|
||||
public override void Closed()
|
||||
{
|
||||
base.Closed();
|
||||
_window.OnClose -= () => SendMessage(new EditSolutionsEuiMsg.Close());
|
||||
_window.Close();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,18 @@ public sealed class AdminNotesEui : BaseEui
|
||||
NoteControl.OnNoteChanged += (id, text) => SendMessage(new EditNoteRequest(id, text));
|
||||
NoteControl.OnNewNoteEntered += text => SendMessage(new CreateNoteRequest(text));
|
||||
NoteControl.OnNoteDeleted += id => SendMessage(new DeleteNoteRequest(id));
|
||||
NoteWindow.OnClose += OnClosed;
|
||||
}
|
||||
|
||||
private void OnClosed()
|
||||
{
|
||||
SendMessage(new CloseEuiMessage());
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
base.Closed();
|
||||
NoteWindow.Close();
|
||||
}
|
||||
|
||||
private AdminNotesWindow NoteWindow { get; }
|
||||
|
||||
@@ -45,6 +45,7 @@ namespace Content.Client.Administration.UI
|
||||
{
|
||||
base.Closed();
|
||||
|
||||
SendMessage(new CloseEuiMessage());
|
||||
CloseEverything();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,12 @@ namespace Content.Client.Administration.UI.SetOutfit
|
||||
public SetOutfitEui()
|
||||
{
|
||||
_window = new SetOutfitMenu();
|
||||
_window.OnClose += OnClosed;
|
||||
}
|
||||
|
||||
private void OnClosed()
|
||||
{
|
||||
SendMessage(new CloseEuiMessage());
|
||||
}
|
||||
|
||||
public override void Opened()
|
||||
|
||||
@@ -38,7 +38,7 @@ public sealed class SpawnExplosionEui : BaseEui
|
||||
|
||||
public void SendClosedMessage()
|
||||
{
|
||||
SendMessage(new SpawnExplosionEuiMsg.Close());
|
||||
SendMessage(new CloseEuiMessage());
|
||||
}
|
||||
|
||||
public void ClearOverlay()
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'anomaly-generator-ui-title'}"
|
||||
MinSize="270 180"
|
||||
SetSize="360 180">
|
||||
<BoxContainer Margin="10 0 10 0"
|
||||
|
||||
Resizable="False">
|
||||
<BoxContainer Margin="0 0"
|
||||
Orientation="Vertical"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="True">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<BoxContainer Margin="10 0 10 0" Orientation="Horizontal">
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
HorizontalExpand="True"
|
||||
@@ -30,6 +30,7 @@
|
||||
<RichTextLabel Name="CooldownLabel" StyleClasses="StatusFieldTitle" />
|
||||
<RichTextLabel Name="ReadyLabel" StyleClasses="StatusFieldTitle" />
|
||||
</BoxContainer>
|
||||
<!--Sprite View-->
|
||||
<PanelContainer Margin="12 0 0 0"
|
||||
StyleClasses="Inset"
|
||||
VerticalAlignment="Center">
|
||||
@@ -40,9 +41,22 @@
|
||||
</BoxContainer>
|
||||
<BoxContainer VerticalExpand="True"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
VerticalAlignment="Center"
|
||||
Margin="0 10">
|
||||
<Button Name="GenerateButton"
|
||||
Text="{Loc 'anomaly-generator-generate'}"></Button>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Footer -->
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
|
||||
<Label Text="{Loc 'anomaly-generator-flavor-left'}" StyleClasses="WindowFooterText" />
|
||||
<Label Text="{Loc 'anomaly-generator-flavor-right'}" StyleClasses="WindowFooterText"
|
||||
HorizontalAlignment="Right" HorizontalExpand="True" Margin="0 0 5 0" />
|
||||
<TextureRect StyleClasses="NTLogoDark" Stretch="KeepAspectCentered"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="19 19"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
|
||||
@@ -25,6 +25,7 @@ public sealed partial class AnomalyGeneratorWindow : FancyWindow
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
EntityView.Sprite = _entityManager.GetComponent<SpriteComponent>(gen);
|
||||
EntityView.SpriteOffset = false;
|
||||
|
||||
GenerateButton.OnPressed += _ => OnGenerateButtonPressed?.Invoke();
|
||||
}
|
||||
|
||||
24
Content.Client/Chemistry/EntitySystems/PillSystem.cs
Normal file
24
Content.Client/Chemistry/EntitySystems/PillSystem.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Chemistry.EntitySystems;
|
||||
|
||||
public sealed class PillSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PillComponent, AfterAutoHandleStateEvent>(OnHandleState);
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, PillComponent component, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (!TryComp(uid, out SpriteComponent? sprite))
|
||||
return;
|
||||
|
||||
if (!sprite.TryGetLayer(0, out var layer))
|
||||
return;
|
||||
|
||||
layer.SetState($"pill{component.PillType + 1}");
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ namespace Content.Client.Cloning.UI
|
||||
_window.Close();
|
||||
};
|
||||
|
||||
_window.OnClose += () => SendMessage(new AcceptCloningChoiceMessage(AcceptCloningUiButton.Deny));
|
||||
|
||||
_window.AcceptButton.OnPressed += _ =>
|
||||
{
|
||||
SendMessage(new AcceptCloningChoiceMessage(AcceptCloningUiButton.Accept));
|
||||
|
||||
@@ -16,7 +16,7 @@ public sealed class CrewManifestEui : BaseEui
|
||||
|
||||
_window.OnClose += () =>
|
||||
{
|
||||
SendMessage(new CrewManifestEuiClosed());
|
||||
SendMessage(new CloseEuiMessage());
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Content.Shared.Eye.Blinding.Components;
|
||||
using static Content.Shared.Interaction.SharedInteractionSystem;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using Robust.Client.Player;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.Eye.Blinding;
|
||||
using Content.Shared.Eye.Blinding.Components;
|
||||
|
||||
namespace Content.Client.Eye.Blinding
|
||||
{
|
||||
@@ -46,7 +47,7 @@ namespace Content.Client.Eye.Blinding
|
||||
|
||||
_blindableComponent = blindComp;
|
||||
|
||||
var blind = _blindableComponent.Sources > 0;
|
||||
var blind = _blindableComponent.IsBlind;
|
||||
|
||||
if (!blind && _blindableComponent.LightSetup) // Turn FOV back on if we can see again
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Administration.Events;
|
||||
using Content.Shared.Eye.Blinding.Components;
|
||||
using Content.Shared.GameTicking;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
@@ -4,24 +4,21 @@ using Robust.Client.Player;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.Eye.Blinding;
|
||||
using Content.Shared.Eye.Blinding.Components;
|
||||
|
||||
namespace Content.Client.Eye.Blinding
|
||||
{
|
||||
public sealed class BlurryVisionOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
public override bool RequestScreenTexture => true;
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
private readonly ShaderInstance _dim;
|
||||
private BlurryVisionComponent _blurryVisionComponent = default!;
|
||||
private float _magnitude;
|
||||
|
||||
public BlurryVisionOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_dim = _prototypeManager.Index<ShaderPrototype>("Dim").InstanceUnique();
|
||||
}
|
||||
|
||||
protected override bool BeforeDraw(in OverlayDrawArgs args)
|
||||
@@ -40,33 +37,29 @@ namespace Content.Client.Eye.Blinding
|
||||
if (!_entityManager.TryGetComponent<BlurryVisionComponent>(playerEntity, out var blurComp))
|
||||
return false;
|
||||
|
||||
if (!blurComp.Active)
|
||||
if (blurComp.Magnitude <= 0)
|
||||
return false;
|
||||
|
||||
if (_entityManager.TryGetComponent<BlindableComponent>(playerEntity, out var blindComp)
|
||||
&& blindComp.Sources > 0)
|
||||
&& blindComp.IsBlind)
|
||||
return false;
|
||||
|
||||
_blurryVisionComponent = blurComp;
|
||||
_magnitude = blurComp.Magnitude;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (ScreenTexture == null)
|
||||
return;
|
||||
|
||||
var opacity = -(_blurryVisionComponent.Magnitude / 15) + 0.9f;
|
||||
|
||||
_dim.SetParameter("DAMAGE_AMOUNT", opacity);
|
||||
|
||||
// TODO make this better.
|
||||
// This is a really shitty effect.
|
||||
// Maybe gradually shrink the view-size?
|
||||
// Make the effect only apply to the edge of the viewport?
|
||||
// Actually make it blurry??
|
||||
var opacity = 0.5f * _magnitude / BlurryVisionComponent.MaxMagnitude;
|
||||
var worldHandle = args.WorldHandle;
|
||||
var viewport = args.WorldBounds;
|
||||
|
||||
worldHandle.UseShader(_dim);
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
worldHandle.DrawRect(viewport, Color.Black);
|
||||
worldHandle.UseShader(null);
|
||||
worldHandle.DrawRect(viewport, Color.White.WithAlpha(opacity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.Eye.Blinding;
|
||||
using Content.Shared.Eye.Blinding.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
@@ -22,8 +23,6 @@ public sealed class BlurryVisionSystem : EntitySystem
|
||||
SubscribeLocalEvent<BlurryVisionComponent, PlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<BlurryVisionComponent, PlayerDetachedEvent>(OnPlayerDetached);
|
||||
|
||||
SubscribeLocalEvent<BlurryVisionComponent, ComponentHandleState>(OnHandleState);
|
||||
|
||||
_overlay = new();
|
||||
}
|
||||
|
||||
@@ -50,12 +49,4 @@ public sealed class BlurryVisionSystem : EntitySystem
|
||||
_overlayMan.RemoveOverlay(_overlay);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, BlurryVisionComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not BlurryVisionComponentState state)
|
||||
return;
|
||||
|
||||
component.Magnitude = state.Magnitude;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public sealed class PuddleSystem : SharedPuddleSystem
|
||||
args.Sprite.LayerSetState(0, $"{smooth.StateBase}a");
|
||||
_smooth.SetEnabled(uid, false, smooth);
|
||||
}
|
||||
else if (volume < 0.6f)
|
||||
else if (volume < MediumThreshold)
|
||||
{
|
||||
args.Sprite.LayerSetState(0, $"{smooth.StateBase}b");
|
||||
_smooth.SetEnabled(uid, false, smooth);
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Content.Client.Hands
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
|
||||
private HandsSystem? _hands;
|
||||
private readonly IRenderTexture _renderBackbuffer;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||
@@ -58,9 +59,10 @@ namespace Content.Client.Hands
|
||||
return;
|
||||
}
|
||||
|
||||
var handEntity = EntityOverride ?? EntitySystem.Get<HandsSystem>().GetActiveHandEntity();
|
||||
_hands ??= _entMan.System<HandsSystem>();
|
||||
var handEntity = _hands.GetActiveHandEntity();
|
||||
|
||||
if (handEntity == null || !_entMan.HasComponent<SpriteComponent>(handEntity))
|
||||
if (handEntity == null || !_entMan.TryGetComponent(handEntity, out SpriteComponent? sprite))
|
||||
return;
|
||||
|
||||
var halfSize = _renderBackbuffer.Size / 2;
|
||||
@@ -68,7 +70,7 @@ namespace Content.Client.Hands
|
||||
|
||||
screen.RenderInRenderTarget(_renderBackbuffer, () =>
|
||||
{
|
||||
screen.DrawEntity(handEntity.Value, halfSize, new Vector2(1f, 1f) * uiScale, Direction.South);
|
||||
screen.DrawEntity(handEntity.Value, halfSize, new Vector2(1f, 1f) * uiScale, Angle.Zero, Angle.Zero, Direction.South, sprite);
|
||||
}, Color.Transparent);
|
||||
|
||||
screen.DrawTexture(_renderBackbuffer.Texture, mousePos - halfSize + offset, Color.White.WithAlpha(0.75f));
|
||||
|
||||
@@ -14,8 +14,10 @@ namespace Content.Client.Input
|
||||
var common = contexts.GetContext("common");
|
||||
common.AddFunction(ContentKeyFunctions.FocusChat);
|
||||
common.AddFunction(ContentKeyFunctions.FocusLocalChat);
|
||||
common.AddFunction(ContentKeyFunctions.FocusEmote);
|
||||
common.AddFunction(ContentKeyFunctions.FocusWhisperChat);
|
||||
common.AddFunction(ContentKeyFunctions.FocusRadio);
|
||||
common.AddFunction(ContentKeyFunctions.FocusLOOC);
|
||||
common.AddFunction(ContentKeyFunctions.FocusOOC);
|
||||
common.AddFunction(ContentKeyFunctions.FocusAdminChat);
|
||||
common.AddFunction(ContentKeyFunctions.FocusConsoleChat);
|
||||
|
||||
@@ -40,9 +40,14 @@ public sealed class MaterialStorageSystem : SharedMaterialStorageSystem
|
||||
}
|
||||
}
|
||||
|
||||
public override bool TryInsertMaterialEntity(EntityUid user, EntityUid toInsert, EntityUid receiver, MaterialStorageComponent? component = null)
|
||||
public override bool TryInsertMaterialEntity(EntityUid user,
|
||||
EntityUid toInsert,
|
||||
EntityUid receiver,
|
||||
MaterialStorageComponent? storage = null,
|
||||
MaterialComponent? material = null,
|
||||
PhysicalCompositionComponent? composition = null)
|
||||
{
|
||||
if (!base.TryInsertMaterialEntity(user, toInsert, receiver, component))
|
||||
if (!base.TryInsertMaterialEntity(user, toInsert, receiver, storage, material, composition))
|
||||
return false;
|
||||
_transform.DetachParentToNull(toInsert, Transform(toInsert));
|
||||
return true;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Client.Eui;
|
||||
using Content.Shared.Eui;
|
||||
|
||||
namespace Content.Client.NPC;
|
||||
|
||||
@@ -11,6 +12,12 @@ public sealed class NPCEui : BaseEui
|
||||
base.Opened();
|
||||
_window = new NPCWindow();
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += OnClosed;
|
||||
}
|
||||
|
||||
private void OnClosed()
|
||||
{
|
||||
SendMessage(new CloseEuiMessage());
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="NPC debug"
|
||||
MinSize="200 200">
|
||||
<BoxContainer Name="Options" Orientation="Vertical">
|
||||
<BoxContainer Name="Options" Orientation="Vertical" Margin="8 8">
|
||||
<controls:StripeBack>
|
||||
<Label Text="NPC" HorizontalAlignment="Center"/>
|
||||
</controls:StripeBack>
|
||||
<BoxContainer Name="NPCBox" Orientation="Vertical">
|
||||
<CheckBox Name="NPCPath" Text="Path"/>
|
||||
<CheckBox Name="NPCThonk" Text="Thonk"/>
|
||||
</BoxContainer>
|
||||
<controls:StripeBack>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Client.NPC.HTN;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.NPC;
|
||||
using Robust.Client.AutoGenerated;
|
||||
@@ -13,14 +14,17 @@ public sealed partial class NPCWindow : FancyWindow
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
var sysManager = IoCManager.Resolve<IEntitySystemManager>();
|
||||
var htn = sysManager.GetEntitySystem<HTNSystem>();
|
||||
var path = sysManager.GetEntitySystem<PathfindingSystem>();
|
||||
|
||||
NPCThonk.Pressed = htn.EnableOverlay;
|
||||
PathCrumbs.Pressed = (path.Modes & PathfindingDebugMode.Breadcrumbs) != 0x0;
|
||||
PathPolys.Pressed = (path.Modes & PathfindingDebugMode.Polys) != 0x0;
|
||||
PathNeighbors.Pressed = (path.Modes & PathfindingDebugMode.PolyNeighbors) != 0x0;
|
||||
PathRouteCosts.Pressed = (path.Modes & PathfindingDebugMode.RouteCosts) != 0x0;
|
||||
PathRoutes.Pressed = (path.Modes & PathfindingDebugMode.Routes) != 0x0;
|
||||
|
||||
NPCThonk.OnToggled += args => htn.EnableOverlay = args.Pressed;
|
||||
PathCrumbs.OnToggled += args => path.Modes ^= PathfindingDebugMode.Breadcrumbs;
|
||||
PathPolys.OnToggled += args => path.Modes ^= PathfindingDebugMode.Polys;
|
||||
PathNeighbors.OnToggled += args => path.Modes ^= PathfindingDebugMode.PolyNeighbors;
|
||||
|
||||
@@ -129,8 +129,10 @@ namespace Content.Client.Options.UI.Tabs
|
||||
AddHeader("ui-options-header-ui");
|
||||
AddButton(ContentKeyFunctions.FocusChat);
|
||||
AddButton(ContentKeyFunctions.FocusLocalChat);
|
||||
AddButton(ContentKeyFunctions.FocusEmote);
|
||||
AddButton(ContentKeyFunctions.FocusWhisperChat);
|
||||
AddButton(ContentKeyFunctions.FocusRadio);
|
||||
AddButton(ContentKeyFunctions.FocusLOOC);
|
||||
AddButton(ContentKeyFunctions.FocusOOC);
|
||||
AddButton(ContentKeyFunctions.FocusAdminChat);
|
||||
AddButton(ContentKeyFunctions.FocusDeadChat);
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
using Content.Shared.Power;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.PowerCell
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class PowerChargerVisualizer : AppearanceVisualizer
|
||||
{
|
||||
[Obsolete("Subscribe to your component being initialised instead.")]
|
||||
public override void InitializeEntity(EntityUid entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
var sprite = IoCManager.Resolve<IEntityManager>().GetComponent<SpriteComponent>(entity);
|
||||
|
||||
// Base item
|
||||
sprite.LayerMapSet(Layers.Base, sprite.AddLayerState("empty"));
|
||||
|
||||
// Light
|
||||
sprite.LayerMapSet(Layers.Light, sprite.AddLayerState("light-off"));
|
||||
sprite.LayerSetShader(Layers.Light, "unshaded");
|
||||
}
|
||||
|
||||
[Obsolete("Subscribe to AppearanceChangeEvent instead.")]
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = IoCManager.Resolve<IEntityManager>().GetComponent<SpriteComponent>(component.Owner);
|
||||
|
||||
// Update base item
|
||||
if (component.TryGetData(CellVisual.Occupied, out bool occupied))
|
||||
{
|
||||
// TODO: don't throw if it doesn't have a full state
|
||||
sprite.LayerSetState(Layers.Base, occupied ? "full" : "empty");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(Layers.Base, "empty");
|
||||
}
|
||||
|
||||
// Update lighting
|
||||
if (component.TryGetData(CellVisual.Light, out CellChargerStatus status))
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case CellChargerStatus.Off:
|
||||
sprite.LayerSetState(Layers.Light, "light-off");
|
||||
break;
|
||||
case CellChargerStatus.Empty:
|
||||
sprite.LayerSetState(Layers.Light, "light-empty");
|
||||
break;
|
||||
case CellChargerStatus.Charging:
|
||||
sprite.LayerSetState(Layers.Light, "light-charging");
|
||||
break;
|
||||
case CellChargerStatus.Charged:
|
||||
sprite.LayerSetState(Layers.Light, "light-charged");
|
||||
break;
|
||||
default:
|
||||
sprite.LayerSetState(Layers.Light, "light-off");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(Layers.Light, "light-off");
|
||||
}
|
||||
}
|
||||
|
||||
enum Layers : byte
|
||||
{
|
||||
Base,
|
||||
Light,
|
||||
}
|
||||
}
|
||||
}
|
||||
39
Content.Client/PowerCell/PowerChargerVisualizerComponent.cs
Normal file
39
Content.Client/PowerCell/PowerChargerVisualizerComponent.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using Content.Shared.Power;
|
||||
|
||||
namespace Content.Client.PowerCell;
|
||||
|
||||
[RegisterComponent]
|
||||
[Access(typeof(PowerChargerVisualizerSystem))]
|
||||
public sealed class PowerChargerVisualsComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The base sprite state used if the power cell charger does not contain a power cell.
|
||||
/// </summary>
|
||||
[DataField("emptyState")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string EmptyState = "empty";
|
||||
|
||||
/// <summary>
|
||||
/// The base sprite state used if the power cell charger contains a power cell.
|
||||
/// </summary>
|
||||
[DataField("occupiedState")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string OccupiedState = "full";
|
||||
|
||||
/// <summary>
|
||||
/// A mapping of the indicator light overlays for the power cell charger.
|
||||
/// <see cref="CellChargerStatus.Off"/> Maps to the state used when the charger is out of power/disabled.
|
||||
/// <see cref="CellChargerStatus.Empty"/> Maps to the state used when the charger does not contain a power cell.
|
||||
/// <see cref="CellChargerStatus.Charging"/> Maps to the state used when the charger is charging a power cell.
|
||||
/// <see cref="CellChargerStatus.Charged"/> Maps to the state used when the charger contains a fully charged power cell.
|
||||
/// </summary>
|
||||
[DataField("lightStates")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public readonly Dictionary<CellChargerStatus, string> LightStates = new()
|
||||
{
|
||||
[CellChargerStatus.Off] = "light-off",
|
||||
[CellChargerStatus.Empty] = "light-empty",
|
||||
[CellChargerStatus.Charging] = "light-charging",
|
||||
[CellChargerStatus.Charged] = "light-charged",
|
||||
};
|
||||
}
|
||||
41
Content.Client/PowerCell/PowerChargerVisualizerSystem.cs
Normal file
41
Content.Client/PowerCell/PowerChargerVisualizerSystem.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Content.Shared.Power;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.PowerCell;
|
||||
|
||||
public sealed class PowerChargerVisualizerSystem : VisualizerSystem<PowerChargerVisualsComponent>
|
||||
{
|
||||
protected override void OnAppearanceChange(EntityUid uid, PowerChargerVisualsComponent comp, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
// Update base item
|
||||
if (AppearanceSystem.TryGetData<bool>(uid, CellVisual.Occupied, out var occupied, args.Component) && occupied)
|
||||
{
|
||||
// TODO: don't throw if it doesn't have a full state
|
||||
args.Sprite.LayerSetState(PowerChargerVisualLayers.Base, comp.OccupiedState);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Sprite.LayerSetState(PowerChargerVisualLayers.Base, comp.EmptyState);
|
||||
}
|
||||
|
||||
// Update lighting
|
||||
if (AppearanceSystem.TryGetData<CellChargerStatus>(uid, CellVisual.Light, out var status, args.Component)
|
||||
&& comp.LightStates.TryGetValue(status, out var lightState))
|
||||
{
|
||||
args.Sprite.LayerSetState(PowerChargerVisualLayers.Light, lightState);
|
||||
args.Sprite.LayerSetVisible(PowerChargerVisualLayers.Light, true);
|
||||
}
|
||||
else
|
||||
//
|
||||
args.Sprite.LayerSetVisible(PowerChargerVisualLayers.Light, false);
|
||||
}
|
||||
}
|
||||
|
||||
enum PowerChargerVisualLayers : byte
|
||||
{
|
||||
Base,
|
||||
Light,
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using Content.Shared.Smoking;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.Smoking
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class BurnStateVisualizer : AppearanceVisualizer, ISerializationHooks
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
|
||||
[DataField("burntIcon")]
|
||||
private string _burntIcon = "burnt-icon";
|
||||
[DataField("litIcon")]
|
||||
private string _litIcon = "lit-icon";
|
||||
[DataField("unlitIcon")]
|
||||
private string _unlitIcon = "icon";
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
[Obsolete("Subscribe to AppearanceChangeEvent instead.")]
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (!_entMan.TryGetComponent(component.Owner, out SpriteComponent? sprite))
|
||||
return;
|
||||
|
||||
if (!component.TryGetData<SmokableState>(SmokingVisuals.Smoking, out var burnState))
|
||||
return;
|
||||
|
||||
var state = burnState switch
|
||||
{
|
||||
SmokableState.Lit => _litIcon,
|
||||
SmokableState.Burnt => _burntIcon,
|
||||
_ => _unlitIcon
|
||||
};
|
||||
|
||||
sprite.LayerSetState(0, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Content.Client/Smoking/BurnStateVisualizerSystem.cs
Normal file
25
Content.Client/Smoking/BurnStateVisualizerSystem.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Robust.Client.GameObjects;
|
||||
using Content.Shared.Smoking;
|
||||
|
||||
namespace Content.Client.Smoking;
|
||||
|
||||
public sealed class BurnStateVisualizerSystem : VisualizerSystem<BurnStateVisualsComponent>
|
||||
{
|
||||
protected override void OnAppearanceChange(EntityUid uid, BurnStateVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
if (!args.AppearanceData.TryGetValue(SmokingVisuals.Smoking, out var burnState))
|
||||
return;
|
||||
|
||||
var state = burnState switch
|
||||
{
|
||||
SmokableState.Lit => component.LitIcon,
|
||||
SmokableState.Burnt => component.BurntIcon,
|
||||
_ => component.UnlitIcon
|
||||
};
|
||||
|
||||
args.Sprite.LayerSetState(0, state);
|
||||
}
|
||||
}
|
||||
|
||||
13
Content.Client/Smoking/BurnStateVisualsComponent.cs
Normal file
13
Content.Client/Smoking/BurnStateVisualsComponent.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace Content.Client.Smoking;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class BurnStateVisualsComponent : Component
|
||||
{
|
||||
[DataField("burntIcon")]
|
||||
public string BurntIcon = "burnt-icon";
|
||||
[DataField("litIcon")]
|
||||
public string LitIcon = "lit-icon";
|
||||
[DataField("unlitIcon")]
|
||||
public string UnlitIcon = "icon";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.Speech.EntitySystems;
|
||||
|
||||
namespace Content.Client.Speech.EntitySystems;
|
||||
|
||||
public sealed class RatvarianLanguageSystem : SharedRatvarianLanguageSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -161,9 +161,15 @@ public sealed class ChatUIController : UIController
|
||||
_input.SetInputCommand(ContentKeyFunctions.FocusLocalChat,
|
||||
InputCmdHandler.FromDelegate(_ => FocusChannel(ChatSelectChannel.Local)));
|
||||
|
||||
_input.SetInputCommand(ContentKeyFunctions.FocusEmote,
|
||||
InputCmdHandler.FromDelegate(_ => FocusChannel(ChatSelectChannel.Emotes)));
|
||||
|
||||
_input.SetInputCommand(ContentKeyFunctions.FocusWhisperChat,
|
||||
InputCmdHandler.FromDelegate(_ => FocusChannel(ChatSelectChannel.Whisper)));
|
||||
|
||||
_input.SetInputCommand(ContentKeyFunctions.FocusLOOC,
|
||||
InputCmdHandler.FromDelegate(_ => FocusChannel(ChatSelectChannel.LOOC)));
|
||||
|
||||
_input.SetInputCommand(ContentKeyFunctions.FocusOOC,
|
||||
InputCmdHandler.FromDelegate(_ => FocusChannel(ChatSelectChannel.OOC)));
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
|
||||
|
||||
_window.OnClose += () =>
|
||||
{
|
||||
SendMessage(new GhostRoleWindowCloseMessage());
|
||||
SendMessage(new CloseEuiMessage());
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
|
||||
private void OnClose()
|
||||
{
|
||||
base.Closed();
|
||||
SendMessage(new MakeGhostRoleWindowClosedMessage());
|
||||
SendMessage(new CloseEuiMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,6 @@ public abstract partial class InteractionTest
|
||||
protected const string Cap4 = "QuadraticCapacitorStockPart";
|
||||
protected const string Manipulator1 = "MicroManipulatorStockPart";
|
||||
protected const string Manipulator4 = "FemtoManipulatorStockPart";
|
||||
protected const string Laser1 = "MicroLaserStockPart";
|
||||
protected const string Laser2 = "QuadUltraMicroLaserStockPart";
|
||||
protected const string Battery1 = "PowerCellSmall";
|
||||
protected const string Battery4 = "PowerCellHyper";
|
||||
}
|
||||
|
||||
@@ -107,11 +107,12 @@ public sealed class MaterialArbitrageTest
|
||||
var stackProto = protoManager.Index<StackPrototype>(materialStep.MaterialPrototypeId);
|
||||
var spawnProto = protoManager.Index<EntityPrototype>(stackProto.Spawn);
|
||||
|
||||
if (!spawnProto.Components.TryGetValue(materialName, out var matreg))
|
||||
if (!spawnProto.Components.ContainsKey(materialName) ||
|
||||
!spawnProto.Components.TryGetValue(compositionName, out var compositionReg))
|
||||
continue;
|
||||
|
||||
var mat = (MaterialComponent) matreg.Component;
|
||||
foreach (var (matId, amount) in mat.Materials)
|
||||
var mat = (PhysicalCompositionComponent) compositionReg.Component;
|
||||
foreach (var (matId, amount) in mat.MaterialComposition)
|
||||
{
|
||||
materials[matId] = materialStep.Amount * amount + materials.GetValueOrDefault(matId);
|
||||
}
|
||||
@@ -156,11 +157,13 @@ public sealed class MaterialArbitrageTest
|
||||
var spawnProto = protoManager.Index<EntityPrototype>(key);
|
||||
|
||||
// get the amount of each material included in the entity
|
||||
if (!spawnProto.Components.TryGetValue(materialName, out var matreg))
|
||||
continue;
|
||||
var mat = (MaterialComponent) matreg.Component;
|
||||
|
||||
foreach (var (matId, amount) in mat.Materials)
|
||||
if (!spawnProto.Components.ContainsKey(materialName) ||
|
||||
!spawnProto.Components.TryGetValue(compositionName, out var compositionReg))
|
||||
continue;
|
||||
|
||||
var mat = (PhysicalCompositionComponent) compositionReg.Component;
|
||||
foreach (var (matId, amount) in mat.MaterialComposition)
|
||||
{
|
||||
spawnedMats[matId] = value.Max * amount + spawnedMats.GetValueOrDefault(matId);
|
||||
}
|
||||
@@ -235,11 +238,12 @@ public sealed class MaterialArbitrageTest
|
||||
|
||||
var spawnProto = protoManager.Index<EntityPrototype>(spawnCompletion.Prototype);
|
||||
|
||||
if (!spawnProto.Components.TryGetValue(materialName, out var matreg))
|
||||
if (!spawnProto.Components.ContainsKey(materialName) ||
|
||||
!spawnProto.Components.TryGetValue(compositionName, out var compositionReg))
|
||||
continue;
|
||||
|
||||
var mat = (MaterialComponent) matreg.Component;
|
||||
foreach (var (matId, amount) in mat.Materials)
|
||||
var mat = (PhysicalCompositionComponent) compositionReg.Component;
|
||||
foreach (var (matId, amount) in mat.MaterialComposition)
|
||||
{
|
||||
materials[matId] = spawnCompletion.Amount * amount + materials.GetValueOrDefault(matId);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Content.MapRenderer.Painters
|
||||
{
|
||||
var path = _sTileDefinitionManager[tile.Tile.TypeId].Sprite.ToString();
|
||||
|
||||
if (path == null)
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return;
|
||||
|
||||
var x = (int) (tile.X + xOffset);
|
||||
@@ -72,7 +72,7 @@ namespace Content.MapRenderer.Painters
|
||||
{
|
||||
var path = definition.Sprite.ToString();
|
||||
|
||||
if (path == null)
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
continue;
|
||||
|
||||
images[path] = new List<Image>(definition.Variants);
|
||||
|
||||
@@ -460,6 +460,14 @@ namespace Content.Server.Database
|
||||
/// Ban is a datacenter range, connections usually imply usage of a VPN service.
|
||||
/// </summary>
|
||||
Datacenter = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Ban only matches the IP.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Intended use is for users with shared connections. This should not be used as an alternative to <see cref="Datacenter"/>.
|
||||
/// </remarks>
|
||||
IP = 1 << 1,
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ public sealed class BanExemptionGetCommand : LocalizedCommands
|
||||
{
|
||||
var mask = (ServerBanExemptFlags) (1 << i);
|
||||
if ((mask & flags) == 0)
|
||||
break;
|
||||
continue;
|
||||
|
||||
if (!first)
|
||||
joined.Append(", ");
|
||||
|
||||
@@ -92,6 +92,8 @@ public sealed class AdminLogsEui : BaseEui
|
||||
|
||||
public override async void HandleMessage(EuiMessageBase msg)
|
||||
{
|
||||
base.HandleMessage(msg);
|
||||
|
||||
if (!_adminManager.HasAdminFlag(Player, AdminFlags.Logs))
|
||||
{
|
||||
return;
|
||||
@@ -99,11 +101,6 @@ public sealed class AdminLogsEui : BaseEui
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case Close _:
|
||||
{
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
case LogsRequest request:
|
||||
{
|
||||
_sawmill.Info($"Admin log request from admin with id {Player.UserId.UserId} and name {Player.Name}");
|
||||
|
||||
@@ -58,11 +58,6 @@ public sealed class AdminNotesEui : BaseEui
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case Close _:
|
||||
{
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
case CreateNoteRequest {Message: var message}:
|
||||
{
|
||||
if (!_notesMan.CanCreate(Player))
|
||||
|
||||
@@ -32,11 +32,10 @@ namespace Content.Server.Administration.UI
|
||||
|
||||
public override void HandleMessage(EuiMessageBase msg)
|
||||
{
|
||||
base.HandleMessage(msg);
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case AdminAnnounceEuiMsg.Close:
|
||||
Close();
|
||||
break;
|
||||
case AdminAnnounceEuiMsg.DoAnnounce doAnnounce:
|
||||
if (!_adminManager.HasAdminFlag(Player, AdminFlags.Admin))
|
||||
{
|
||||
|
||||
@@ -38,15 +38,5 @@ namespace Content.Server.Administration.UI
|
||||
var solutions = _entityManager.GetComponentOrNull<SolutionContainerManagerComponent>(Target)?.Solutions;
|
||||
return new EditSolutionsEuiState(Target, solutions);
|
||||
}
|
||||
|
||||
public override void HandleMessage(EuiMessageBase msg)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case EditSolutionsEuiMsg.Close:
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,14 +86,10 @@ namespace Content.Server.Administration.UI
|
||||
|
||||
public override async void HandleMessage(EuiMessageBase msg)
|
||||
{
|
||||
base.HandleMessage(msg);
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case Close _:
|
||||
{
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
|
||||
case AddAdmin ca:
|
||||
{
|
||||
await HandleCreateAdmin(ca);
|
||||
|
||||
@@ -14,11 +14,7 @@ public sealed class SpawnExplosionEui : BaseEui
|
||||
{
|
||||
public override void HandleMessage(EuiMessageBase msg)
|
||||
{
|
||||
if (msg is SpawnExplosionEuiMsg.Close)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
base.HandleMessage(msg);
|
||||
|
||||
if (msg is not SpawnExplosionEuiMsg.PreviewRequest request)
|
||||
return;
|
||||
|
||||
@@ -144,8 +144,7 @@ public sealed partial class AnomalySystem
|
||||
msg.AddMarkup(stateLoc);
|
||||
msg.PushNewline();
|
||||
|
||||
var points = GetAnomalyPointValue(anomaly, anomalyComp) / 10 * 10; //round to tens place
|
||||
msg.AddMarkup(Loc.GetString("anomaly-scanner-point-output", ("point", points)));
|
||||
msg.AddMarkup(Loc.GetString("anomaly-scanner-point-output", ("point", GetAnomalyPointValue(anomaly, anomalyComp))));
|
||||
msg.PushNewline();
|
||||
msg.PushNewline();
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public sealed class AnomalyVesselComponent : Component
|
||||
/// The machine part that affects the point multiplier of the vessel
|
||||
/// </summary>
|
||||
[DataField("machinePartPointModifier", customTypeSerializer: typeof(PrototypeIdSerializer<MachinePartPrototype>))]
|
||||
public string MachinePartPointModifier = "ScanningModule";
|
||||
public string MachinePartPointModifier = "Capacitor";
|
||||
|
||||
/// <summary>
|
||||
/// A value used to scale the point multiplier
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Content.Server.Atmos.Piping.Binary.Components
|
||||
public float BaseMinTemp = 300 + Atmospherics.T0C;
|
||||
|
||||
[DataField("machinePartMinTemp", customTypeSerializer: typeof(PrototypeIdSerializer<MachinePartPrototype>))]
|
||||
public string MachinePartMinTemp = "Laser";
|
||||
public string MachinePartMinTemp = "Capacitor";
|
||||
|
||||
[DataField("partRatingMinTempMultiplier")]
|
||||
public float PartRatingMinTempMultiplier = 0.95f;
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace Content.Server.Atmos.Piping.Unary.Components
|
||||
/// The machine part that affects the temperature range.
|
||||
/// </summary>
|
||||
[DataField("machinePartTemperature", customTypeSerializer: typeof(PrototypeIdSerializer<MachinePartPrototype>))]
|
||||
public string MachinePartTemperature = "Laser";
|
||||
public string MachinePartTemperature = "Capacitor";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ namespace Content.Server.Bed.Components
|
||||
public float Multiplier = 10f;
|
||||
|
||||
[DataField("machinePartMetabolismModifier", customTypeSerializer: typeof(PrototypeIdSerializer<MachinePartPrototype>))]
|
||||
public string MachinePartMetabolismModifier = "Manipulator";
|
||||
public string MachinePartMetabolismModifier = "Capacitor";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,16 @@ using Content.Server.Mind.Components;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Events;
|
||||
using Content.Shared.Body.Organ;
|
||||
using Content.Shared.Body.Part;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
|
||||
namespace Content.Server.Body.Systems
|
||||
{
|
||||
public sealed class BrainSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -42,7 +46,14 @@ namespace Content.Server.Body.Systems
|
||||
Comp<GhostOnMoveComponent>(newEntity).MustBeDead = true;
|
||||
|
||||
// TODO: This is an awful solution.
|
||||
EnsureComp<InputMoverComponent>(newEntity);
|
||||
// Our greatest minds still can't figure out how to allow brains/heads to ghost without giving them the
|
||||
// ability to move first. I hate this with a passion.
|
||||
if (!HasComp<InputMoverComponent>(newEntity))
|
||||
{
|
||||
AddComp<InputMoverComponent>(newEntity);
|
||||
var move = EnsureComp<MovementSpeedModifierComponent>(newEntity);
|
||||
_movementSpeed.ChangeBaseSpeed(newEntity, 0, 0 , 0, move);
|
||||
}
|
||||
|
||||
oldMind.Mind?.TransferTo(newEntity);
|
||||
}
|
||||
|
||||
@@ -11,12 +11,6 @@ public sealed partial class BotanySystem
|
||||
if (!TryGetSeed(produce, out var seed))
|
||||
return;
|
||||
|
||||
if (TryComp(uid, out SpriteComponent? sprite))
|
||||
{
|
||||
sprite.LayerSetRSI(0, seed.PlantRsi);
|
||||
sprite.LayerSetState(0, seed.PlantIconState);
|
||||
}
|
||||
|
||||
var solutionContainer = _solutionContainerSystem.EnsureSolution(uid, produce.SolutionName);
|
||||
|
||||
solutionContainer.RemoveAllSolution();
|
||||
|
||||
@@ -99,13 +99,6 @@ public sealed partial class BotanySystem : EntitySystem
|
||||
var seedComp = EnsureComp<SeedComponent>(seed);
|
||||
seedComp.Seed = proto;
|
||||
|
||||
if (TryComp(seed, out SpriteComponent? sprite))
|
||||
{
|
||||
// TODO visualizer
|
||||
// SeedPrototype state will always be seed. Blame the spriter if that's not the case!
|
||||
sprite.LayerSetSprite(0, new SpriteSpecifier.Rsi(proto.PlantRsi, "seed"));
|
||||
}
|
||||
|
||||
var name = Loc.GetString(proto.Name);
|
||||
var noun = Loc.GetString(proto.Noun);
|
||||
var val = Loc.GetString("botany-seed-packet-name", ("seedName", name), ("seedNoun", noun));
|
||||
|
||||
@@ -121,10 +121,10 @@ public sealed class PricingSystem : EntitySystem
|
||||
return price;
|
||||
}
|
||||
|
||||
private double GetMaterialPrice(MaterialComponent component)
|
||||
private double GetMaterialPrice(PhysicalCompositionComponent component)
|
||||
{
|
||||
double price = 0;
|
||||
foreach (var (id, quantity) in component.Materials)
|
||||
foreach (var (id, quantity) in component.MaterialComposition)
|
||||
{
|
||||
price += _prototypeManager.Index<MaterialPrototype>(id).Price * quantity;
|
||||
}
|
||||
@@ -213,9 +213,10 @@ public sealed class PricingSystem : EntitySystem
|
||||
{
|
||||
double price = 0;
|
||||
|
||||
if (TryComp<MaterialComponent>(uid, out var material))
|
||||
if (HasComp<MaterialComponent>(uid) &&
|
||||
TryComp<PhysicalCompositionComponent>(uid, out var composition))
|
||||
{
|
||||
var matPrice = GetMaterialPrice(material);
|
||||
var matPrice = GetMaterialPrice(composition);
|
||||
if (TryComp<StackComponent>(uid, out var stack))
|
||||
matPrice *= stack.Count;
|
||||
|
||||
@@ -229,10 +230,11 @@ public sealed class PricingSystem : EntitySystem
|
||||
{
|
||||
double price = 0;
|
||||
|
||||
if (prototype.Components.TryGetValue(_factory.GetComponentName(typeof(MaterialComponent)), out var materials))
|
||||
if (prototype.Components.ContainsKey(_factory.GetComponentName(typeof(MaterialComponent))) &&
|
||||
prototype.Components.TryGetValue(_factory.GetComponentName(typeof(PhysicalCompositionComponent)), out var composition))
|
||||
{
|
||||
var materialsComp = (MaterialComponent) materials.Component;
|
||||
var matPrice = GetMaterialPrice(materialsComp);
|
||||
var compositionComp = (PhysicalCompositionComponent) composition.Component;
|
||||
var matPrice = GetMaterialPrice(compositionComp);
|
||||
|
||||
if (prototype.Components.TryGetValue(_factory.GetComponentName(typeof(StackComponent)), out var stackProto))
|
||||
{
|
||||
|
||||
@@ -120,7 +120,8 @@ namespace Content.Server.Chat.Managers
|
||||
|
||||
public void SendAdminAlert(EntityUid player, string message, MindComponent? mindComponent = null)
|
||||
{
|
||||
if(mindComponent == null && !_entityManager.TryGetComponent(player, out mindComponent))
|
||||
if((mindComponent == null && !_entityManager.TryGetComponent(player, out mindComponent))
|
||||
|| mindComponent.Mind == null)
|
||||
{
|
||||
SendAdminAlert(message);
|
||||
return;
|
||||
|
||||
@@ -12,7 +12,7 @@ public sealed class SolutionHeaterComponent : Component
|
||||
public float HeatMultiplier = 1;
|
||||
|
||||
[DataField("machinePartHeatPerSecond")]
|
||||
public string MachinePartHeatPerSecond = "Laser";
|
||||
public string MachinePartHeatPerSecond = "Capacitor";
|
||||
|
||||
[DataField("partRatingHeatMultiplier")]
|
||||
public float PartRatingHeatMultiplier = 1.5f;
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
[Dependency] private readonly StorageSystem _storageSystem = default!;
|
||||
[Dependency] private readonly LabelSystem _labelSystem = default!;
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
||||
|
||||
private const string PillPrototypeId = "Pill";
|
||||
|
||||
@@ -206,8 +207,9 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
_solutionContainerSystem.TryAddSolution(
|
||||
item, itemSolution, withdrawal.SplitSolution(message.Dosage));
|
||||
|
||||
if (TryComp<SpriteComponent>(item, out var spriteComp))
|
||||
spriteComp.LayerSetState(0, "pill" + (chemMaster.PillType + 1));
|
||||
var pill = EnsureComp<PillComponent>(item);
|
||||
pill.PillType = chemMaster.PillType;
|
||||
Dirty(pill);
|
||||
|
||||
if (user.HasValue)
|
||||
{
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Eye.Blinding;
|
||||
using Content.Shared.Eye.Blinding.Systems;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Server.Chemistry.ReagentEffects
|
||||
{
|
||||
/// <summary>
|
||||
/// Heal eye damage (or deal)
|
||||
/// Heal or apply eye damage
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class ChemHealEyeDamage : ReagentEffect
|
||||
{
|
||||
/// <summary>
|
||||
/// How much eye damage to remove.
|
||||
/// How much eye damage to add.
|
||||
/// </summary>
|
||||
[DataField("amount")]
|
||||
public int Amount = -1;
|
||||
|
||||
public override void Effect(ReagentEffectArgs args)
|
||||
{
|
||||
if (args.Scale != 1f)
|
||||
if (args.Scale != 1f) // huh?
|
||||
return;
|
||||
|
||||
args.EntityManager.EntitySysManager.GetEntitySystem<SharedBlindingSystem>().AdjustEyeDamage(args.SolutionEntity, Amount);
|
||||
args.EntityManager.EntitySysManager.GetEntitySystem<BlindableSystem>().AdjustEyeDamage(args.SolutionEntity, Amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ using Content.Server.Construction;
|
||||
using Content.Server.Materials;
|
||||
using Content.Server.Stack;
|
||||
using Content.Server.Jobs;
|
||||
using Content.Shared.Emag.Components;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Zombies;
|
||||
@@ -32,6 +33,11 @@ using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Emag.Systems;
|
||||
using Robust.Shared.Audio;
|
||||
using System.Runtime.InteropServices;
|
||||
using Content.Server.Popups;
|
||||
|
||||
namespace Content.Server.Cloning
|
||||
{
|
||||
@@ -52,8 +58,10 @@ namespace Content.Server.Cloning
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly PuddleSystem _puddleSystem = default!;
|
||||
[Dependency] private readonly ChatSystem _chatSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly MaterialStorageSystem _material = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
|
||||
public readonly Dictionary<Mind.Mind, EntityUid> ClonesWaitingForMind = new();
|
||||
public const float EasyModeCloningCost = 0.7f;
|
||||
@@ -70,6 +78,7 @@ namespace Content.Server.Cloning
|
||||
SubscribeLocalEvent<CloningPodComponent, PortDisconnectedEvent>(OnPortDisconnected);
|
||||
SubscribeLocalEvent<CloningPodComponent, AnchorStateChangedEvent>(OnAnchor);
|
||||
SubscribeLocalEvent<CloningPodComponent, ExaminedEvent>(OnExamined);
|
||||
SubscribeLocalEvent<CloningPodComponent, GotEmaggedEvent>(OnEmagged);
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, CloningPodComponent clonePod, ComponentInit args)
|
||||
@@ -280,6 +289,19 @@ namespace Content.Server.Cloning
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On emag, spawns a failed clone when cloning process fails which attacks nearby crew.
|
||||
/// </summary>
|
||||
private void OnEmagged(EntityUid uid, CloningPodComponent clonePod, ref GotEmaggedEvent args)
|
||||
{
|
||||
if (!this.IsPowered(uid, EntityManager))
|
||||
return;
|
||||
|
||||
_audio.PlayPvs(clonePod.SparkSound, uid);
|
||||
_popupSystem.PopupEntity(Loc.GetString("cloning-pod-component-upgrade-emag-requirement"), uid);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
public void Eject(EntityUid uid, CloningPodComponent? clonePod)
|
||||
{
|
||||
if (!Resolve(uid, ref clonePod))
|
||||
@@ -306,6 +328,12 @@ namespace Content.Server.Cloning
|
||||
|
||||
var tileMix = _atmosphereSystem.GetTileMixture(transform.GridUid, null, indices, true);
|
||||
|
||||
if (HasComp<EmaggedComponent>(uid))
|
||||
{
|
||||
_audio.PlayPvs(clonePod.ScreamSound, uid);
|
||||
Spawn(clonePod.MobSpawnId, transform.Coordinates);
|
||||
}
|
||||
|
||||
Solution bloodSolution = new();
|
||||
|
||||
int i = 0;
|
||||
@@ -318,7 +346,10 @@ namespace Content.Server.Cloning
|
||||
}
|
||||
_puddleSystem.TrySpillAt(uid, bloodSolution, out _);
|
||||
|
||||
if (!HasComp<EmaggedComponent>(uid))
|
||||
{
|
||||
_material.SpawnMultipleFromMaterial(_robustRandom.Next(1, (int) (clonePod.UsedBiomass / 2.5)), clonePod.RequiredMaterial, Transform(uid).Coordinates);
|
||||
}
|
||||
|
||||
clonePod.UsedBiomass = 0;
|
||||
RemCompDeferred<ActiveCloningPodComponent>(uid);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Shared.Cloning;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Content.Shared.Materials;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
@@ -49,7 +50,7 @@ namespace Content.Server.Cloning.Components
|
||||
/// The machine part that affects cloning speed
|
||||
/// </summary>
|
||||
[DataField("machinePartCloningSpeed", customTypeSerializer: typeof(PrototypeIdSerializer<MachinePartPrototype>))]
|
||||
public string MachinePartCloningSpeed = "ScanningModule";
|
||||
public string MachinePartCloningSpeed = "Manipulator";
|
||||
|
||||
/// <summary>
|
||||
/// The current amount of time it takes to clone a body
|
||||
@@ -57,6 +58,27 @@ namespace Content.Server.Cloning.Components
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float CloningTime = 30f;
|
||||
|
||||
/// <summary>
|
||||
/// The mob to spawn on emag
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("mobSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string MobSpawnId = "MobAbomination";
|
||||
|
||||
/// <summary>
|
||||
/// Emag sound effects.
|
||||
/// </summary>
|
||||
[DataField("sparkSound")]
|
||||
public SoundSpecifier SparkSound = new SoundCollectionSpecifier("sparks")
|
||||
{
|
||||
Params = AudioParams.Default.WithVolume(8),
|
||||
};
|
||||
|
||||
[DataField("screamSound")]
|
||||
public SoundSpecifier ScreamSound = new SoundCollectionSpecifier("ZombieScreams")
|
||||
{
|
||||
Params = AudioParams.Default.WithVolume(4),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The machine part that affects how much biomass is needed to clone a body.
|
||||
/// </summary>
|
||||
@@ -74,7 +96,7 @@ namespace Content.Server.Cloning.Components
|
||||
/// The machine part that decreases the amount of material needed for cloning
|
||||
/// </summary>
|
||||
[DataField("machinePartMaterialUse", customTypeSerializer: typeof(PrototypeIdSerializer<MachinePartPrototype>))]
|
||||
public string MachinePartMaterialUse = "Manipulator";
|
||||
public string MachinePartMaterialUse = "MatterBin";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public CloningPodStatus Status;
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
using Content.Shared.Construction;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Construction.Completions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed class SpriteChange : IGraphAction
|
||||
{
|
||||
[DataField("layer")] public int Layer { get; private set; } = 0;
|
||||
[DataField("specifier")] public SpriteSpecifier? SpriteSpecifier { get; private set; } = SpriteSpecifier.Invalid;
|
||||
|
||||
public void PerformAction(EntityUid uid, EntityUid? userUid, IEntityManager entityManager)
|
||||
{
|
||||
if (SpriteSpecifier == null || SpriteSpecifier == SpriteSpecifier.Invalid)
|
||||
return;
|
||||
|
||||
if (!entityManager.TryGetComponent(uid, out SpriteComponent? sprite))
|
||||
return;
|
||||
|
||||
// That layer doesn't exist, we do nothing.
|
||||
if (sprite.LayerCount <= Layer)
|
||||
return;
|
||||
|
||||
sprite.LayerSetSprite(Layer, SpriteSpecifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,18 +33,6 @@ public sealed class CrewManifestEui : BaseEui
|
||||
return new(name, entries);
|
||||
}
|
||||
|
||||
public override void HandleMessage(EuiMessageBase msg)
|
||||
{
|
||||
base.HandleMessage(msg);
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case CrewManifestEuiClosed:
|
||||
Closed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
base.Closed();
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace Content.Server.Database
|
||||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
|
||||
if (address != null)
|
||||
if (address != null && !exemptFlags.GetValueOrDefault(ServerBanExemptFlags.None).HasFlag(ServerBanExemptFlags.IP))
|
||||
{
|
||||
var newQ = db.PgDbContext.Ban
|
||||
.Include(p => p.Unban)
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace Content.Server.Database
|
||||
// So just pull down the whole list into memory.
|
||||
var bans = await GetAllBans(db.SqliteDbContext, includeUnbanned: false, exempt);
|
||||
|
||||
return bans.FirstOrDefault(b => BanMatches(b, address, userId, hwId)) is { } foundBan
|
||||
return bans.FirstOrDefault(b => BanMatches(b, address, userId, hwId, exempt)) is { } foundBan
|
||||
? ConvertBan(foundBan)
|
||||
: null;
|
||||
}
|
||||
@@ -92,7 +92,7 @@ namespace Content.Server.Database
|
||||
var queryBans = await GetAllBans(db.SqliteDbContext, includeUnbanned, exempt);
|
||||
|
||||
return queryBans
|
||||
.Where(b => BanMatches(b, address, userId, hwId))
|
||||
.Where(b => BanMatches(b, address, userId, hwId, exempt))
|
||||
.Select(ConvertBan)
|
||||
.ToList()!;
|
||||
}
|
||||
@@ -117,13 +117,14 @@ namespace Content.Server.Database
|
||||
return await query.ToListAsync();
|
||||
}
|
||||
|
||||
private static bool BanMatches(
|
||||
ServerBan ban,
|
||||
private static bool BanMatches(ServerBan ban,
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId)
|
||||
ImmutableArray<byte>? hwId,
|
||||
ServerBanExemptFlags? exemptFlags)
|
||||
{
|
||||
if (address != null && ban.Address is not null && IPAddressExt.IsInSubnet(address, ban.Address.Value))
|
||||
if (!exemptFlags.GetValueOrDefault(ServerBanExemptFlags.None).HasFlag(ServerBanExemptFlags.IP)
|
||||
&& address != null && ban.Address is not null && IPAddressExt.IsInSubnet(address, ban.Address.Value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
39
Content.Server/Disease/Effects/DiseaseHonk.cs
Normal file
39
Content.Server/Disease/Effects/DiseaseHonk.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Disease;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Disease
|
||||
{
|
||||
/// <summary>
|
||||
/// Makes the diseased honk.
|
||||
/// or neither.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class DiseaseHonk : DiseaseEffect
|
||||
{
|
||||
/// <summary>
|
||||
/// Message to play when honking.
|
||||
/// </summary>
|
||||
[DataField("honkMessage")]
|
||||
public string HonkMessage = "disease-honk";
|
||||
|
||||
/// <summary>
|
||||
/// Emote to play when honking.
|
||||
/// </summary>
|
||||
[DataField("emote", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EmotePrototype>))]
|
||||
public string EmoteId = String.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to spread the disease through the air.
|
||||
/// </summary>
|
||||
[DataField("airTransmit")]
|
||||
public bool AirTransmit = false;
|
||||
|
||||
public override void Effect(DiseaseEffectArgs args)
|
||||
{
|
||||
EntitySystem.Get<DiseaseSystem>().SneezeCough(args.DiseasedEntity, args.Disease, EmoteId, AirTransmit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Linq;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Shared.Dragon;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
@@ -30,13 +31,14 @@ public sealed partial class DragonSystem
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
|
||||
var spawnLocations = EntityQuery<MapGridComponent, TransformComponent>().ToList();
|
||||
|
||||
if (spawnLocations.Count == 0)
|
||||
if (!_station.Stations.Any())
|
||||
return;
|
||||
|
||||
var location = _random.Pick(spawnLocations);
|
||||
Spawn("MobDragon", location.Item2.MapPosition);
|
||||
var station = _random.Pick(_station.Stations);
|
||||
if (_station.GetLargestGrid(EntityManager.GetComponent<StationDataComponent>(station)) is not { } grid)
|
||||
return;
|
||||
|
||||
Spawn("MobDragon", Transform(grid).MapPosition);
|
||||
}
|
||||
|
||||
private void OnRiftRoundEnd(RoundEndTextAppendEvent args)
|
||||
|
||||
@@ -17,6 +17,7 @@ using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Random;
|
||||
using Content.Server.NPC.Systems;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Mobs;
|
||||
@@ -37,6 +38,7 @@ namespace Content.Server.Dragon
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
[Dependency] private readonly NPCSystem _npc = default!;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -49,6 +49,8 @@ namespace Content.Server.EUI
|
||||
/// </summary>
|
||||
public virtual void HandleMessage(EuiMessageBase msg)
|
||||
{
|
||||
if (msg is CloseEuiMessage)
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Explosion.Components;
|
||||
using Content.Server.NodeContainer.EntitySystems;
|
||||
using Content.Server.NPC.Pathfinding;
|
||||
using Content.Shared.Camera;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Explosion;
|
||||
@@ -38,6 +40,7 @@ public sealed partial class ExplosionSystem : EntitySystem
|
||||
[Dependency] private readonly PathfindingSystem _pathfindingSystem = default!;
|
||||
[Dependency] private readonly SharedCameraRecoilSystem _recoilSystem = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly IChatManager _chat = default!;
|
||||
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
|
||||
[Dependency] private readonly PVSOverrideSystem _pvsSys = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
@@ -227,11 +230,18 @@ public sealed partial class ExplosionSystem : EntitySystem
|
||||
return;
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
_adminLogger.Add(LogType.Explosion, LogImpact.High,
|
||||
$"{ToPrettyString(uid):entity} exploded ({typeId}) at {pos.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}");
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Content.Shared.Eye.Blinding;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Eye.Blinding.Components;
|
||||
using Content.Shared.Eye.Blinding.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
@@ -23,14 +25,14 @@ public sealed class ActivatableUIRequiresVisionSystem : EntitySystem
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (TryComp<BlindableComponent>(args.User, out var blindable) && blindable.Sources > 0)
|
||||
if (TryComp<BlindableComponent>(args.User, out var blindable) && blindable.IsBlind)
|
||||
{
|
||||
_popupSystem.PopupCursor(Loc.GetString("blindness-fail-attempt"), args.User, Shared.Popups.PopupType.MediumCaution);
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBlindnessChanged(EntityUid uid, BlindableComponent component, BlindnessChangedEvent args)
|
||||
private void OnBlindnessChanged(EntityUid uid, BlindableComponent component, ref BlindnessChangedEvent args)
|
||||
{
|
||||
if (!args.Blind)
|
||||
return;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using Content.Shared.Eye.Blinding.EyeProtection; // why aren't tools predicted 🙂
|
||||
using Content.Shared.Eye.Blinding;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Server.Tools;
|
||||
using Content.Shared.Eye.Blinding.Components;
|
||||
using Content.Shared.Eye.Blinding.Systems;
|
||||
using Content.Shared.Tools.Components;
|
||||
|
||||
namespace Content.Server.Eye.Blinding.EyeProtection
|
||||
@@ -12,15 +11,26 @@ namespace Content.Server.Eye.Blinding.EyeProtection
|
||||
public sealed class EyeProtectionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
|
||||
[Dependency] private readonly SharedBlindingSystem _blindingSystem = default!;
|
||||
[Dependency] private readonly BlindableSystem _blindingSystem = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<RequiresEyeProtectionComponent, ToolUseAttemptEvent>(OnUseAttempt);
|
||||
SubscribeLocalEvent<RequiresEyeProtectionComponent, WelderToggledEvent>(OnWelderToggled);
|
||||
|
||||
SubscribeLocalEvent<EyeProtectionComponent, GotEquippedEvent>(OnEquipped);
|
||||
SubscribeLocalEvent<EyeProtectionComponent, GotUnequippedEvent>(OnUnequipped);
|
||||
SubscribeLocalEvent<EyeProtectionComponent, GetEyeProtectionEvent>(OnGetProtection);
|
||||
SubscribeLocalEvent<EyeProtectionComponent, InventoryRelayedEvent<GetEyeProtectionEvent>>(OnGetRelayedProtection);
|
||||
}
|
||||
|
||||
private void OnGetRelayedProtection(EntityUid uid, EyeProtectionComponent component,
|
||||
InventoryRelayedEvent<GetEyeProtectionEvent> args)
|
||||
{
|
||||
OnGetProtection(uid, component, args.Args);
|
||||
}
|
||||
|
||||
private void OnGetProtection(EntityUid uid, EyeProtectionComponent component, GetEyeProtectionEvent args)
|
||||
{
|
||||
args.Protection += component.ProtectionTime;
|
||||
}
|
||||
|
||||
private void OnUseAttempt(EntityUid uid, RequiresEyeProtectionComponent component, ToolUseAttemptEvent args)
|
||||
@@ -28,51 +38,26 @@ namespace Content.Server.Eye.Blinding.EyeProtection
|
||||
if (!component.Toggled)
|
||||
return;
|
||||
|
||||
if (!HasComp<StatusEffectsComponent>(args.User) || !TryComp<BlindableComponent>(args.User, out var blindable))
|
||||
if (!TryComp<BlindableComponent>(args.User, out var blindable) || blindable.IsBlind)
|
||||
return;
|
||||
|
||||
if (blindable.Sources > 0)
|
||||
var ev = new GetEyeProtectionEvent();
|
||||
RaiseLocalEvent(args.User, ev);
|
||||
|
||||
var time = (float) (component.StatusEffectTime- ev.Protection).TotalSeconds;
|
||||
if (time <= 0)
|
||||
return;
|
||||
|
||||
var statusTime = (float) component.StatusEffectTime.TotalSeconds - blindable.BlindResistance;
|
||||
|
||||
if (statusTime <= 0)
|
||||
return;
|
||||
|
||||
var statusTimeSpan = TimeSpan.FromSeconds(statusTime * (blindable.EyeDamage + 1));
|
||||
// Add permanent eye damage if they had zero protection, also scale their temporary blindness by how much they already accumulated.
|
||||
if (_statusEffectsSystem.TryAddStatusEffect(args.User, SharedBlindingSystem.BlindingStatusEffect, statusTimeSpan, false, "TemporaryBlindness") && blindable.BlindResistance <= 0)
|
||||
_blindingSystem.AdjustEyeDamage(args.User, 1, blindable);
|
||||
// Add permanent eye damage if they had zero protection, also somewhat scale their temporary blindness by
|
||||
// how much damage they already accumulated.
|
||||
_blindingSystem.AdjustEyeDamage(args.User, 1, blindable);
|
||||
var statusTimeSpan = TimeSpan.FromSeconds(time * MathF.Sqrt(blindable.EyeDamage));
|
||||
_statusEffectsSystem.TryAddStatusEffect(args.User, TemporaryBlindnessSystem.BlindingStatusEffect,
|
||||
statusTimeSpan, false, TemporaryBlindnessSystem.BlindingStatusEffect);
|
||||
}
|
||||
private void OnWelderToggled(EntityUid uid, RequiresEyeProtectionComponent component, WelderToggledEvent args)
|
||||
{
|
||||
component.Toggled = args.WelderOn;
|
||||
}
|
||||
|
||||
private void OnEquipped(EntityUid uid, EyeProtectionComponent component, GotEquippedEvent args)
|
||||
{
|
||||
if (!TryComp<ClothingComponent>(uid, out var clothing) || clothing.Slots == SlotFlags.PREVENTEQUIP)
|
||||
return;
|
||||
|
||||
if (!clothing.Slots.HasFlag(args.SlotFlags))
|
||||
return;
|
||||
|
||||
component.IsActive = true;
|
||||
if (!TryComp<BlindableComponent>(args.Equipee, out var blindComp))
|
||||
return;
|
||||
|
||||
blindComp.BlindResistance += (float) component.ProtectionTime.TotalSeconds;
|
||||
}
|
||||
|
||||
private void OnUnequipped(EntityUid uid, EyeProtectionComponent component, GotUnequippedEvent args)
|
||||
{
|
||||
if (!component.IsActive)
|
||||
return;
|
||||
component.IsActive = false;
|
||||
if (!TryComp<BlindableComponent>(args.Equipee, out var blindComp))
|
||||
return;
|
||||
|
||||
blindComp.BlindResistance -= (float) component.ProtectionTime.TotalSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,13 +38,10 @@ public sealed class AdminFaxEui : BaseEui
|
||||
|
||||
public override void HandleMessage(EuiMessageBase msg)
|
||||
{
|
||||
base.HandleMessage(msg);
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case AdminFaxEuiMsg.Close:
|
||||
{
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
case AdminFaxEuiMsg.Follow followData:
|
||||
{
|
||||
if (Player.AttachedEntity == null ||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Flash.Components;
|
||||
using Content.Server.Light.EntitySystems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Stunnable;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Eye.Blinding;
|
||||
using Content.Shared.Eye.Blinding.Components;
|
||||
using Content.Shared.Flash;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction;
|
||||
@@ -28,9 +29,11 @@ namespace Content.Server.Flash
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly StunSystem _stunSystem = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
|
||||
[Dependency] private readonly AudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -50,7 +53,7 @@ namespace Content.Server.Flash
|
||||
{
|
||||
if (!args.IsHit ||
|
||||
!args.HitEntities.Any() ||
|
||||
!UseFlash(comp, args.User))
|
||||
!UseFlash(uid, comp, args.User))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -64,46 +67,37 @@ namespace Content.Server.Flash
|
||||
|
||||
private void OnFlashUseInHand(EntityUid uid, FlashComponent comp, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled || !UseFlash(comp, args.User))
|
||||
if (args.Handled || !UseFlash(uid, comp, args.User))
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
FlashArea(uid, args.User, comp.Range, comp.AoeFlashDuration, comp.SlowTo, true);
|
||||
}
|
||||
|
||||
private bool UseFlash(FlashComponent comp, EntityUid user)
|
||||
private bool UseFlash(EntityUid uid, FlashComponent comp, EntityUid user)
|
||||
{
|
||||
if (comp.HasUses)
|
||||
if (!comp.HasUses || comp.Flashing)
|
||||
return false;
|
||||
|
||||
comp.Uses--;
|
||||
_audio.PlayPvs(comp.Sound, uid);
|
||||
comp.Flashing = true;
|
||||
_appearance.SetData(uid, FlashVisuals.Flashing, true);
|
||||
|
||||
if (comp.Uses == 0)
|
||||
{
|
||||
// TODO flash visualizer
|
||||
if (!EntityManager.TryGetComponent<SpriteComponent?>(comp.Owner, out var sprite))
|
||||
return false;
|
||||
|
||||
if (--comp.Uses == 0)
|
||||
{
|
||||
sprite.LayerSetState(0, "burnt");
|
||||
|
||||
_tagSystem.AddTag(comp.Owner, "Trash");
|
||||
comp.Owner.PopupMessage(user, Loc.GetString("flash-component-becomes-empty"));
|
||||
}
|
||||
else if (!comp.Flashing)
|
||||
{
|
||||
int animLayer = sprite.AddLayerWithState("flashing");
|
||||
comp.Flashing = true;
|
||||
|
||||
comp.Owner.SpawnTimer(400, () =>
|
||||
{
|
||||
sprite.RemoveLayer(animLayer);
|
||||
comp.Flashing = false;
|
||||
});
|
||||
}
|
||||
|
||||
SoundSystem.Play(comp.Sound.GetSound(), Filter.Pvs(comp.Owner), comp.Owner, AudioParams.Default);
|
||||
|
||||
return true;
|
||||
_appearance.SetData(uid, FlashVisuals.Burnt, true);
|
||||
_tagSystem.AddTag(uid, "Trash");
|
||||
_popup.PopupEntity(Loc.GetString("flash-component-becomes-empty"), user);
|
||||
}
|
||||
|
||||
return false;
|
||||
uid.SpawnTimer(400, () =>
|
||||
{
|
||||
_appearance.SetData(uid, FlashVisuals.Flashing, false);
|
||||
comp.Flashing = false;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Flash(EntityUid target, EntityUid? user, EntityUid? used, float flashDuration, float slowTo, bool displayPopup = true, FlashableComponent? flashable = null)
|
||||
|
||||
@@ -105,7 +105,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
|
||||
continue;
|
||||
}
|
||||
|
||||
var remaining = neighborSolution.Volume - puddle.OverflowVolume;
|
||||
var remaining = puddle.OverflowVolume - neighborSolution.Volume;
|
||||
|
||||
if (remaining <= FixedPoint2.Zero)
|
||||
continue;
|
||||
@@ -129,20 +129,13 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
|
||||
}
|
||||
}
|
||||
|
||||
// Then we go to free tiles -> only overflow if we can go up to capacity at least.
|
||||
// Then we go to free tiles.
|
||||
// Need to go even if we have a little remainder to avoid solution sploshing around internally
|
||||
// for ages.
|
||||
if (args.NeighborFreeTiles.Count > 0 && args.Updates > 0)
|
||||
{
|
||||
// We'll only spill if we have the minimum threshold per tile.
|
||||
var spillCount = (int) Math.Floor(overflow.Volume.Float() / component.OverflowVolume.Float());
|
||||
|
||||
if (spillCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_random.Shuffle(args.NeighborFreeTiles);
|
||||
spillCount = Math.Min(args.NeighborFreeTiles.Count, spillCount);
|
||||
var spillAmount = overflow.Volume / spillCount;
|
||||
var spillAmount = overflow.Volume / args.NeighborFreeTiles.Count;
|
||||
|
||||
foreach (var neighbor in args.NeighborFreeTiles)
|
||||
{
|
||||
@@ -165,12 +158,10 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
|
||||
|
||||
foreach (var neighbor in args.Neighbors)
|
||||
{
|
||||
// Overflow to neighbours but not if they're already at the cap
|
||||
// This is to avoid diluting solutions too much.
|
||||
// Overflow to neighbours (unless it's pure water)
|
||||
if (!puddleQuery.TryGetComponent(neighbor, out var puddle) ||
|
||||
!_solutionContainerSystem.TryGetSolution(neighbor, puddle.SolutionName, out var neighborSolution) ||
|
||||
CanFullyEvaporate(neighborSolution) ||
|
||||
neighborSolution.Volume >= puddle.OverflowVolume)
|
||||
CanFullyEvaporate(neighborSolution))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -173,6 +173,11 @@ namespace Content.Server.GameTicking
|
||||
if (!_userDb.IsLoadComplete(player))
|
||||
return;
|
||||
|
||||
if (RunLevel != GameRunLevel.PreRoundLobby)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var status = ready ? PlayerGameStatus.ReadyToPlay : PlayerGameStatus.NotReadyToPlay;
|
||||
_playerGameStatuses[player.UserId] = ready ? PlayerGameStatus.ReadyToPlay : PlayerGameStatus.NotReadyToPlay;
|
||||
RaiseNetworkEvent(GetStatusMsg(player), player.ConnectedClient);
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Content.Server.GameTicking.Rules;
|
||||
|
||||
public abstract class GameRuleSystem<T> : EntitySystem where T : Component
|
||||
{
|
||||
[Dependency] protected GameTicker GameTicker = default!;
|
||||
[Dependency] protected readonly GameTicker GameTicker = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
||||
@@ -210,8 +210,9 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
|
||||
if (traitorRule == null)
|
||||
{
|
||||
//todo fuck me this shit is awful
|
||||
GameTicker.StartGameRule("traitor", out var ruleEntity);
|
||||
traitorRule = EntityManager.GetComponent<TraitorRuleComponent>(ruleEntity);
|
||||
//no i wont fuck you, erp is against rules
|
||||
GameTicker.StartGameRule("Traitor", out var ruleEntity);
|
||||
traitorRule = Comp<TraitorRuleComponent>(ruleEntity);
|
||||
}
|
||||
|
||||
var mind = traitor.Data.ContentData()?.Mind;
|
||||
|
||||
@@ -23,9 +23,6 @@ namespace Content.Server.Ghost.Roles.UI
|
||||
case GhostRoleFollowRequestMessage req:
|
||||
EntitySystem.Get<GhostRoleSystem>().Follow(Player, req.Identifier);
|
||||
break;
|
||||
case GhostRoleWindowCloseMessage _:
|
||||
Closed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,18 +18,6 @@ namespace Content.Server.Ghost.Roles.UI
|
||||
return new MakeGhostRoleEuiState(EntityUid);
|
||||
}
|
||||
|
||||
public override void HandleMessage(EuiMessageBase msg)
|
||||
{
|
||||
base.HandleMessage(msg);
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case MakeGhostRoleWindowClosedMessage _:
|
||||
Closed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
base.Closed();
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
using Content.Server.Cuffs;
|
||||
using Content.Server.Store.Components;
|
||||
using Content.Server.Store.Systems;
|
||||
using Content.Shared.Cuffs.Components;
|
||||
using Content.Shared.Implants;
|
||||
using Content.Shared.Implants.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Server.Implants;
|
||||
@@ -12,6 +16,8 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem
|
||||
{
|
||||
[Dependency] private readonly CuffableSystem _cuffable = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly StoreSystem _store = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -19,7 +25,10 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem
|
||||
|
||||
SubscribeLocalEvent<SubdermalImplantComponent, UseFreedomImplantEvent>(OnFreedomImplant);
|
||||
|
||||
SubscribeLocalEvent<StoreComponent, AfterInteractUsingEvent>(OnUplinkInteractUsing);
|
||||
|
||||
SubscribeLocalEvent<ImplantedComponent, MobStateChangedEvent>(RelayToImplantEvent);
|
||||
SubscribeLocalEvent<ImplantedComponent, AfterInteractUsingEvent>(RelayToImplantEvent);
|
||||
SubscribeLocalEvent<ImplantedComponent, SuicideEvent>(RelayToImplantEvent);
|
||||
}
|
||||
|
||||
@@ -32,6 +41,26 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnUplinkInteractUsing(EntityUid uid, StoreComponent store, AfterInteractUsingEvent args)
|
||||
{
|
||||
// can only insert into yourself to prevent uplink checking with renault
|
||||
if (args.Target != args.User)
|
||||
return;
|
||||
|
||||
if (!TryComp<CurrencyComponent>(args.Used, out var currency))
|
||||
return;
|
||||
|
||||
// same as store code, but message is only shown to yourself
|
||||
args.Handled = _store.TryAddCurrency(_store.GetCurrencyValue(args.Used, currency), uid, store);
|
||||
|
||||
if (!args.Handled)
|
||||
return;
|
||||
|
||||
var msg = Loc.GetString("store-currency-inserted-implant", ("used", args.Used));
|
||||
_popup.PopupEntity(msg, args.User, args.User);
|
||||
QueueDel(args.Used);
|
||||
}
|
||||
|
||||
#region Relays
|
||||
|
||||
//Relays from the implanted to the implant
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Content.Server.Kitchen.Components
|
||||
[DataField("cookTimeMultiplier"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public float CookTimeMultiplier = 1;
|
||||
[DataField("machinePartCookTimeMultiplier", customTypeSerializer: typeof(PrototypeIdSerializer<MachinePartPrototype>))]
|
||||
public string MachinePartCookTimeMultiplier = "Laser";
|
||||
public string MachinePartCookTimeMultiplier = "Capacitor";
|
||||
[DataField("cookTimeScalingConstant")]
|
||||
public float CookTimeScalingConstant = 0.5f;
|
||||
|
||||
|
||||
@@ -40,15 +40,20 @@ public sealed class MaterialStorageSystem : SharedMaterialStorageSystem
|
||||
}
|
||||
}
|
||||
|
||||
public override bool TryInsertMaterialEntity(EntityUid user, EntityUid toInsert, EntityUid receiver, MaterialStorageComponent? component = null)
|
||||
public override bool TryInsertMaterialEntity(EntityUid user,
|
||||
EntityUid toInsert,
|
||||
EntityUid receiver,
|
||||
MaterialStorageComponent? storage = null,
|
||||
MaterialComponent? material = null,
|
||||
PhysicalCompositionComponent? composition = null)
|
||||
{
|
||||
if (!Resolve(receiver, ref component))
|
||||
if (!Resolve(receiver, ref storage) || !Resolve(toInsert, ref material, ref composition, false))
|
||||
return false;
|
||||
if (TryComp<ApcPowerReceiverComponent>(receiver, out var power) && !power.Powered)
|
||||
return false;
|
||||
if (!base.TryInsertMaterialEntity(user, toInsert, receiver, component))
|
||||
if (!base.TryInsertMaterialEntity(user, toInsert, receiver, storage, material, composition))
|
||||
return false;
|
||||
_audio.PlayPvs(component.InsertingSound, receiver);
|
||||
_audio.PlayPvs(storage.InsertingSound, receiver);
|
||||
_popup.PopupEntity(Loc.GetString("machine-insert-item", ("user", user), ("machine", receiver),
|
||||
("item", toInsert)), receiver);
|
||||
QueueDel(toInsert);
|
||||
@@ -116,10 +121,10 @@ public sealed class MaterialStorageSystem : SharedMaterialStorageSystem
|
||||
return new List<EntityUid>();
|
||||
|
||||
var entProto = _prototypeManager.Index<EntityPrototype>(materialProto.StackEntity);
|
||||
if (!entProto.TryGetComponent<MaterialComponent>(out var material))
|
||||
if (!entProto.TryGetComponent<PhysicalCompositionComponent>(out var composition))
|
||||
return new List<EntityUid>();
|
||||
|
||||
var materialPerStack = material.Materials[materialProto.ID];
|
||||
var materialPerStack = composition.MaterialComposition[materialProto.ID];
|
||||
var amountToSpawn = amount / materialPerStack;
|
||||
overflowMaterial = amount - amountToSpawn * materialPerStack;
|
||||
return _stackSystem.SpawnMultiple(materialProto.StackEntity, amountToSpawn, coordinates);
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Content.Server.Medical.BiomassReclaimer
|
||||
/// Machine part whose rating modifies the yield per mass.
|
||||
/// </summary>
|
||||
[DataField("machinePartYieldAmount", customTypeSerializer: typeof(PrototypeIdSerializer<MachinePartPrototype>))]
|
||||
public string MachinePartYieldAmount = "Manipulator";
|
||||
public string MachinePartYieldAmount = "MatterBin";
|
||||
|
||||
/// <summary>
|
||||
/// How much the machine part quality affects the yield.
|
||||
@@ -89,7 +89,7 @@ namespace Content.Server.Medical.BiomassReclaimer
|
||||
/// The machine part that increses the processing speed.
|
||||
/// </summary>
|
||||
[DataField("machinePartProcessSpeed", customTypeSerializer: typeof(PrototypeIdSerializer<MachinePartPrototype>))]
|
||||
public string MachinePartProcessingSpeed = "Laser";
|
||||
public string MachinePartProcessingSpeed = "Manipulator";
|
||||
|
||||
/// <summary>
|
||||
/// How much the machine part quality affects the yield.
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Content.Server.Medical.Components
|
||||
public float CloningFailChanceMultiplier = 1f;
|
||||
|
||||
[DataField("machinePartCloningFailChance", customTypeSerializer: typeof(PrototypeIdSerializer<MachinePartPrototype>))]
|
||||
public string MachinePartCloningFailChance = "ScanningModule";
|
||||
public string MachinePartCloningFailChance = "Capacitor";
|
||||
|
||||
[DataField("partRatingCloningFailChanceMultiplier")]
|
||||
public float PartRatingFailMultiplier = 0.75f;
|
||||
|
||||
@@ -151,7 +151,10 @@ public sealed class HealingSystem : EntitySystem
|
||||
if (!TryComp<StackComponent>(uid, out var stack) || stack.Count < 1)
|
||||
return false;
|
||||
|
||||
if (!HasDamage(targetDamage, component))
|
||||
if (!TryComp<BloodstreamComponent>(target, out var bloodstream))
|
||||
return false;
|
||||
|
||||
if (!HasDamage(targetDamage, component) && !(bloodstream.BloodSolution.Volume < bloodstream.BloodSolution.MaxVolume && component.ModifyBloodLevel > 0))
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("medical-item-cant-use", ("item", uid)), uid);
|
||||
return false;
|
||||
|
||||
@@ -289,12 +289,12 @@ namespace Content.Server.Mind
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an objective to this mind.
|
||||
/// Removes an objective from this mind.
|
||||
/// </summary>
|
||||
/// <returns>Returns true if the removal succeeded.</returns>
|
||||
public bool TryRemoveObjective(int index)
|
||||
{
|
||||
if (_objectives.Count >= index) return false;
|
||||
if (index < 0 || index >= _objectives.Count) return false;
|
||||
|
||||
var objective = _objectives[index];
|
||||
|
||||
|
||||
@@ -223,11 +223,10 @@ public sealed class HTNSystem : EntitySystem
|
||||
{
|
||||
text.AppendLine($"BTR: {string.Join(", ", comp.Plan.BranchTraversalRecord)}");
|
||||
text.AppendLine($"tasks:");
|
||||
|
||||
foreach (var task in comp.Plan.Tasks)
|
||||
{
|
||||
text.AppendLine($"- {task.ID}");
|
||||
}
|
||||
var root = _prototypeManager.Index<HTNCompoundTask>(comp.RootTask);
|
||||
var btr = new List<int>();
|
||||
var level = -1;
|
||||
AppendDebugText(root, text, comp.Plan.BranchTraversalRecord, btr, ref level);
|
||||
}
|
||||
|
||||
RaiseNetworkEvent(new HTNMessage()
|
||||
@@ -247,6 +246,49 @@ public sealed class HTNSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void AppendDebugText(HTNTask task, StringBuilder text, List<int> planBtr, List<int> btr, ref int level)
|
||||
{
|
||||
// If it's the selected BTR then highlight.
|
||||
for (var i = 0; i < btr.Count; i++)
|
||||
{
|
||||
text.Append('-');
|
||||
}
|
||||
|
||||
text.Append(' ');
|
||||
|
||||
if (task is HTNPrimitiveTask primitive)
|
||||
{
|
||||
text.AppendLine(primitive.ID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (task is HTNCompoundTask compound)
|
||||
{
|
||||
level++;
|
||||
text.AppendLine(compound.ID);
|
||||
var branches = _compoundBranches[compound];
|
||||
|
||||
for (var i = 0; i < branches.Length; i++)
|
||||
{
|
||||
var branch = branches[i];
|
||||
btr.Add(i);
|
||||
text.AppendLine($" branch {string.Join(" ", btr)}:");
|
||||
|
||||
foreach (var sub in branch)
|
||||
{
|
||||
AppendDebugText(sub, text, planBtr, btr, ref level);
|
||||
}
|
||||
|
||||
btr.RemoveAt(btr.Count - 1);
|
||||
}
|
||||
|
||||
level--;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void Update(HTNComponent component, float frameTime)
|
||||
{
|
||||
// If we're not planning then countdown to next one.
|
||||
|
||||
@@ -37,7 +37,7 @@ public sealed class FatExtractorComponent : Component
|
||||
/// Which machine part affects the nutrition rate
|
||||
/// </summary>
|
||||
[DataField("machinePartNutritionRate", customTypeSerializer: typeof(PrototypeIdSerializer<MachinePartPrototype>))]
|
||||
public string MachinePartNutritionRate = "Laser";
|
||||
public string MachinePartNutritionRate = "Manipulator";
|
||||
|
||||
/// <summary>
|
||||
/// The increase in rate per each rating above 1.
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Content.Server.Objectives.Commands
|
||||
}
|
||||
for (var i = 0; i < objectives.Count; i++)
|
||||
{
|
||||
shell.WriteLine($"- [{i + 1}] {objectives[i].Conditions[0].Title}");
|
||||
shell.WriteLine($"- [{i}] {objectives[i].Conditions[0].Title}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -310,22 +310,14 @@ namespace Content.Server.Physics.Controllers
|
||||
angularInput /= count;
|
||||
brakeInput /= count;
|
||||
|
||||
/*
|
||||
* So essentially:
|
||||
* 1. We do the same calcs for braking as we do for linear thrust so it's similar to a player pressing it
|
||||
* but we also need to handle when they get close to 0 hence why it sets velocity directly.
|
||||
*
|
||||
* 2. We do a similar calculation to mob movement where the closer you are to your speed cap the slower you accelerate
|
||||
*
|
||||
* TODO: Could combine braking linear input and thrust more but my brain was just not working debugging
|
||||
* TODO: Need to have variable speed caps based on thruster count or whatever
|
||||
*/
|
||||
|
||||
// Handle shuttle movement
|
||||
if (brakeInput > 0f)
|
||||
{
|
||||
if (body.LinearVelocity.Length > 0f)
|
||||
{
|
||||
// Minimum brake velocity for a direction to show its thrust appearance.
|
||||
var appearanceThreshold = 0.1f;
|
||||
|
||||
// Get velocity relative to the shuttle so we know which thrusters to fire
|
||||
var shuttleVelocity = (-shuttleNorthAngle).RotateVec(body.LinearVelocity);
|
||||
var force = Vector2.Zero;
|
||||
@@ -333,7 +325,9 @@ namespace Content.Server.Physics.Controllers
|
||||
if (shuttleVelocity.X < 0f)
|
||||
{
|
||||
_thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.West);
|
||||
_thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.East);
|
||||
|
||||
if (shuttleVelocity.X < -appearanceThreshold)
|
||||
_thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.East);
|
||||
|
||||
var index = (int) Math.Log2((int) DirectionFlag.East);
|
||||
force.X += shuttle.LinearThrust[index];
|
||||
@@ -341,7 +335,9 @@ namespace Content.Server.Physics.Controllers
|
||||
else if (shuttleVelocity.X > 0f)
|
||||
{
|
||||
_thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.East);
|
||||
_thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.West);
|
||||
|
||||
if (shuttleVelocity.X > appearanceThreshold)
|
||||
_thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.West);
|
||||
|
||||
var index = (int) Math.Log2((int) DirectionFlag.West);
|
||||
force.X -= shuttle.LinearThrust[index];
|
||||
@@ -350,7 +346,9 @@ namespace Content.Server.Physics.Controllers
|
||||
if (shuttleVelocity.Y < 0f)
|
||||
{
|
||||
_thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.South);
|
||||
_thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.North);
|
||||
|
||||
if (shuttleVelocity.Y < -appearanceThreshold)
|
||||
_thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.North);
|
||||
|
||||
var index = (int) Math.Log2((int) DirectionFlag.North);
|
||||
force.Y += shuttle.LinearThrust[index];
|
||||
@@ -358,47 +356,24 @@ namespace Content.Server.Physics.Controllers
|
||||
else if (shuttleVelocity.Y > 0f)
|
||||
{
|
||||
_thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.North);
|
||||
_thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.South);
|
||||
|
||||
if (shuttleVelocity.Y > appearanceThreshold)
|
||||
_thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.South);
|
||||
|
||||
var index = (int) Math.Log2((int) DirectionFlag.South);
|
||||
force.Y -= shuttle.LinearThrust[index];
|
||||
}
|
||||
|
||||
var impulse = force * brakeInput;
|
||||
var wishDir = impulse.Normalized;
|
||||
// TODO: Adjust max possible speed based on total thrust in particular direction.
|
||||
var wishSpeed = 20f;
|
||||
var impulse = force * brakeInput * ShuttleComponent.BrakeCoefficient;
|
||||
var maxImpulse = shuttleVelocity * body.Mass;
|
||||
|
||||
var currentSpeed = Vector2.Dot(shuttleVelocity, wishDir);
|
||||
var addSpeed = wishSpeed - currentSpeed;
|
||||
|
||||
if (addSpeed > 0f)
|
||||
if ((impulse * frameTime).LengthSquared > maxImpulse.LengthSquared)
|
||||
{
|
||||
var accelSpeed = impulse.Length * frameTime;
|
||||
accelSpeed = MathF.Min(accelSpeed, addSpeed);
|
||||
impulse = impulse.Normalized * accelSpeed * body.InvMass;
|
||||
|
||||
// Cap inputs
|
||||
if (shuttleVelocity.X < 0f)
|
||||
{
|
||||
impulse.X = MathF.Min(impulse.X, -shuttleVelocity.X);
|
||||
}
|
||||
else if (shuttleVelocity.X > 0f)
|
||||
{
|
||||
impulse.X = MathF.Max(impulse.X, -shuttleVelocity.X);
|
||||
}
|
||||
|
||||
if (shuttleVelocity.Y < 0f)
|
||||
{
|
||||
impulse.Y = MathF.Min(impulse.Y, -shuttleVelocity.Y);
|
||||
}
|
||||
else if (shuttleVelocity.Y > 0f)
|
||||
{
|
||||
impulse.Y = MathF.Max(impulse.Y, -shuttleVelocity.Y);
|
||||
}
|
||||
|
||||
PhysicsSystem.SetLinearVelocity(shuttle.Owner, body.LinearVelocity + shuttleNorthAngle.RotateVec(impulse), body: body);
|
||||
impulse = -maxImpulse;
|
||||
}
|
||||
|
||||
PhysicsSystem.ApplyForce(shuttle.Owner, shuttleNorthAngle.RotateVec(impulse), body: body);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -407,32 +382,20 @@ namespace Content.Server.Physics.Controllers
|
||||
|
||||
if (body.AngularVelocity != 0f)
|
||||
{
|
||||
var impulse = shuttle.AngularThrust * brakeInput * (body.AngularVelocity > 0f ? -1f : 1f);
|
||||
var wishSpeed = MathF.PI;
|
||||
var impulse = shuttle.AngularThrust * brakeInput * (body.AngularVelocity > 0f ? -1f : 1f) * ShuttleComponent.BrakeCoefficient;
|
||||
var maxImpulse = body.AngularVelocity * body.Inertia;
|
||||
|
||||
if (impulse < 0f)
|
||||
wishSpeed *= -1f;
|
||||
|
||||
var currentSpeed = body.AngularVelocity;
|
||||
var addSpeed = wishSpeed - currentSpeed;
|
||||
|
||||
if (!addSpeed.Equals(0f))
|
||||
if (Math.Abs(impulse * frameTime) > Math.Abs(maxImpulse))
|
||||
{
|
||||
var accelSpeed = impulse * body.InvI * frameTime;
|
||||
|
||||
if (accelSpeed < 0f)
|
||||
accelSpeed = MathF.Max(accelSpeed, addSpeed);
|
||||
else
|
||||
accelSpeed = MathF.Min(accelSpeed, addSpeed);
|
||||
|
||||
if (body.AngularVelocity < 0f && body.AngularVelocity + accelSpeed > 0f)
|
||||
accelSpeed = -body.AngularVelocity;
|
||||
else if (body.AngularVelocity > 0f && body.AngularVelocity + accelSpeed < 0f)
|
||||
accelSpeed = -body.AngularVelocity;
|
||||
|
||||
PhysicsSystem.SetAngularVelocity(shuttle.Owner, body.AngularVelocity + accelSpeed, body: body);
|
||||
_thruster.SetAngularThrust(shuttle, true);
|
||||
impulse = -maxImpulse;
|
||||
}
|
||||
|
||||
PhysicsSystem.ApplyTorque(shuttle.Owner, impulse, body: body);
|
||||
_thruster.SetAngularThrust(shuttle, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_thruster.SetAngularThrust(shuttle, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,11 +405,6 @@ namespace Content.Server.Physics.Controllers
|
||||
|
||||
if (brakeInput.Equals(0f))
|
||||
_thruster.DisableLinearThrusters(shuttle);
|
||||
|
||||
if (body.LinearVelocity.Length < 0.08)
|
||||
{
|
||||
PhysicsSystem.SetLinearVelocity(shuttle.Owner, Vector2.Zero, body: body);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -454,8 +412,7 @@ namespace Content.Server.Physics.Controllers
|
||||
var angle = linearInput.ToWorldAngle();
|
||||
var linearDir = angle.GetDir();
|
||||
var dockFlag = linearDir.AsFlag();
|
||||
|
||||
var totalForce = new Vector2();
|
||||
var totalForce = Vector2.Zero;
|
||||
|
||||
// Won't just do cardinal directions.
|
||||
foreach (DirectionFlag dir in Enum.GetValues(typeof(DirectionFlag)))
|
||||
@@ -478,83 +435,62 @@ namespace Content.Server.Physics.Controllers
|
||||
continue;
|
||||
}
|
||||
|
||||
var force = Vector2.Zero;
|
||||
var index = (int) Math.Log2((int) dir);
|
||||
var thrust = shuttle.LinearThrust[index];
|
||||
|
||||
switch (dir)
|
||||
{
|
||||
case DirectionFlag.North:
|
||||
totalForce.Y += thrust;
|
||||
force.Y += thrust;
|
||||
break;
|
||||
case DirectionFlag.South:
|
||||
totalForce.Y -= thrust;
|
||||
force.Y -= thrust;
|
||||
break;
|
||||
case DirectionFlag.East:
|
||||
totalForce.X += thrust;
|
||||
force.X += thrust;
|
||||
break;
|
||||
case DirectionFlag.West:
|
||||
totalForce.X -= thrust;
|
||||
force.X -= thrust;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
_thruster.EnableLinearThrustDirection(shuttle, dir);
|
||||
var impulse = force * linearInput.Length;
|
||||
totalForce += impulse;
|
||||
}
|
||||
|
||||
// We don't want to touch damping if no inputs are given
|
||||
// so we'll just add an artifical drag to the velocity input.
|
||||
var shuttleVelocity = (-shuttleNorthAngle).RotateVec(body.LinearVelocity);
|
||||
totalForce = shuttleNorthAngle.RotateVec(totalForce);
|
||||
|
||||
var wishDir = totalForce.Normalized;
|
||||
// TODO: Adjust max possible speed based on total thrust in particular direction.
|
||||
var wishSpeed = 20f;
|
||||
|
||||
var currentSpeed = Vector2.Dot(shuttleVelocity, wishDir);
|
||||
var addSpeed = wishSpeed - currentSpeed;
|
||||
|
||||
if (addSpeed > 0f)
|
||||
if ((body.LinearVelocity + totalForce / body.Mass * frameTime).Length <= ShuttleComponent.MaxLinearVelocity)
|
||||
{
|
||||
var accelSpeed = totalForce.Length * frameTime;
|
||||
accelSpeed = MathF.Min(accelSpeed, addSpeed);
|
||||
PhysicsSystem.ApplyLinearImpulse(shuttle.Owner, shuttleNorthAngle.RotateVec(totalForce.Normalized * accelSpeed), body: body);
|
||||
PhysicsSystem.ApplyForce(shuttle.Owner, totalForce, body: body);
|
||||
}
|
||||
}
|
||||
|
||||
if (MathHelper.CloseTo(angularInput, 0f))
|
||||
{
|
||||
_thruster.SetAngularThrust(shuttle, false);
|
||||
PhysicsSystem.SetSleepingAllowed(shuttle.Owner, body, true);
|
||||
|
||||
if (Math.Abs(body.AngularVelocity) < 0.01f)
|
||||
{
|
||||
PhysicsSystem.SetAngularVelocity(shuttle.Owner, 0f, body: body);
|
||||
}
|
||||
if (brakeInput <= 0f)
|
||||
_thruster.SetAngularThrust(shuttle, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
PhysicsSystem.SetSleepingAllowed(shuttle.Owner, body, false);
|
||||
var impulse = shuttle.AngularThrust * -angularInput;
|
||||
var wishSpeed = MathF.PI;
|
||||
var tickChange = impulse * frameTime * body.InvI;
|
||||
|
||||
if (impulse < 0f)
|
||||
wishSpeed *= -1f;
|
||||
|
||||
var currentSpeed = body.AngularVelocity;
|
||||
var addSpeed = wishSpeed - currentSpeed;
|
||||
|
||||
if (!addSpeed.Equals(0f))
|
||||
// If the rotation brings it above speedcap then noop.
|
||||
if (Math.Sign(body.AngularVelocity) != Math.Sign(tickChange) ||
|
||||
Math.Abs(body.AngularVelocity + tickChange) <= ShuttleComponent.MaxAngularVelocity)
|
||||
{
|
||||
var accelSpeed = impulse * body.InvI * frameTime;
|
||||
|
||||
if (accelSpeed < 0f)
|
||||
accelSpeed = MathF.Max(accelSpeed, addSpeed);
|
||||
else
|
||||
accelSpeed = MathF.Min(accelSpeed, addSpeed);
|
||||
|
||||
PhysicsSystem.SetAngularVelocity(shuttle.Owner, body.AngularVelocity + accelSpeed, body: body);
|
||||
_thruster.SetAngularThrust(shuttle, true);
|
||||
PhysicsSystem.ApplyTorque(shuttle.Owner, impulse, body: body);
|
||||
}
|
||||
|
||||
_thruster.SetAngularThrust(shuttle, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,18 @@ namespace Content.Server.Shuttles.Components
|
||||
[ViewVariables]
|
||||
public bool Enabled = true;
|
||||
|
||||
[ViewVariables]
|
||||
public Vector2[] CenterOfThrust = new Vector2[4];
|
||||
|
||||
/// <summary>
|
||||
/// Thrust gets multiplied by this value if it's for braking.
|
||||
/// </summary>
|
||||
public const float BrakeCoefficient = 1.5f;
|
||||
|
||||
public const float MaxLinearVelocity = 10f;
|
||||
|
||||
public const float MaxAngularVelocity = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// The cached thrust available for each cardinal direction
|
||||
/// </summary>
|
||||
|
||||
@@ -46,10 +46,10 @@ namespace Content.Server.Shuttles.Components
|
||||
|
||||
// Need to serialize this because RefreshParts isn't called on Init and this will break post-mapinit maps!
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("thrust")]
|
||||
public float Thrust;
|
||||
public float Thrust = 100f;
|
||||
|
||||
[DataField("baseThrust"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseThrust = 750f;
|
||||
public float BaseThrust = 100f;
|
||||
|
||||
[DataField("thrusterType")]
|
||||
public ThrusterType Type = ThrusterType.Linear;
|
||||
@@ -83,7 +83,7 @@ namespace Content.Server.Shuttles.Components
|
||||
public TimeSpan NextFire;
|
||||
|
||||
[DataField("machinePartThrust", customTypeSerializer: typeof(PrototypeIdSerializer<MachinePartPrototype>))]
|
||||
public string MachinePartThrust = "Laser";
|
||||
public string MachinePartThrust = "Capacitor";
|
||||
|
||||
[DataField("partRatingThrustMultiplier")]
|
||||
public float PartRatingThrustMultiplier = 1.5f;
|
||||
|
||||
@@ -13,6 +13,7 @@ using Content.Shared.Shuttles.Components;
|
||||
using Content.Shared.Temperature;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Events;
|
||||
@@ -295,6 +296,38 @@ public sealed class ThrusterSystem : EntitySystem
|
||||
}
|
||||
|
||||
_ambient.SetAmbience(uid, true);
|
||||
RefreshCenter(uid, shuttleComponent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the center of thrust for movement calculations.
|
||||
/// </summary>
|
||||
private void RefreshCenter(EntityUid uid, ShuttleComponent shuttle)
|
||||
{
|
||||
// TODO: Only refresh relevant directions.
|
||||
var center = Vector2.Zero;
|
||||
var thrustQuery = GetEntityQuery<ThrusterComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var dir in new[]
|
||||
{ Direction.South, Direction.East, Direction.North, Direction.West })
|
||||
{
|
||||
var index = (int) dir / 2;
|
||||
var pop = shuttle.LinearThrusters[index];
|
||||
var totalThrust = 0f;
|
||||
|
||||
foreach (var ent in pop)
|
||||
{
|
||||
if (!thrustQuery.TryGetComponent(ent, out var thruster) || !xformQuery.TryGetComponent(ent, out var xform))
|
||||
continue;
|
||||
|
||||
center += xform.LocalPosition * thruster.Thrust;
|
||||
totalThrust += thruster.Thrust;
|
||||
}
|
||||
|
||||
center /= pop.Count * totalThrust;
|
||||
shuttle.CenterOfThrust[index] = center;
|
||||
}
|
||||
}
|
||||
|
||||
public void DisableThruster(EntityUid uid, ThrusterComponent component, TransformComponent? xform = null, Angle? angle = null)
|
||||
@@ -358,6 +391,7 @@ public sealed class ThrusterSystem : EntitySystem
|
||||
}
|
||||
|
||||
component.Colliding.Clear();
|
||||
RefreshCenter(uid, shuttleComponent);
|
||||
}
|
||||
|
||||
public bool CanEnable(EntityUid uid, ThrusterComponent component)
|
||||
|
||||
@@ -179,11 +179,8 @@ namespace Content.Server.Singularity.EntitySystems
|
||||
|
||||
private void OnRefreshParts(EntityUid uid, EmitterComponent component, RefreshPartsEvent args)
|
||||
{
|
||||
var powerUseRating = args.PartRatings[component.MachinePartPowerUse];
|
||||
var fireRateRating = args.PartRatings[component.MachinePartFireRate];
|
||||
|
||||
component.PowerUseActive = (int) (component.BasePowerUseActive * MathF.Pow(component.PowerUseMultiplier, powerUseRating - 1));
|
||||
|
||||
component.FireInterval = component.BaseFireInterval * MathF.Pow(component.FireRateMultiplier, fireRateRating - 1);
|
||||
component.FireBurstDelayMin = component.BaseFireBurstDelayMin * MathF.Pow(component.FireRateMultiplier, fireRateRating - 1);
|
||||
component.FireBurstDelayMax = component.BaseFireBurstDelayMax * MathF.Pow(component.FireRateMultiplier, fireRateRating - 1);
|
||||
@@ -192,8 +189,6 @@ namespace Content.Server.Singularity.EntitySystems
|
||||
private void OnUpgradeExamine(EntityUid uid, EmitterComponent component, UpgradeExamineEvent args)
|
||||
{
|
||||
args.AddPercentageUpgrade("emitter-component-upgrade-fire-rate", (float) (component.BaseFireInterval.TotalSeconds / component.FireInterval.TotalSeconds));
|
||||
// TODO: Remove this and use UpgradePowerDrawComponent instead.
|
||||
args.AddPercentageUpgrade("upgrade-power-draw", component.PowerUseActive / (float) component.BasePowerUseActive);
|
||||
}
|
||||
|
||||
public void SwitchOff(EmitterComponent component)
|
||||
|
||||
115
Content.Server/Speech/EntitySystems/RatvarianLanguageSystem.cs
Normal file
115
Content.Server/Speech/EntitySystems/RatvarianLanguageSystem.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Content.Shared.Speech.Components;
|
||||
using Content.Shared.Speech.EntitySystems;
|
||||
using Content.Shared.StatusEffect;
|
||||
|
||||
namespace Content.Server.Speech.EntitySystems;
|
||||
|
||||
public sealed class RatvarianLanguageSystem : SharedRatvarianLanguageSystem
|
||||
{
|
||||
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
|
||||
private const string RatvarianKey = "RatvarianLanguage";
|
||||
|
||||
// This is the word of Ratvar and those who speak it shall abide by His rules:
|
||||
/*
|
||||
* Any time the word "of" occurs, it's linked to the previous word by a hyphen: "I am-of Ratvar"
|
||||
* Any time "th", followed by any two letters occurs, you add a grave (`) between those two letters: "Thi`s"
|
||||
* In the same vein, any time "ti" followed by one letter occurs, you add a grave (`) between "i" and the letter: "Ti`me"
|
||||
* Wherever "te" or "et" appear and there is another letter next to the "e", add a hyphen between "e" and the letter: "M-etal/Greate-r"
|
||||
* Where "gua" appears, add a hyphen between "gu" and "a": "Gu-ard"
|
||||
* Where the word "and" appears it's linked to all surrounding words by hyphens: "Sword-and-shield"
|
||||
* Where the word "to" appears, it's linked to the following word by a hyphen: "to-use"
|
||||
* Where the word "my" appears, it's linked to the following word by a hyphen: "my-light"
|
||||
* Any Ratvarian proper noun is not translated: Ratvar, Nezbere, Sevtug, Nzcrentr and Inath-neq
|
||||
* This only applies if they're being used as a proper noun: armorer/Nezbere
|
||||
*/
|
||||
|
||||
private static Regex THPattern = new Regex(@"th\w\B", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static Regex ETPattern = new Regex(@"\Bet", RegexOptions.Compiled);
|
||||
private static Regex TEPattern = new Regex(@"te\B",RegexOptions.Compiled);
|
||||
private static Regex OFPattern = new Regex(@"(\s)(of)");
|
||||
private static Regex TIPattern = new Regex(@"ti\B", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static Regex GUAPattern = new Regex(@"(gu)(a)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static Regex ANDPattern = new Regex(@"\b(\s)(and)(\s)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static Regex TOMYPattern = new Regex(@"(to|my)\s", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static Regex ProperNouns = new Regex(@"(ratvar)|(nezbere)|(sevtuq)|(nzcrentr)|(inath-neq)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
// Activate before other modifications so translation works properly
|
||||
SubscribeLocalEvent<RatvarianLanguageComponent, AccentGetEvent>(OnAccent, before: new[] {typeof(SharedSlurredSystem), typeof(SharedStutteringSystem)});
|
||||
}
|
||||
|
||||
public override void DoRatvarian(EntityUid uid, TimeSpan time, bool refresh, StatusEffectsComponent? status = null)
|
||||
{
|
||||
if (!Resolve(uid, ref status, false))
|
||||
return;
|
||||
|
||||
_statusEffects.TryAddStatusEffect<RatvarianLanguageComponent>(uid, RatvarianKey, time, refresh, status);
|
||||
}
|
||||
|
||||
private void OnAccent(EntityUid uid, RatvarianLanguageComponent component, AccentGetEvent args)
|
||||
{
|
||||
args.Message = Translate(args.Message);
|
||||
}
|
||||
|
||||
private string Translate(string message)
|
||||
{
|
||||
var ruleTranslation = message;
|
||||
var finalMessage = new StringBuilder();
|
||||
var newWord = new StringBuilder();
|
||||
|
||||
ruleTranslation = THPattern.Replace(ruleTranslation, "$&`");
|
||||
ruleTranslation = TEPattern.Replace(ruleTranslation, "$&-");
|
||||
ruleTranslation = ETPattern.Replace(ruleTranslation, "-$&");
|
||||
ruleTranslation = OFPattern.Replace(ruleTranslation, "-$2");
|
||||
ruleTranslation = TIPattern.Replace(ruleTranslation, "$&`");
|
||||
ruleTranslation = GUAPattern.Replace(ruleTranslation, "$1-$2");
|
||||
ruleTranslation = ANDPattern.Replace(ruleTranslation, "-$2-");
|
||||
ruleTranslation = TOMYPattern.Replace(ruleTranslation, "$1-");
|
||||
|
||||
var temp = ruleTranslation.Split(' ');
|
||||
|
||||
foreach (var word in temp)
|
||||
{
|
||||
newWord.Clear();
|
||||
|
||||
if (ProperNouns.IsMatch(word))
|
||||
newWord.Append(word);
|
||||
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < word.Length; i++)
|
||||
{
|
||||
var letter = word[i];
|
||||
|
||||
if (letter >= 97 && letter <= 122)
|
||||
{
|
||||
var letterRot = letter + 13;
|
||||
|
||||
if (letterRot > 122)
|
||||
letterRot -= 26;
|
||||
|
||||
newWord.Append((char) letterRot);
|
||||
}
|
||||
else if (letter >= 65 && letter <= 90)
|
||||
{
|
||||
var letterRot = letter + 13;
|
||||
|
||||
if (letterRot > 90)
|
||||
letterRot -= 26;
|
||||
|
||||
newWord.Append((char) letterRot);
|
||||
}
|
||||
else
|
||||
{
|
||||
newWord.Append(word[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
finalMessage.Append(newWord + " ");
|
||||
}
|
||||
return finalMessage.ToString().Trim();
|
||||
}
|
||||
}
|
||||
@@ -5,21 +5,36 @@ namespace Content.Server.StationEvents.Components;
|
||||
[RegisterComponent, Access(typeof(MeteorSwarmRule))]
|
||||
public sealed class MeteorSwarmRuleComponent : Component
|
||||
{
|
||||
public float _cooldown;
|
||||
[DataField("cooldown")]
|
||||
public float Cooldown;
|
||||
|
||||
/// <summary>
|
||||
/// We'll send a specific amount of waves of meteors towards the station per ending rather than using a timer.
|
||||
/// </summary>
|
||||
public int _waveCounter;
|
||||
[DataField("waveCounter")]
|
||||
public int WaveCounter;
|
||||
|
||||
[DataField("minimumWaves")]
|
||||
public int MinimumWaves = 3;
|
||||
|
||||
[DataField("maximumWaves")]
|
||||
public int MaximumWaves = 8;
|
||||
|
||||
[DataField("minimumCooldown")]
|
||||
public float MinimumCooldown = 10f;
|
||||
|
||||
[DataField("maximumCooldown")]
|
||||
public float MaximumCooldown = 60f;
|
||||
|
||||
[DataField("meteorsPerWave")]
|
||||
public int MeteorsPerWave = 5;
|
||||
|
||||
[DataField("meteorVelocity")]
|
||||
public float MeteorVelocity = 10f;
|
||||
|
||||
[DataField("maxAngularVelocity")]
|
||||
public float MaxAngularVelocity = 0.25f;
|
||||
|
||||
[DataField("minAngularVelocity")]
|
||||
public float MinAngularVelocity = -0.25f;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public sealed class StationEventComponent : Component
|
||||
/// How long the event lasts.
|
||||
/// </summary>
|
||||
[DataField("duration")]
|
||||
public TimeSpan Duration = TimeSpan.FromSeconds(1);
|
||||
public TimeSpan? Duration = TimeSpan.FromSeconds(1);
|
||||
|
||||
/// <summary>
|
||||
/// The max amount of time the event lasts.
|
||||
@@ -85,5 +85,5 @@ public sealed class StationEventComponent : Component
|
||||
/// When the station event starts.
|
||||
/// </summary>
|
||||
[DataField("endTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan EndTime;
|
||||
public TimeSpan? EndTime;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user