mirror of
https://github.com/corvax-team/ss14-wl.git
synced 2026-02-15 03:31:38 +01:00
Merge remote-tracking branch 'refs/remotes/upstream/master' into upstream-sync
# Conflicts: # .github/workflows/publish.yml # .github/workflows/test-packaging.yml # Content.Server/Administration/Systems/AdminSystem.cs # Resources/Prototypes/Entities/Clothing/Hands/gloves.yml # Resources/Textures/Clothing/Head/Hardhats/blue.rsi/icon.png # Resources/Textures/Clothing/Head/Hardhats/blue.rsi/light-icon.png # Resources/Textures/Clothing/Head/Hardhats/orange.rsi/icon.png # Resources/Textures/Clothing/Head/Hardhats/orange.rsi/light-icon.png # Resources/Textures/Clothing/Head/Hardhats/red.rsi/icon.png # Resources/Textures/Clothing/Head/Hardhats/red.rsi/light-icon.png # Resources/Textures/Clothing/Head/Hardhats/white.rsi/icon.png # Resources/Textures/Clothing/Head/Hardhats/white.rsi/light-icon.png # Resources/Textures/Clothing/Head/Hardhats/yellow.rsi/icon.png # Resources/Textures/Clothing/Head/Hardhats/yellow.rsi/light-icon.png # Tools/gen_build_info.py
This commit is contained in:
46
.github/workflows/publish.yml
vendored
46
.github/workflows/publish.yml
vendored
@@ -60,33 +60,22 @@ jobs:
|
||||
- name: Package client
|
||||
run: dotnet run --project Content.Packaging client --no-wipe-release
|
||||
|
||||
- name: Update Build Info
|
||||
- name: Upload build artifact
|
||||
id: artifact-upload-step
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build
|
||||
path: release/*.zip
|
||||
compression-level: 0
|
||||
retention-days: 0
|
||||
|
||||
- name: Publish version
|
||||
run: Tools/publish_github_artifact.py
|
||||
env:
|
||||
FORK_ID: ${{ vars.FORK_ID }}
|
||||
run: Tools/gen_build_info.py
|
||||
|
||||
- name: Shuffle files around
|
||||
run: |
|
||||
mkdir "release/${{ github.sha }}"
|
||||
mv release/*.zip "release/${{ github.sha }}"
|
||||
|
||||
- name: Upload files to mothership
|
||||
uses: burnett01/rsync-deployments@5.2
|
||||
with:
|
||||
switches: -avzr --ignore-existing
|
||||
path: "release/${{ github.sha }}"
|
||||
remote_path: ${{ secrets.BUILDS_PATH }}
|
||||
remote_host: ${{ secrets.BUILDS_HOST }}
|
||||
remote_user: ${{ secrets.BUILDS_USERNAME }}
|
||||
remote_key: ${{ secrets.BUILDS_SSH_KEY }}
|
||||
|
||||
- name: Update manifest JSON
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.BUILDS_HOST }}
|
||||
username: ${{ secrets.BUILDS_USERNAME }}
|
||||
key: ${{ secrets.BUILDS_SSH_KEY }}
|
||||
script: node ~/scripts/push_to_manifest.js -fork ${{ vars.FORK_ID }} -id ${{ github.sha }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
|
||||
ARTIFACT_ID: ${{ steps.artifact-upload-step.outputs.artifact-id }}
|
||||
GITHUB_REPOSITORY: ${{ vars.GITHUB_REPOSITORY }}
|
||||
|
||||
# - name: Publish changelog (Discord)
|
||||
# run: Tools/actions_changelogs_since_last_run.py
|
||||
@@ -99,3 +88,8 @@ jobs:
|
||||
# run: Tools/actions_changelog_rss.py
|
||||
# env:
|
||||
# CHANGELOG_RSS_KEY: ${{ secrets.CHANGELOG_RSS_KEY }}
|
||||
|
||||
- uses: geekyeggo/delete-artifact@v5
|
||||
if: always()
|
||||
with:
|
||||
name: build
|
||||
|
||||
10
.github/workflows/test-packaging.yml
vendored
10
.github/workflows/test-packaging.yml
vendored
@@ -78,13 +78,3 @@ jobs:
|
||||
|
||||
- name: Package client
|
||||
run: dotnet run --project Content.Packaging client --no-wipe-release
|
||||
|
||||
- name: Update Build Info
|
||||
env:
|
||||
FORK_ID: ${{ vars.FORK_ID }}
|
||||
run: Tools/gen_build_info.py
|
||||
|
||||
- name: Shuffle files around
|
||||
run: |
|
||||
mkdir "release/${{ github.sha }}"
|
||||
mv release/*.zip "release/${{ github.sha }}"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.Stylesheets;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -77,9 +77,12 @@ namespace Content.Client.Actions.UI
|
||||
MaxWidth = TooltipTextMaxWidth,
|
||||
StyleClasses = {StyleNano.StyleClassTooltipActionRequirements}
|
||||
};
|
||||
requiresLabel.SetMessage(FormattedMessage.FromMarkup("[color=#635c5c]" +
|
||||
requires +
|
||||
"[/color]"));
|
||||
|
||||
if (!FormattedMessage.TryFromMarkup("[color=#635c5c]" + requires + "[/color]", out var markup))
|
||||
return;
|
||||
|
||||
requiresLabel.SetMessage(markup);
|
||||
|
||||
vbox.AddChild(requiresLabel);
|
||||
}
|
||||
}
|
||||
@@ -97,8 +100,11 @@ namespace Content.Client.Actions.UI
|
||||
if (timeLeft > TimeSpan.Zero)
|
||||
{
|
||||
var duration = Cooldown.Value.End - Cooldown.Value.Start;
|
||||
_cooldownLabel.SetMessage(FormattedMessage.FromMarkup(
|
||||
$"[color=#a10505]{(int) duration.TotalSeconds} sec cooldown ({(int) timeLeft.TotalSeconds + 1} sec remaining)[/color]"));
|
||||
|
||||
if (!FormattedMessage.TryFromMarkup($"[color=#a10505]{(int) duration.TotalSeconds} sec cooldown ({(int) timeLeft.TotalSeconds + 1} sec remaining)[/color]", out var markup))
|
||||
return;
|
||||
|
||||
_cooldownLabel.SetMessage(markup);
|
||||
_cooldownLabel.Visible = true;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
xmlns:tabs="clr-namespace:Content.Client.Administration.UI.Tabs"
|
||||
xmlns:playerTab="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
|
||||
xmlns:objectsTab="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab"
|
||||
xmlns:panic="clr-namespace:Content.Client.Administration.UI.Tabs.PanicBunkerTab">
|
||||
xmlns:panic="clr-namespace:Content.Client.Administration.UI.Tabs.PanicBunkerTab"
|
||||
xmlns:baby="clr-namespace:Content.Client.Administration.UI.Tabs.BabyJailTab">
|
||||
<TabContainer Name="MasterTabContainer">
|
||||
<adminTab:AdminTab />
|
||||
<adminbusTab:AdminbusTab />
|
||||
@@ -14,6 +15,7 @@
|
||||
<tabs:RoundTab />
|
||||
<tabs:ServerTab />
|
||||
<panic:PanicBunkerTab Name="PanicBunkerControl" Access="Public" />
|
||||
<baby:BabyJailTab Name="BabyJailControl" Access="Public" />
|
||||
<playerTab:PlayerTab Name="PlayerTabControl" Access="Public" />
|
||||
<objectsTab:ObjectsTab Name="ObjectsTabControl" Access="Public" />
|
||||
</TabContainer>
|
||||
|
||||
@@ -15,14 +15,18 @@ public sealed partial class AdminMenuWindow : DefaultWindow
|
||||
MinSize = new Vector2(650, 250);
|
||||
Title = Loc.GetString("admin-menu-title");
|
||||
RobustXamlLoader.Load(this);
|
||||
MasterTabContainer.SetTabTitle(0, Loc.GetString("admin-menu-admin-tab"));
|
||||
MasterTabContainer.SetTabTitle(1, Loc.GetString("admin-menu-adminbus-tab"));
|
||||
MasterTabContainer.SetTabTitle(2, Loc.GetString("admin-menu-atmos-tab"));
|
||||
MasterTabContainer.SetTabTitle(3, Loc.GetString("admin-menu-round-tab"));
|
||||
MasterTabContainer.SetTabTitle(4, Loc.GetString("admin-menu-server-tab"));
|
||||
MasterTabContainer.SetTabTitle(5, Loc.GetString("admin-menu-panic-bunker-tab"));
|
||||
MasterTabContainer.SetTabTitle(6, Loc.GetString("admin-menu-players-tab"));
|
||||
MasterTabContainer.SetTabTitle(7, Loc.GetString("admin-menu-objects-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Admin, Loc.GetString("admin-menu-admin-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Adminbus, Loc.GetString("admin-menu-adminbus-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Atmos, Loc.GetString("admin-menu-atmos-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Round, Loc.GetString("admin-menu-round-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Server, Loc.GetString("admin-menu-server-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.PanicBunker, Loc.GetString("admin-menu-panic-bunker-tab"));
|
||||
/*
|
||||
* TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.BabyJail, Loc.GetString("admin-menu-baby-jail-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Players, Loc.GetString("admin-menu-players-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Objects, Loc.GetString("admin-menu-objects-tab"));
|
||||
MasterTabContainer.OnTabChanged += OnTabChanged;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<Popup xmlns="https://spacestation14.io"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
<PanelContainer StyleClasses="BackgroundDark">
|
||||
<PanelContainer>
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BorderThickness="1" BorderColor="#18181B"/>
|
||||
<gfx:StyleBoxFlat BorderThickness="2" BorderColor="#18181B" BackgroundColor="#25252a"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Vertical" Margin="4 4 4 4">
|
||||
<Label Name="PlayerNameLabel"/>
|
||||
<Label Name="IdLabel"/>
|
||||
<Label Name="TypeLabel"/>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<controls:BabyJailStatusWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.Administration.UI.Tabs.BabyJailTab"
|
||||
Title="{Loc admin-ui-baby-jail-window-title}">
|
||||
<RichTextLabel Name="MessageLabel" Access="Public" />
|
||||
</controls:BabyJailStatusWindow>
|
||||
@@ -0,0 +1,21 @@
|
||||
using Content.Client.Message;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.BabyJailTab;
|
||||
|
||||
/*
|
||||
* TODO: Remove me once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BabyJailStatusWindow : FancyWindow
|
||||
{
|
||||
public BabyJailStatusWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
MessageLabel.SetMarkup(Loc.GetString("admin-ui-baby-jail-is-enabled"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<controls:BabyJailTab
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.Administration.UI.Tabs.BabyJailTab"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Margin="4">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<cc:CommandButton Name="EnabledButton" Command="babyjail" ToggleMode="True"
|
||||
Text="{Loc admin-ui-baby-jail-disabled}"
|
||||
ToolTip="{Loc admin-ui-baby-jail-tooltip}" />
|
||||
<cc:CommandButton Name="ShowReasonButton" Command="babyjail_show_reason"
|
||||
ToggleMode="True" Text="{Loc admin-ui-baby-jail-show-reason}"
|
||||
ToolTip="{Loc admin-ui-baby-jail-show-reason-tooltip}" />
|
||||
<BoxContainer Orientation="Vertical" Margin="0 10 0 0">
|
||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<Label Text="{Loc admin-ui-baby-jail-max-account-age}" MinWidth="175" />
|
||||
<LineEdit Name="MaxAccountAge" MinWidth="50" Margin="0 0 5 0" />
|
||||
<Label Text="{Loc generic-minutes}" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<Label Text="{Loc admin-ui-baby-jail-max-overall-minutes}" MinWidth="175" />
|
||||
<LineEdit Name="MaxOverallMinutes" MinWidth="50" Margin="0 0 5 0" />
|
||||
<Label Text="{Loc generic-minutes}" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:BabyJailTab>
|
||||
@@ -0,0 +1,75 @@
|
||||
using Content.Shared.Administration.Events;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
/*
|
||||
* TODO: Remove me once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.BabyJailTab;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BabyJailTab : Control
|
||||
{
|
||||
[Dependency] private readonly IConsoleHost _console = default!;
|
||||
|
||||
private string _maxAccountAge;
|
||||
private string _maxOverallMinutes;
|
||||
|
||||
public BabyJailTab()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
MaxAccountAge.OnTextEntered += args => SendMaxAccountAge(args.Text);
|
||||
MaxAccountAge.OnFocusExit += args => SendMaxAccountAge(args.Text);
|
||||
_maxAccountAge = MaxAccountAge.Text;
|
||||
|
||||
MaxOverallMinutes.OnTextEntered += args => SendMaxOverallMinutes(args.Text);
|
||||
MaxOverallMinutes.OnFocusExit += args => SendMaxOverallMinutes(args.Text);
|
||||
_maxOverallMinutes = MaxOverallMinutes.Text;
|
||||
}
|
||||
|
||||
private void SendMaxAccountAge(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text) ||
|
||||
text == _maxAccountAge ||
|
||||
!int.TryParse(text, out var minutes))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_console.ExecuteCommand($"babyjail_max_account_age {minutes}");
|
||||
}
|
||||
|
||||
private void SendMaxOverallMinutes(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text) ||
|
||||
text == _maxOverallMinutes ||
|
||||
!int.TryParse(text, out var minutes))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_console.ExecuteCommand($"babyjail_max_overall_minutes {minutes}");
|
||||
}
|
||||
|
||||
public void UpdateStatus(BabyJailStatus status)
|
||||
{
|
||||
EnabledButton.Pressed = status.Enabled;
|
||||
EnabledButton.Text = Loc.GetString(status.Enabled
|
||||
? "admin-ui-baby-jail-enabled"
|
||||
: "admin-ui-baby-jail-disabled"
|
||||
);
|
||||
EnabledButton.ModulateSelfOverride = status.Enabled ? Color.Red : null;
|
||||
ShowReasonButton.Pressed = status.ShowReason;
|
||||
|
||||
MaxAccountAge.Text = status.MaxAccountAgeMinutes.ToString();
|
||||
_maxAccountAge = MaxAccountAge.Text;
|
||||
|
||||
MaxOverallMinutes.Text = status.MaxOverallMinutes.ToString();
|
||||
_maxOverallMinutes = MaxOverallMinutes.Text;
|
||||
}
|
||||
}
|
||||
26
Content.Client/Clock/ClockSystem.cs
Normal file
26
Content.Client/Clock/ClockSystem.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Content.Shared.Clock;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Clock;
|
||||
|
||||
public sealed class ClockSystem : SharedClockSystem
|
||||
{
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<ClockComponent, SpriteComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp, out var sprite))
|
||||
{
|
||||
if (!sprite.LayerMapTryGet(ClockVisualLayers.HourHand, out var hourLayer) ||
|
||||
!sprite.LayerMapTryGet(ClockVisualLayers.MinuteHand, out var minuteLayer))
|
||||
continue;
|
||||
|
||||
var time = GetClockTime((uid, comp));
|
||||
var hourState = $"{comp.HoursBase}{time.Hours % 12}";
|
||||
var minuteState = $"{comp.MinutesBase}{time.Minutes / 5}";
|
||||
sprite.LayerSetState(hourLayer, hourState);
|
||||
sprite.LayerSetState(minuteLayer, minuteState);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,11 +48,11 @@ namespace Content.Client.Construction
|
||||
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.OpenCraftingMenu,
|
||||
new PointerInputCmdHandler(HandleOpenCraftingMenu, outsidePrediction:true))
|
||||
new PointerInputCmdHandler(HandleOpenCraftingMenu, outsidePrediction: true))
|
||||
.Bind(EngineKeyFunctions.Use,
|
||||
new PointerInputCmdHandler(HandleUse, outsidePrediction: true))
|
||||
.Bind(ContentKeyFunctions.EditorFlipObject,
|
||||
new PointerInputCmdHandler(HandleFlip, outsidePrediction:true))
|
||||
new PointerInputCmdHandler(HandleFlip, outsidePrediction: true))
|
||||
.Register<ConstructionSystem>();
|
||||
|
||||
SubscribeLocalEvent<ConstructionGhostComponent, ExaminedEvent>(HandleConstructionGhostExamined);
|
||||
@@ -196,7 +196,7 @@ namespace Content.Client.Construction
|
||||
if (GhostPresent(loc))
|
||||
return false;
|
||||
|
||||
var predicate = GetPredicate(prototype.CanBuildInImpassable, loc.ToMap(EntityManager, _transformSystem));
|
||||
var predicate = GetPredicate(prototype.CanBuildInImpassable, _transformSystem.ToMapCoordinates(loc));
|
||||
if (!_examineSystem.InRangeUnOccluded(user, loc, 20f, predicate: predicate))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace Content.Client.ContextMenu.UI
|
||||
if (_combatMode.IsInCombatMode(args.Session?.AttachedEntity))
|
||||
return false;
|
||||
|
||||
var coords = args.Coordinates.ToMap(_entityManager, _xform);
|
||||
var coords = _xform.ToMapCoordinates(args.Coordinates);
|
||||
|
||||
if (_verbSystem.TryGetEntityMenuEntities(coords, out var entities))
|
||||
OpenRootMenu(entities);
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
using Content.Shared.Extinguisher;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Extinguisher;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class FireExtinguisherComponent : SharedFireExtinguisherComponent;
|
||||
@@ -1,27 +1,22 @@
|
||||
using Content.Shared.Flash;
|
||||
using Content.Shared.Flash.Components;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Client.Viewport;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
||||
namespace Content.Client.Flash
|
||||
{
|
||||
public sealed class FlashOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IClyde _displayManager = default!;
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private readonly StatusEffectsSystem _statusSys;
|
||||
private readonly StatusEffectsSystem _statusSys;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
private readonly ShaderInstance _shader;
|
||||
@@ -56,20 +51,6 @@ namespace Content.Client.Flash
|
||||
PercentComplete = timeDone / lastsFor;
|
||||
}
|
||||
|
||||
public void ReceiveFlash()
|
||||
{
|
||||
if (_stateManager.CurrentState is IMainViewportState state)
|
||||
{
|
||||
// take a screenshot
|
||||
// note that the callback takes a while and ScreenshotTexture will be null the first few Draws
|
||||
state.Viewport.Viewport.Screenshot(image =>
|
||||
{
|
||||
var rgba32Image = image.CloneAs<Rgba32>(SixLabors.ImageSharp.Configuration.Default);
|
||||
ScreenshotTexture = _displayManager.LoadTextureFromImage(rgba32Image);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool BeforeDraw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (!_entityManager.TryGetComponent(_playerManager.LocalEntity, out EyeComponent? eyeComp))
|
||||
@@ -82,6 +63,11 @@ namespace Content.Client.Flash
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (RequestScreenTexture && ScreenTexture != null)
|
||||
{
|
||||
ScreenshotTexture = ScreenTexture;
|
||||
RequestScreenTexture = false; // we only need the first frame, so we can stop the request now for performance reasons
|
||||
}
|
||||
if (ScreenshotTexture == null)
|
||||
return;
|
||||
|
||||
@@ -96,7 +82,6 @@ namespace Content.Client.Flash
|
||||
{
|
||||
base.DisposeBehavior();
|
||||
ScreenshotTexture = null;
|
||||
PercentComplete = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ public sealed class FlashSystem : SharedFlashSystem
|
||||
SubscribeLocalEvent<FlashedComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<FlashedComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<FlashedComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
|
||||
SubscribeLocalEvent<FlashedComponent, StatusEffectAddedEvent>(OnStatusAdded);
|
||||
|
||||
_overlay = new();
|
||||
}
|
||||
@@ -34,8 +33,8 @@ public sealed class FlashSystem : SharedFlashSystem
|
||||
|
||||
private void OnPlayerDetached(EntityUid uid, FlashedComponent component, LocalPlayerDetachedEvent args)
|
||||
{
|
||||
_overlay.PercentComplete = 1.0f;
|
||||
_overlay.ScreenshotTexture = null;
|
||||
_overlay.RequestScreenTexture = false;
|
||||
_overlayMan.RemoveOverlay(_overlay);
|
||||
}
|
||||
|
||||
@@ -43,6 +42,7 @@ public sealed class FlashSystem : SharedFlashSystem
|
||||
{
|
||||
if (_player.LocalEntity == uid)
|
||||
{
|
||||
_overlay.RequestScreenTexture = true;
|
||||
_overlayMan.AddOverlay(_overlay);
|
||||
}
|
||||
}
|
||||
@@ -51,17 +51,9 @@ public sealed class FlashSystem : SharedFlashSystem
|
||||
{
|
||||
if (_player.LocalEntity == uid)
|
||||
{
|
||||
_overlay.PercentComplete = 1.0f;
|
||||
_overlay.ScreenshotTexture = null;
|
||||
_overlay.RequestScreenTexture = false;
|
||||
_overlayMan.RemoveOverlay(_overlay);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStatusAdded(EntityUid uid, FlashedComponent component, StatusEffectAddedEvent args)
|
||||
{
|
||||
if (_player.LocalEntity == uid && args.Key == FlashedKey)
|
||||
{
|
||||
_overlay.ReceiveFlash();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,8 @@ namespace Content.Client.Gameplay
|
||||
|
||||
public IEnumerable<EntityUid> GetClickableEntities(EntityCoordinates coordinates)
|
||||
{
|
||||
return GetClickableEntities(coordinates.ToMap(_entityManager, _entitySystemManager.GetEntitySystem<SharedTransformSystem>()));
|
||||
var transformSystem = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
|
||||
return GetClickableEntities(transformSystem.ToMapCoordinates(coordinates));
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetClickableEntities(MapCoordinates coordinates)
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
using Content.Shared.Item.ItemToggle;
|
||||
|
||||
namespace Content.Shared.Item;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed class ItemToggleSystem : SharedItemToggleSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -25,6 +25,9 @@ namespace Content.Client.MainMenu
|
||||
[Dependency] private readonly IGameController _controllerProxy = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
private MainMenuControl _mainMenuControl = default!;
|
||||
private bool _isConnecting;
|
||||
@@ -35,6 +38,8 @@ namespace Content.Client.MainMenu
|
||||
/// <inheritdoc />
|
||||
protected override void Startup()
|
||||
{
|
||||
_sawmill = _logManager.GetSawmill("mainmenu");
|
||||
|
||||
_mainMenuControl = new MainMenuControl(_resourceCache, _configurationManager);
|
||||
_userInterfaceManager.StateRoot.AddChild(_mainMenuControl);
|
||||
|
||||
@@ -116,7 +121,7 @@ namespace Content.Client.MainMenu
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
_userInterfaceManager.Popup($"Unable to connect: {e.Message}", "Connection error.");
|
||||
Logger.Warning(e.ToString());
|
||||
_sawmill.Warning(e.ToString());
|
||||
_netManager.ConnectFailed -= _onConnectFailed;
|
||||
_setConnectingState(false);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.Materials;
|
||||
using Content.Shared.Materials;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Materials;
|
||||
@@ -49,7 +49,7 @@ public sealed class MaterialStorageSystem : SharedMaterialStorageSystem
|
||||
{
|
||||
if (!base.TryInsertMaterialEntity(user, toInsert, receiver, storage, material, composition))
|
||||
return false;
|
||||
_transform.DetachParentToNull(toInsert, Transform(toInsert));
|
||||
_transform.DetachEntity(toInsert, Transform(toInsert));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ public sealed class JetpackSystem : SharedJetpackSystem
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly ClothingSystem _clothing = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -73,11 +74,11 @@ public sealed class JetpackSystem : SharedJetpackSystem
|
||||
|
||||
var uidXform = Transform(uid);
|
||||
var coordinates = uidXform.Coordinates;
|
||||
var gridUid = coordinates.GetGridUid(EntityManager);
|
||||
var gridUid = _transform.GetGrid(coordinates);
|
||||
|
||||
if (TryComp<MapGridComponent>(gridUid, out var grid))
|
||||
{
|
||||
coordinates = new EntityCoordinates(gridUid.Value, grid.WorldToLocal(coordinates.ToMapPos(EntityManager, _transform)));
|
||||
coordinates = new EntityCoordinates(gridUid.Value, _mapSystem.WorldToLocal(gridUid.Value, grid, _transform.ToMapCoordinates(coordinates).Position));
|
||||
}
|
||||
else if (uidXform.MapUid != null)
|
||||
{
|
||||
|
||||
@@ -203,7 +203,7 @@ namespace Content.Client.NPC
|
||||
if (found || !_system.Breadcrumbs.TryGetValue(netGrid, out var crumbs) || !xformQuery.TryGetComponent(grid, out var gridXform))
|
||||
continue;
|
||||
|
||||
var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv();
|
||||
var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform);
|
||||
var localAABB = invWorldMatrix.TransformBox(aabb.Enlarged(float.Epsilon - SharedPathfindingSystem.ChunkSize));
|
||||
|
||||
foreach (var chunk in crumbs)
|
||||
@@ -287,7 +287,7 @@ namespace Content.Client.NPC
|
||||
return;
|
||||
}
|
||||
|
||||
var invGridMatrix = gridXform.InvWorldMatrix;
|
||||
var invGridMatrix = _transformSystem.GetInvWorldMatrix(gridXform);
|
||||
DebugPathPoly? nearest = null;
|
||||
|
||||
foreach (var poly in tile)
|
||||
@@ -359,7 +359,7 @@ namespace Content.Client.NPC
|
||||
continue;
|
||||
}
|
||||
|
||||
var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv();
|
||||
var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform);
|
||||
worldHandle.SetTransform(worldMatrix);
|
||||
var localAABB = invWorldMatrix.TransformBox(aabb);
|
||||
|
||||
@@ -419,7 +419,7 @@ namespace Content.Client.NPC
|
||||
!xformQuery.TryGetComponent(grid, out var gridXform))
|
||||
continue;
|
||||
|
||||
var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv();
|
||||
var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform);
|
||||
worldHandle.SetTransform(worldMatrix);
|
||||
var localAABB = invWorldMatrix.TransformBox(aabb);
|
||||
|
||||
@@ -458,7 +458,7 @@ namespace Content.Client.NPC
|
||||
!xformQuery.TryGetComponent(grid, out var gridXform))
|
||||
continue;
|
||||
|
||||
var (_, _, worldMatrix, invMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv();
|
||||
var (_, _, worldMatrix, invMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform);
|
||||
worldHandle.SetTransform(worldMatrix);
|
||||
var localAABB = invMatrix.TransformBox(aabb);
|
||||
|
||||
@@ -483,7 +483,7 @@ namespace Content.Client.NPC
|
||||
if (neighborPoly.NetEntity != poly.GraphUid)
|
||||
{
|
||||
color = Color.Green;
|
||||
var neighborMap = _entManager.GetCoordinates(neighborPoly).ToMap(_entManager, _transformSystem);
|
||||
var neighborMap = _transformSystem.ToMapCoordinates(_entManager.GetCoordinates(neighborPoly));
|
||||
|
||||
if (neighborMap.MapId != args.MapId)
|
||||
continue;
|
||||
@@ -517,7 +517,7 @@ namespace Content.Client.NPC
|
||||
!xformQuery.TryGetComponent(grid, out var gridXform))
|
||||
continue;
|
||||
|
||||
var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv();
|
||||
var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform);
|
||||
worldHandle.SetTransform(worldMatrix);
|
||||
var localAABB = invWorldMatrix.TransformBox(args.WorldBounds);
|
||||
|
||||
@@ -544,7 +544,7 @@ namespace Content.Client.NPC
|
||||
if (!_entManager.TryGetComponent<TransformComponent>(_entManager.GetEntity(node.GraphUid), out var graphXform))
|
||||
continue;
|
||||
|
||||
worldHandle.SetTransform(graphXform.WorldMatrix);
|
||||
worldHandle.SetTransform(_transformSystem.GetWorldMatrix(graphXform));
|
||||
worldHandle.DrawRect(node.Box, Color.Orange.WithAlpha(0.10f));
|
||||
}
|
||||
}
|
||||
@@ -568,7 +568,7 @@ namespace Content.Client.NPC
|
||||
continue;
|
||||
|
||||
matrix = graph;
|
||||
worldHandle.SetTransform(graphXform.WorldMatrix);
|
||||
worldHandle.SetTransform(_transformSystem.GetWorldMatrix(graphXform));
|
||||
}
|
||||
|
||||
worldHandle.DrawRect(node.Box, new Color(0f, cost / highestGScore, 1f - (cost / highestGScore), 0.10f));
|
||||
|
||||
5
Content.Client/Ninja/Systems/ItemCreatorSystem.cs
Normal file
5
Content.Client/Ninja/Systems/ItemCreatorSystem.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
using Content.Shared.Ninja.Systems;
|
||||
|
||||
namespace Content.Client.Ninja.Systems;
|
||||
|
||||
public sealed class ItemCreatorSystem : SharedItemCreatorSystem;
|
||||
@@ -2,9 +2,4 @@ using Content.Shared.Ninja.Systems;
|
||||
|
||||
namespace Content.Client.Ninja.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Does nothing special, only exists to provide a client implementation.
|
||||
/// </summary>
|
||||
public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem
|
||||
{
|
||||
}
|
||||
public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem;
|
||||
|
||||
@@ -1,24 +1,5 @@
|
||||
using Content.Shared.Clothing.EntitySystems;
|
||||
using Content.Shared.Ninja.Components;
|
||||
using Content.Shared.Ninja.Systems;
|
||||
|
||||
namespace Content.Client.Ninja.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Disables cloak prediction since client has no knowledge of battery power.
|
||||
/// Cloak will still be enabled after server tells it.
|
||||
/// </summary>
|
||||
public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NinjaSuitComponent, AttemptStealthEvent>(OnAttemptStealth);
|
||||
}
|
||||
|
||||
private void OnAttemptStealth(EntityUid uid, NinjaSuitComponent comp, AttemptStealthEvent args)
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
public sealed class NinjaSuitSystem : SharedNinjaSuitSystem;
|
||||
|
||||
@@ -2,11 +2,4 @@ using Content.Shared.Ninja.Systems;
|
||||
|
||||
namespace Content.Client.Ninja.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Currently does nothing special clientside.
|
||||
/// All functionality is in shared and server.
|
||||
/// Only exists to prevent crashing.
|
||||
/// </summary>
|
||||
public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem
|
||||
{
|
||||
}
|
||||
public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem;
|
||||
|
||||
5
Content.Client/Ninja/Systems/SpiderChargeSystem.cs
Normal file
5
Content.Client/Ninja/Systems/SpiderChargeSystem.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
using Content.Shared.Ninja.Systems;
|
||||
|
||||
namespace Content.Client.Ninja.Systems;
|
||||
|
||||
public sealed class SpiderChargeSystem : SharedSpiderChargeSystem;
|
||||
@@ -58,8 +58,8 @@ public sealed class JointVisualsOverlay : Overlay
|
||||
coordsA = coordsA.Offset(rotA.RotateVec(visuals.OffsetA));
|
||||
coordsB = coordsB.Offset(rotB.RotateVec(visuals.OffsetB));
|
||||
|
||||
var posA = coordsA.ToMapPos(_entManager, xformSystem);
|
||||
var posB = coordsB.ToMapPos(_entManager, xformSystem);
|
||||
var posA = xformSystem.ToMapCoordinates(coordsA).Position;
|
||||
var posB = xformSystem.ToMapCoordinates(coordsB).Position;
|
||||
var diff = (posB - posA);
|
||||
var length = diff.Length();
|
||||
|
||||
|
||||
@@ -228,8 +228,8 @@ public partial class NavMapControl : MapGridControl
|
||||
{
|
||||
if (!blip.Selectable)
|
||||
continue;
|
||||
|
||||
var currentDistance = (blip.Coordinates.ToMapPos(EntManager, _transformSystem) - worldPosition).Length();
|
||||
|
||||
var currentDistance = (_transformSystem.ToMapCoordinates(blip.Coordinates).Position - worldPosition).Length();
|
||||
|
||||
if (closestDistance < currentDistance || currentDistance * MinimapScale > MaxSelectableDistance)
|
||||
continue;
|
||||
@@ -397,7 +397,7 @@ public partial class NavMapControl : MapGridControl
|
||||
{
|
||||
if (lit && value.Visible)
|
||||
{
|
||||
var mapPos = coord.ToMap(EntManager, _transformSystem);
|
||||
var mapPos = _transformSystem.ToMapCoordinates(coord);
|
||||
|
||||
if (mapPos.MapId != MapId.Nullspace)
|
||||
{
|
||||
@@ -418,7 +418,7 @@ public partial class NavMapControl : MapGridControl
|
||||
if (blip.Texture == null)
|
||||
continue;
|
||||
|
||||
var mapPos = blip.Coordinates.ToMap(EntManager, _transformSystem);
|
||||
var mapPos = _transformSystem.ToMapCoordinates(blip.Coordinates);
|
||||
|
||||
if (mapPos.MapId != MapId.Nullspace)
|
||||
{
|
||||
@@ -535,7 +535,7 @@ public partial class NavMapControl : MapGridControl
|
||||
// East edge
|
||||
neighborData = 0;
|
||||
if (relativeTile.X != SharedNavMapSystem.ChunkSize - 1)
|
||||
neighborData = chunk.TileData[i+SharedNavMapSystem.ChunkSize];
|
||||
neighborData = chunk.TileData[i + SharedNavMapSystem.ChunkSize];
|
||||
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Right, out neighborChunk))
|
||||
neighborData = neighborChunk.TileData[i + SharedNavMapSystem.ChunkSize - SharedNavMapSystem.ArraySize];
|
||||
|
||||
@@ -548,7 +548,7 @@ public partial class NavMapControl : MapGridControl
|
||||
// South edge
|
||||
neighborData = 0;
|
||||
if (relativeTile.Y != 0)
|
||||
neighborData = chunk.TileData[i-1];
|
||||
neighborData = chunk.TileData[i - 1];
|
||||
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Down, out neighborChunk))
|
||||
neighborData = neighborChunk.TileData[i - 1 + SharedNavMapSystem.ChunkSize];
|
||||
|
||||
@@ -561,7 +561,7 @@ public partial class NavMapControl : MapGridControl
|
||||
// West edge
|
||||
neighborData = 0;
|
||||
if (relativeTile.X != 0)
|
||||
neighborData = chunk.TileData[i-SharedNavMapSystem.ChunkSize];
|
||||
neighborData = chunk.TileData[i - SharedNavMapSystem.ChunkSize];
|
||||
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Left, out neighborChunk))
|
||||
neighborData = neighborChunk.TileData[i - SharedNavMapSystem.ChunkSize + SharedNavMapSystem.ArraySize];
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public sealed class AlignRCDConstruction : PlacementMode
|
||||
_unalignedMouseCoords = ScreenToCursorGrid(mouseScreen);
|
||||
MouseCoords = _unalignedMouseCoords.AlignWithClosestGridTile(SearchBoxSize, _entityManager, _mapManager);
|
||||
|
||||
var gridId = MouseCoords.GetGridUid(_entityManager);
|
||||
var gridId = _transformSystem.GetGrid(MouseCoords);
|
||||
|
||||
if (!_entityManager.TryGetComponent<MapGridComponent>(gridId, out var mapGrid))
|
||||
return;
|
||||
@@ -75,7 +75,7 @@ public sealed class AlignRCDConstruction : PlacementMode
|
||||
if (!_entityManager.TryGetComponent<TransformComponent>(player, out var xform))
|
||||
return false;
|
||||
|
||||
if (!xform.Coordinates.InRange(_entityManager, _transformSystem, position, SharedInteractionSystem.InteractionRange))
|
||||
if (!_transformSystem.InRange(xform.Coordinates, position, SharedInteractionSystem.InteractionRange))
|
||||
{
|
||||
InvalidPlaceColor = InvalidPlaceColor.WithAlpha(0);
|
||||
return false;
|
||||
@@ -105,8 +105,8 @@ public sealed class AlignRCDConstruction : PlacementMode
|
||||
|
||||
if (currentState is not GameplayStateBase screen)
|
||||
return false;
|
||||
|
||||
var target = screen.GetClickedEntity(_unalignedMouseCoords.ToMap(_entityManager, _transformSystem));
|
||||
|
||||
var target = screen.GetClickedEntity(_transformSystem.ToMapCoordinates(_unalignedMouseCoords));
|
||||
|
||||
// Determine if the RCD operation is valid or not
|
||||
if (!_rcdSystem.IsRCDOperationStillValid(heldEntity.Value, rcd, mapGridData.Value, target, player.Value, false))
|
||||
|
||||
@@ -116,7 +116,9 @@ namespace Content.Client.Radiation.Overlays
|
||||
var shaderInstance = _pulses[pulseEntity];
|
||||
shaderInstance.instance.CurrentMapCoords = _transform.GetMapCoordinates(pulseEntity);
|
||||
shaderInstance.instance.Range = pulse.VisualRange;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_pulses[pulseEntity].shd.Dispose();
|
||||
_pulses.Remove(pulseEntity);
|
||||
}
|
||||
@@ -129,7 +131,7 @@ namespace Content.Client.Radiation.Overlays
|
||||
var transformComponent = _entityManager.GetComponent<TransformComponent>(pulseEntity);
|
||||
var transformSystem = _entityManager.System<SharedTransformSystem>();
|
||||
return transformComponent.MapID == currentEyeLoc.MapId
|
||||
&& transformComponent.Coordinates.InRange(_entityManager, transformSystem, EntityCoordinates.FromMap(transformComponent.ParentUid, currentEyeLoc, transformSystem, _entityManager), MaxDist);
|
||||
&& transformSystem.InRange(transformComponent.Coordinates, transformSystem.ToCoordinates(transformComponent.ParentUid, currentEyeLoc), MaxDist);
|
||||
}
|
||||
|
||||
private sealed record RadiationShaderInstance(MapCoordinates CurrentMapCoords, float Range, TimeSpan Start, float Duration)
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace Content.Client.Sandbox
|
||||
[Dependency] private readonly IPlacementManager _placement = default!;
|
||||
[Dependency] private readonly ContentEyeSystem _contentEye = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
|
||||
private bool _sandboxEnabled;
|
||||
public bool SandboxAllowed { get; private set; }
|
||||
@@ -92,7 +93,7 @@ namespace Content.Client.Sandbox
|
||||
&& EntityManager.TryGetComponent(uid, out MetaDataComponent? comp)
|
||||
&& !comp.EntityDeleted)
|
||||
{
|
||||
if (comp.EntityPrototype == null || comp.EntityPrototype.NoSpawn || comp.EntityPrototype.Abstract)
|
||||
if (comp.EntityPrototype == null || comp.EntityPrototype.HideSpawnMenu || comp.EntityPrototype.Abstract)
|
||||
return false;
|
||||
|
||||
if (_placement.Eraser)
|
||||
@@ -109,7 +110,8 @@ namespace Content.Client.Sandbox
|
||||
}
|
||||
|
||||
// Try copy tile.
|
||||
if (!_map.TryFindGridAt(coords.ToMap(EntityManager, _transform), out _, out var grid) || !grid.TryGetTileRef(coords, out var tileRef))
|
||||
|
||||
if (!_map.TryFindGridAt(_transform.ToMapCoordinates(coords), out var gridUid, out var grid) || !_mapSystem.TryGetTileRef(gridUid, grid, coords, out var tileRef))
|
||||
return false;
|
||||
|
||||
if (_placement.Eraser)
|
||||
|
||||
@@ -35,9 +35,9 @@ public sealed partial class ShuttleSystem
|
||||
switch (mapObj)
|
||||
{
|
||||
case ShuttleBeaconObject beacon:
|
||||
return GetCoordinates(beacon.Coordinates).ToMap(EntityManager, XformSystem);
|
||||
return XformSystem.ToMapCoordinates(GetCoordinates(beacon.Coordinates));
|
||||
case ShuttleExclusionObject exclusion:
|
||||
return GetCoordinates(exclusion.Coordinates).ToMap(EntityManager, XformSystem);
|
||||
return XformSystem.ToMapCoordinates(GetCoordinates(exclusion.Coordinates));
|
||||
case GridMapObject grid:
|
||||
var gridXform = Transform(grid.Entity);
|
||||
|
||||
|
||||
@@ -311,7 +311,7 @@ public sealed partial class MapScreen : BoxContainer
|
||||
};
|
||||
|
||||
_mapHeadings.Add(mapComp.MapId, gridContents);
|
||||
foreach (var grid in _mapManager.GetAllMapGrids(mapComp.MapId))
|
||||
foreach (var grid in _mapManager.GetAllGrids(mapComp.MapId))
|
||||
{
|
||||
_entManager.TryGetComponent(grid.Owner, out IFFComponent? iffComp);
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
|
||||
DrawCircles(handle);
|
||||
var gridNent = EntManager.GetNetEntity(GridEntity);
|
||||
var mapPos = _xformSystem.ToMapCoordinates(_coordinates.Value);
|
||||
var ourGridMatrix = _xformSystem.GetWorldMatrix(gridXform.Owner);
|
||||
var ourGridMatrix = _xformSystem.GetWorldMatrix(GridEntity.Value);
|
||||
var dockMatrix = Matrix3Helpers.CreateTransform(_coordinates.Value.Position, Angle.Zero);
|
||||
var worldFromDock = Matrix3x2.Multiply(dockMatrix, ourGridMatrix);
|
||||
|
||||
|
||||
@@ -519,7 +519,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
if (mapO is not ShuttleBeaconObject beacon)
|
||||
continue;
|
||||
|
||||
var beaconCoords = EntManager.GetCoordinates(beacon.Coordinates).ToMap(EntManager, _xformSystem);
|
||||
var beaconCoords = _xformSystem.ToMapCoordinates(EntManager.GetCoordinates(beacon.Coordinates));
|
||||
var position = Vector2.Transform(beaconCoords.Position, mapTransform);
|
||||
var localPos = ScalePosition(position with {Y = -position.Y});
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Content.Client.Stack
|
||||
// TODO PREDICT ENTITY DELETION: This should really just be a normal entity deletion call.
|
||||
if (component.Count <= 0 && !component.Lingering)
|
||||
{
|
||||
Xform.DetachParentToNull(uid, Transform(uid));
|
||||
Xform.DetachEntity(uid, Transform(uid));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.Animations;
|
||||
using Content.Shared.Hands;
|
||||
@@ -142,14 +142,14 @@ public sealed class StorageSystem : SharedStorageSystem
|
||||
{
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
if (finalCoords.InRange(EntityManager, TransformSystem, initialCoords, 0.1f) ||
|
||||
|
||||
if (TransformSystem.InRange(finalCoords, initialCoords, 0.1f) ||
|
||||
!Exists(initialCoords.EntityId) || !Exists(finalCoords.EntityId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var finalMapPos = finalCoords.ToMapPos(EntityManager, TransformSystem);
|
||||
var finalMapPos = TransformSystem.ToMapCoordinates(finalCoords).Position;
|
||||
var finalPos = Vector2.Transform(finalMapPos, TransformSystem.GetInvWorldMatrix(initialCoords.EntityId));
|
||||
|
||||
_entityPickupAnimation.AnimateEntityPickup(item, initialCoords, finalPos, initialAngle);
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Client.Administration.Systems;
|
||||
using Content.Client.Administration.UI;
|
||||
using Content.Client.Administration.UI.Tabs.ObjectsTab;
|
||||
using Content.Client.Administration.UI.Tabs.PanicBunkerTab;
|
||||
using Content.Client.Administration.UI.Tabs.BabyJailTab;
|
||||
using Content.Client.Administration.UI.Tabs.PlayerTab;
|
||||
using Content.Client.Gameplay;
|
||||
using Content.Client.Lobby;
|
||||
@@ -37,11 +38,13 @@ public sealed class AdminUIController : UIController,
|
||||
private AdminMenuWindow? _window;
|
||||
private MenuButton? AdminButton => UIManager.GetActiveUIWidgetOrNull<MenuBar.Widgets.GameTopMenuBar>()?.AdminButton;
|
||||
private PanicBunkerStatus? _panicBunker;
|
||||
private BabyJailStatus? _babyJail;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeNetworkEvent<PanicBunkerChangedEvent>(OnPanicBunkerUpdated);
|
||||
SubscribeNetworkEvent<BabyJailChangedEvent>(OnBabyJailUpdated);
|
||||
}
|
||||
|
||||
private void OnPanicBunkerUpdated(PanicBunkerChangedEvent msg, EntitySessionEventArgs args)
|
||||
@@ -56,6 +59,18 @@ public sealed class AdminUIController : UIController,
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBabyJailUpdated(BabyJailChangedEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var showDialog = _babyJail == null && msg.Status.Enabled;
|
||||
_babyJail = msg.Status;
|
||||
_window?.BabyJailControl.UpdateStatus(msg.Status);
|
||||
|
||||
if (showDialog)
|
||||
{
|
||||
UIManager.CreateWindow<BabyJailStatusWindow>().OpenCentered();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnStateEntered(GameplayState state)
|
||||
{
|
||||
EnsureWindow();
|
||||
@@ -101,6 +116,13 @@ public sealed class AdminUIController : UIController,
|
||||
if (_panicBunker != null)
|
||||
_window.PanicBunkerControl.UpdateStatus(_panicBunker);
|
||||
|
||||
/*
|
||||
* TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
|
||||
if (_babyJail != null)
|
||||
_window.BabyJailControl.UpdateStatus(_babyJail);
|
||||
|
||||
_window.PlayerTabControl.OnEntryKeyBindDown += PlayerTabEntryKeyBindDown;
|
||||
_window.ObjectsTabControl.OnEntryKeyBindDown += ObjectsTabEntryKeyBindDown;
|
||||
_window.OnOpen += OnWindowOpen;
|
||||
|
||||
@@ -220,7 +220,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||
return;
|
||||
}
|
||||
|
||||
var targetMap = coordinates.ToMap(EntityManager, TransformSystem);
|
||||
var targetMap = TransformSystem.ToMapCoordinates(coordinates);
|
||||
|
||||
if (targetMap.MapId != userXform.MapID)
|
||||
return;
|
||||
|
||||
@@ -176,7 +176,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
}
|
||||
|
||||
// Define target coordinates relative to gun entity, so that network latency on moving grids doesn't fuck up the target location.
|
||||
var coordinates = EntityCoordinates.FromMap(entity, mousePos, TransformSystem, EntityManager);
|
||||
var coordinates = TransformSystem.ToCoordinates(entity, mousePos);
|
||||
|
||||
NetEntity? target = null;
|
||||
if (_state.CurrentState is GameplayStateBase screen)
|
||||
@@ -200,7 +200,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
// Rather than splitting client / server for every ammo provider it's easier
|
||||
// to just delete the spawned entities. This is for programmer sanity despite the wasted perf.
|
||||
// This also means any ammo specific stuff can be grabbed as necessary.
|
||||
var direction = fromCoordinates.ToMapPos(EntityManager, TransformSystem) - toCoordinates.ToMapPos(EntityManager, TransformSystem);
|
||||
var direction = TransformSystem.ToMapCoordinates(fromCoordinates).Position - TransformSystem.ToMapCoordinates(toCoordinates).Position;
|
||||
var worldAngle = direction.ToAngle().Opposite();
|
||||
|
||||
foreach (var (ent, shootable) in ammo)
|
||||
@@ -276,6 +276,14 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
if (!Timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
// EntityUid check added to stop throwing exceptions due to https://github.com/space-wizards/space-station-14/issues/28252
|
||||
// TODO: Check to see why invalid entities are firing effects.
|
||||
if (gunUid == EntityUid.Invalid)
|
||||
{
|
||||
Log.Debug($"Invalid Entity sent MuzzleFlashEvent (proto: {message.Prototype}, user: {user})");
|
||||
return;
|
||||
}
|
||||
|
||||
var gunXform = Transform(gunUid);
|
||||
var gridUid = gunXform.GridUid;
|
||||
EntityCoordinates coordinates;
|
||||
@@ -375,6 +383,6 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
var uidPlayer = EnsureComp<AnimationPlayerComponent>(gunUid);
|
||||
|
||||
_animPlayer.Stop(gunUid, uidPlayer, "muzzle-flash-light");
|
||||
_animPlayer.Play((gunUid, uidPlayer), animTwo,"muzzle-flash-light");
|
||||
_animPlayer.Play((gunUid, uidPlayer), animTwo, "muzzle-flash-light");
|
||||
}
|
||||
}
|
||||
|
||||
53
Content.IntegrationTests/Tests/Atmos/GridJoinTest.cs
Normal file
53
Content.IntegrationTests/Tests/Atmos/GridJoinTest.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Atmos.Piping.Components;
|
||||
using Content.Server.Atmos.Piping.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Atmos;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class GridJoinTest
|
||||
{
|
||||
private const string CanisterProtoId = "AirCanister";
|
||||
|
||||
[Test]
|
||||
public async Task TestGridJoinAtmosphere()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var entMan = server.EntMan;
|
||||
var protoMan = server.ProtoMan;
|
||||
var atmosSystem = entMan.System<AtmosphereSystem>();
|
||||
var atmosDeviceSystem = entMan.System<AtmosDeviceSystem>();
|
||||
var transformSystem = entMan.System<SharedTransformSystem>();
|
||||
|
||||
var testMap = await pair.CreateTestMap();
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
// Spawn an atmos device on the grid
|
||||
var canister = entMan.Spawn(CanisterProtoId);
|
||||
transformSystem.SetCoordinates(canister, testMap.GridCoords);
|
||||
var deviceComp = entMan.GetComponent<AtmosDeviceComponent>(canister);
|
||||
var canisterEnt = (canister, deviceComp);
|
||||
|
||||
// Make sure the canister is tracked as an off-grid device
|
||||
Assert.That(atmosDeviceSystem.IsJoinedOffGrid(canisterEnt));
|
||||
|
||||
// Add an atmosphere to the grid
|
||||
entMan.AddComponent<GridAtmosphereComponent>(testMap.Grid);
|
||||
|
||||
// Force AtmosDeviceSystem to update off-grid devices
|
||||
// This means the canister is now considered on-grid,
|
||||
// but it's still tracked as off-grid!
|
||||
Assert.DoesNotThrow(() => atmosDeviceSystem.Update(atmosSystem.AtmosTime));
|
||||
|
||||
// Make sure that the canister is now properly tracked as on-grid
|
||||
Assert.That(atmosDeviceSystem.IsJoinedOffGrid(canisterEnt), Is.False);
|
||||
});
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
133
Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs
Normal file
133
Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Antag.Components;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.GameTicking.Rules;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Roles;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.NPC.Systems;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.GameRules;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class TraitorRuleTest
|
||||
{
|
||||
private const string TraitorGameRuleProtoId = "Traitor";
|
||||
private const string TraitorAntagRoleName = "Traitor";
|
||||
|
||||
[Test]
|
||||
public async Task TestTraitorObjectives()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient(new PoolSettings()
|
||||
{
|
||||
Dirty = true,
|
||||
DummyTicker = false,
|
||||
Connected = true,
|
||||
InLobby = true,
|
||||
});
|
||||
var server = pair.Server;
|
||||
var client = pair.Client;
|
||||
var entMan = server.EntMan;
|
||||
var protoMan = server.ProtoMan;
|
||||
var compFact = server.ResolveDependency<IComponentFactory>();
|
||||
var ticker = server.System<GameTicker>();
|
||||
var mindSys = server.System<MindSystem>();
|
||||
var roleSys = server.System<RoleSystem>();
|
||||
var factionSys = server.System<NpcFactionSystem>();
|
||||
var traitorRuleSys = server.System<TraitorRuleSystem>();
|
||||
|
||||
// Look up the minimum player count and max total objective difficulty for the game rule
|
||||
var minPlayers = 1;
|
||||
var maxDifficulty = 0f;
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(protoMan.TryIndex<EntityPrototype>(TraitorGameRuleProtoId, out var gameRuleEnt),
|
||||
$"Failed to lookup traitor game rule entity prototype with ID \"{TraitorGameRuleProtoId}\"!");
|
||||
|
||||
Assert.That(gameRuleEnt.TryGetComponent<GameRuleComponent>(out var gameRule, compFact),
|
||||
$"Game rule entity {TraitorGameRuleProtoId} does not have a GameRuleComponent!");
|
||||
|
||||
Assert.That(gameRuleEnt.TryGetComponent<AntagRandomObjectivesComponent>(out var randomObjectives, compFact),
|
||||
$"Game rule entity {TraitorGameRuleProtoId} does not have an AntagRandomObjectivesComponent!");
|
||||
|
||||
minPlayers = gameRule.MinPlayers;
|
||||
maxDifficulty = randomObjectives.MaxDifficulty;
|
||||
});
|
||||
|
||||
// Initially in the lobby
|
||||
Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby));
|
||||
Assert.That(client.AttachedEntity, Is.Null);
|
||||
Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.NotReadyToPlay));
|
||||
|
||||
// Add enough dummy players for the game rule
|
||||
var dummies = await pair.Server.AddDummySessions(minPlayers);
|
||||
await pair.RunTicksSync(5);
|
||||
|
||||
// Initially, the players have no attached entities
|
||||
Assert.That(pair.Player?.AttachedEntity, Is.Null);
|
||||
Assert.That(dummies.All(x => x.AttachedEntity == null));
|
||||
|
||||
// Opt-in the player for the traitor role
|
||||
await pair.SetAntagPreference(TraitorAntagRoleName, true);
|
||||
|
||||
// Add the game rule
|
||||
var gameRuleEnt = ticker.AddGameRule(TraitorGameRuleProtoId);
|
||||
Assert.That(entMan.TryGetComponent<TraitorRuleComponent>(gameRuleEnt, out var traitorRule));
|
||||
|
||||
// Ready up
|
||||
ticker.ToggleReadyAll(true);
|
||||
Assert.That(ticker.PlayerGameStatuses.Values.All(x => x == PlayerGameStatus.ReadyToPlay));
|
||||
|
||||
// Start the round
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
ticker.StartRound();
|
||||
// Force traitor mode to start (skip the delay)
|
||||
ticker.StartGameRule(gameRuleEnt);
|
||||
});
|
||||
await pair.RunTicksSync(10);
|
||||
|
||||
// Game should have started
|
||||
Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.InRound));
|
||||
Assert.That(ticker.PlayerGameStatuses.Values.All(x => x == PlayerGameStatus.JoinedGame));
|
||||
Assert.That(client.EntMan.EntityExists(client.AttachedEntity));
|
||||
|
||||
// Check the player and dummies are spawned
|
||||
var dummyEnts = dummies.Select(x => x.AttachedEntity ?? default).ToArray();
|
||||
var player = pair.Player!.AttachedEntity!.Value;
|
||||
Assert.That(entMan.EntityExists(player));
|
||||
Assert.That(dummyEnts.All(entMan.EntityExists));
|
||||
|
||||
// Make sure the player is a traitor.
|
||||
var mind = mindSys.GetMind(player)!.Value;
|
||||
Assert.That(roleSys.MindIsAntagonist(mind));
|
||||
Assert.That(factionSys.IsMember(player, "Syndicate"), Is.True);
|
||||
Assert.That(factionSys.IsMember(player, "NanoTrasen"), Is.False);
|
||||
Assert.That(traitorRule.TotalTraitors, Is.EqualTo(1));
|
||||
Assert.That(traitorRule.TraitorMinds[0], Is.EqualTo(mind));
|
||||
|
||||
// Check total objective difficulty
|
||||
Assert.That(entMan.TryGetComponent<MindComponent>(mind, out var mindComp));
|
||||
var totalDifficulty = mindComp.Objectives.Sum(o => entMan.GetComponent<ObjectiveComponent>(o).Difficulty);
|
||||
Assert.That(totalDifficulty, Is.AtMost(maxDifficulty),
|
||||
$"MaxDifficulty exceeded! Objectives: {string.Join(", ", mindComp.Objectives.Select(o => FormatObjective(o, entMan)))}");
|
||||
Assert.That(mindComp.Objectives, Is.Not.Empty,
|
||||
$"No objectives assigned!");
|
||||
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
private static string FormatObjective(Entity<ObjectiveComponent> entity, IEntityManager entMan)
|
||||
{
|
||||
var meta = entMan.GetComponent<MetaDataComponent>(entity);
|
||||
var objective = entMan.GetComponent<ObjectiveComponent>(entity);
|
||||
return $"{meta.EntityName} ({objective.Difficulty})";
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Storage.EntitySystems;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
@@ -11,6 +13,19 @@ namespace Content.IntegrationTests.Tests.Hands;
|
||||
[TestFixture]
|
||||
public sealed class HandTests
|
||||
{
|
||||
[TestPrototypes]
|
||||
private const string Prototypes = @"
|
||||
- type: entity
|
||||
id: TestPickUpThenDropInContainerTestBox
|
||||
name: box
|
||||
components:
|
||||
- type: EntityStorage
|
||||
- type: ContainerContainer
|
||||
containers:
|
||||
entity_storage: !type:Container
|
||||
";
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task TestPickupDrop()
|
||||
{
|
||||
@@ -57,4 +72,69 @@ public sealed class HandTests
|
||||
await server.WaitPost(() => mapMan.DeleteMap(data.MapId));
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestPickUpThenDropInContainer()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient(new PoolSettings
|
||||
{
|
||||
Connected = true,
|
||||
DummyTicker = false
|
||||
});
|
||||
var server = pair.Server;
|
||||
var map = await pair.CreateTestMap();
|
||||
await pair.RunTicksSync(5);
|
||||
|
||||
var entMan = server.ResolveDependency<IEntityManager>();
|
||||
var playerMan = server.ResolveDependency<IPlayerManager>();
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
var sys = entMan.System<SharedHandsSystem>();
|
||||
var tSys = entMan.System<TransformSystem>();
|
||||
var containerSystem = server.System<SharedContainerSystem>();
|
||||
|
||||
EntityUid item = default;
|
||||
EntityUid box = default;
|
||||
EntityUid player = default;
|
||||
HandsComponent hands = default!;
|
||||
|
||||
// spawn the elusive box and crowbar at the coordinates
|
||||
await server.WaitPost(() => box = server.EntMan.SpawnEntity("TestPickUpThenDropInContainerTestBox", map.GridCoords));
|
||||
await server.WaitPost(() => item = server.EntMan.SpawnEntity("Crowbar", map.GridCoords));
|
||||
// place the player at the exact same coordinates and have them grab the crowbar
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
player = playerMan.Sessions.First().AttachedEntity!.Value;
|
||||
tSys.PlaceNextTo(player, item);
|
||||
hands = entMan.GetComponent<HandsComponent>(player);
|
||||
sys.TryPickup(player, item, hands.ActiveHand!);
|
||||
});
|
||||
await pair.RunTicksSync(5);
|
||||
Assert.That(hands.ActiveHandEntity, Is.EqualTo(item));
|
||||
|
||||
// Open then close the box to place the player, who is holding the crowbar, inside of it
|
||||
var storage = server.System<EntityStorageSystem>();
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
storage.OpenStorage(box);
|
||||
storage.CloseStorage(box);
|
||||
});
|
||||
await pair.RunTicksSync(5);
|
||||
Assert.That(containerSystem.IsEntityInContainer(player), Is.True);
|
||||
|
||||
// Dropping the item while the player is inside the box should cause the item
|
||||
// to also be inside the same container the player is in now,
|
||||
// with the item not being in the player's hands
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
sys.TryDrop(player, item, null!);
|
||||
});
|
||||
await pair.RunTicksSync(5);
|
||||
var xform = entMan.GetComponent<TransformComponent>(player);
|
||||
var itemXform = entMan.GetComponent<TransformComponent>(item);
|
||||
Assert.That(hands.ActiveHandEntity, Is.Not.EqualTo(item));
|
||||
Assert.That(containerSystem.IsInSameOrNoContainer((player, xform), (item, itemXform)));
|
||||
|
||||
await server.WaitPost(() => mapMan.DeleteMap(map.MapId));
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ public abstract partial class InteractionTest
|
||||
return await SpawnEntity((stack.StackTypeId, spec.Quantity), coords);
|
||||
|
||||
Assert.That(spec.Quantity, Is.EqualTo(1), "SpawnEntity only supports returning a singular entity");
|
||||
await Server.WaitPost(() => uid = SEntMan.SpawnEntity(spec.Prototype, coords));
|
||||
await Server.WaitPost(() => uid = SEntMan.SpawnAtPosition(spec.Prototype, coords));
|
||||
return uid;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ public abstract partial class InteractionTest
|
||||
Target = NetEntity.Invalid;
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
Target = SEntMan.GetNetEntity(SEntMan.SpawnEntity(prototype, SEntMan.GetCoordinates(TargetCoords)));
|
||||
Target = SEntMan.GetNetEntity(SEntMan.SpawnAtPosition(prototype, SEntMan.GetCoordinates(TargetCoords)));
|
||||
});
|
||||
|
||||
await RunTicks(5);
|
||||
@@ -171,7 +171,7 @@ public abstract partial class InteractionTest
|
||||
// turn on welders
|
||||
if (enableToggleable && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated)
|
||||
{
|
||||
Assert.That(ItemToggleSys.TryActivate(item, playerEnt, itemToggle: itemToggle));
|
||||
Assert.That(ItemToggleSys.TryActivate((item, itemToggle), user: playerEnt));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ public abstract partial class InteractionTest
|
||||
protected Content.Server.Construction.ConstructionSystem SConstruction = default!;
|
||||
protected SharedDoAfterSystem DoAfterSys = default!;
|
||||
protected ToolSystem ToolSys = default!;
|
||||
protected SharedItemToggleSystem ItemToggleSys = default!;
|
||||
protected ItemToggleSystem ItemToggleSys = default!;
|
||||
protected InteractionTestSystem STestSystem = default!;
|
||||
protected SharedTransformSystem Transform = default!;
|
||||
protected SharedMapSystem MapSystem = default!;
|
||||
@@ -165,7 +165,7 @@ public abstract partial class InteractionTest
|
||||
HandSys = SEntMan.System<HandsSystem>();
|
||||
InteractSys = SEntMan.System<SharedInteractionSystem>();
|
||||
ToolSys = SEntMan.System<ToolSystem>();
|
||||
ItemToggleSys = SEntMan.System<SharedItemToggleSystem>();
|
||||
ItemToggleSys = SEntMan.System<ItemToggleSystem>();
|
||||
DoAfterSys = SEntMan.System<SharedDoAfterSystem>();
|
||||
Transform = SEntMan.System<SharedTransformSystem>();
|
||||
MapSystem = SEntMan.System<SharedMapSystem>();
|
||||
|
||||
@@ -902,6 +902,10 @@ namespace Content.Server.Database
|
||||
Whitelist = 1,
|
||||
Full = 2,
|
||||
Panic = 3,
|
||||
/*
|
||||
* TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
BabyJail = 4,
|
||||
}
|
||||
|
||||
public class ServerBanHit
|
||||
|
||||
139
Content.Server/Administration/Commands/BabyJailCommand.cs
Normal file
139
Content.Server/Administration/Commands/BabyJailCommand.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
/*
|
||||
* TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
[AdminCommand(AdminFlags.Server)]
|
||||
public sealed class BabyJailCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public override string Command => "babyjail";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var toggle = Toggle(CCVars.BabyJailEnabled, shell, args, _cfg);
|
||||
if (toggle == null)
|
||||
return;
|
||||
|
||||
shell.WriteLine(Loc.GetString(toggle.Value ? "babyjail-command-enabled" : "babyjail-command-disabled"));
|
||||
}
|
||||
|
||||
public static bool? Toggle(CVarDef<bool> cvar, IConsoleShell shell, string[] args, IConfigurationManager config)
|
||||
{
|
||||
if (args.Length > 1)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("shell-need-between-arguments",("lower", 0), ("upper", 1)));
|
||||
return null;
|
||||
}
|
||||
|
||||
var enabled = config.GetCVar(cvar);
|
||||
|
||||
switch (args.Length)
|
||||
{
|
||||
case 0:
|
||||
enabled = !enabled;
|
||||
break;
|
||||
case 1 when !bool.TryParse(args[0], out enabled):
|
||||
shell.WriteError(Loc.GetString("shell-argument-must-be-boolean"));
|
||||
return null;
|
||||
}
|
||||
|
||||
config.SetCVar(cvar, enabled);
|
||||
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[AdminCommand(AdminFlags.Server)]
|
||||
public sealed class BabyJailShowReasonCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public override string Command => "babyjail_show_reason";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var toggle = BabyJailCommand.Toggle(CCVars.BabyJailShowReason, shell, args, _cfg);
|
||||
if (toggle == null)
|
||||
return;
|
||||
|
||||
shell.WriteLine(Loc.GetString(toggle.Value
|
||||
? "babyjail-command-show-reason-enabled"
|
||||
: "babyjail-command-show-reason-disabled"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
[AdminCommand(AdminFlags.Server)]
|
||||
public sealed class BabyJailMinAccountAgeCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public override string Command => "babyjail_max_account_age";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
switch (args.Length)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
var current = _cfg.GetCVar(CCVars.BabyJailMaxAccountAge);
|
||||
shell.WriteLine(Loc.GetString("babyjail-command-max-account-age-is", ("minutes", current)));
|
||||
break;
|
||||
}
|
||||
case > 1:
|
||||
shell.WriteError(Loc.GetString("shell-need-between-arguments",("lower", 0), ("upper", 1)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!int.TryParse(args[0], out var minutes))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("shell-argument-must-be-number"));
|
||||
return;
|
||||
}
|
||||
|
||||
_cfg.SetCVar(CCVars.BabyJailMaxAccountAge, minutes);
|
||||
shell.WriteLine(Loc.GetString("babyjail-command-max-account-age-set", ("minutes", minutes)));
|
||||
}
|
||||
}
|
||||
|
||||
[AdminCommand(AdminFlags.Server)]
|
||||
public sealed class BabyJailMinOverallHoursCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public override string Command => "babyjail_max_overall_minutes";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
switch (args.Length)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
var current = _cfg.GetCVar(CCVars.BabyJailMaxOverallMinutes);
|
||||
shell.WriteLine(Loc.GetString("babyjail-command-max-overall-minutes-is", ("minutes", current)));
|
||||
break;
|
||||
}
|
||||
case > 1:
|
||||
shell.WriteError(Loc.GetString("shell-need-between-arguments",("lower", 0), ("upper", 1)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!int.TryParse(args[0], out var hours))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("shell-argument-must-be-number"));
|
||||
return;
|
||||
}
|
||||
|
||||
_cfg.SetCVar(CCVars.BabyJailMaxOverallMinutes, hours);
|
||||
shell.WriteLine(Loc.GetString("babyjail-command-overall-minutes-set", ("hours", hours)));
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,7 @@ namespace Content.Server.Administration.Systems
|
||||
|
||||
private readonly HashSet<NetUserId> _roundActivePlayers = new();
|
||||
public readonly PanicBunkerStatus PanicBunker = new();
|
||||
public readonly BabyJailStatus BabyJail = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -71,6 +72,7 @@ namespace Content.Server.Administration.Systems
|
||||
_adminManager.OnPermsChanged += OnAdminPermsChanged;
|
||||
_playTime.SessionPlayTimeUpdated += OnSessionPlayTimeUpdated;
|
||||
|
||||
// Panic Bunker Settings
|
||||
Subs.CVar(_config, CCVars.PanicBunkerEnabled, OnPanicBunkerChanged, true);
|
||||
Subs.CVar(_config, CCVars.PanicBunkerDisableWithAdmins, OnPanicBunkerDisableWithAdminsChanged, true);
|
||||
Subs.CVar(_config, CCVars.PanicBunkerEnableWithoutAdmins, OnPanicBunkerEnableWithoutAdminsChanged, true);
|
||||
@@ -80,6 +82,16 @@ namespace Content.Server.Administration.Systems
|
||||
Subs.CVar(_config, CCVars.PanicBunkerMinOverallMinutes, OnPanicBunkerMinOverallMinutesChanged, true);
|
||||
Subs.CVar(_config, CCCVars.PanicBunkerDenyVPN, OnPanicBunkerDenyVpnChanged, true); // Corvax-VPNGuard
|
||||
|
||||
/*
|
||||
* TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
|
||||
// Baby Jail Settings
|
||||
Subs.CVar(_config, CCVars.BabyJailEnabled, OnBabyJailChanged, true);
|
||||
Subs.CVar(_config, CCVars.BabyJailShowReason, OnBabyJailShowReasonChanged, true);
|
||||
Subs.CVar(_config, CCVars.BabyJailMaxAccountAge, OnBabyJailMaxAccountAgeChanged, true);
|
||||
Subs.CVar(_config, CCVars.BabyJailMaxOverallMinutes, OnBabyJailMaxOverallMinutesChanged, true);
|
||||
|
||||
SubscribeLocalEvent<IdentityChangedEvent>(OnIdentityChanged);
|
||||
SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<PlayerDetachedEvent>(OnPlayerDetached);
|
||||
@@ -252,6 +264,17 @@ namespace Content.Server.Administration.Systems
|
||||
SendPanicBunkerStatusAll();
|
||||
}
|
||||
|
||||
private void OnBabyJailChanged(bool enabled)
|
||||
{
|
||||
BabyJail.Enabled = enabled;
|
||||
_chat.SendAdminAlert(Loc.GetString(enabled
|
||||
? "admin-ui-baby-jail-enabled-admin-alert"
|
||||
: "admin-ui-baby-jail-disabled-admin-alert"
|
||||
));
|
||||
|
||||
SendBabyJailStatusAll();
|
||||
}
|
||||
|
||||
private void OnPanicBunkerDisableWithAdminsChanged(bool enabled)
|
||||
{
|
||||
PanicBunker.DisableWithAdmins = enabled;
|
||||
@@ -276,18 +299,36 @@ namespace Content.Server.Administration.Systems
|
||||
SendPanicBunkerStatusAll();
|
||||
}
|
||||
|
||||
private void OnBabyJailShowReasonChanged(bool enabled)
|
||||
{
|
||||
BabyJail.ShowReason = enabled;
|
||||
SendBabyJailStatusAll();
|
||||
}
|
||||
|
||||
private void OnPanicBunkerMinAccountAgeChanged(int minutes)
|
||||
{
|
||||
PanicBunker.MinAccountAgeMinutes = minutes;
|
||||
SendPanicBunkerStatusAll();
|
||||
}
|
||||
|
||||
private void OnBabyJailMaxAccountAgeChanged(int minutes)
|
||||
{
|
||||
BabyJail.MaxAccountAgeMinutes = minutes;
|
||||
SendBabyJailStatusAll();
|
||||
}
|
||||
|
||||
private void OnPanicBunkerMinOverallMinutesChanged(int minutes)
|
||||
{
|
||||
PanicBunker.MinOverallMinutes = minutes;
|
||||
SendPanicBunkerStatusAll();
|
||||
}
|
||||
|
||||
private void OnBabyJailMaxOverallMinutesChanged(int minutes)
|
||||
{
|
||||
BabyJail.MaxOverallMinutes = minutes;
|
||||
SendBabyJailStatusAll();
|
||||
}
|
||||
|
||||
// Corvax-VPNGuard-Start
|
||||
private void OnPanicBunkerDenyVpnChanged(bool deny)
|
||||
{
|
||||
@@ -337,6 +378,15 @@ namespace Content.Server.Administration.Systems
|
||||
}
|
||||
}
|
||||
|
||||
private void SendBabyJailStatusAll()
|
||||
{
|
||||
var ev = new BabyJailChangedEvent(BabyJail);
|
||||
foreach (var admin in _adminManager.AllAdmins)
|
||||
{
|
||||
RaiseNetworkEvent(ev, admin);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Erases a player from the round.
|
||||
/// This removes them and any trace of them from the round, deleting their
|
||||
|
||||
@@ -39,7 +39,8 @@ public sealed class AntagRandomObjectivesSystem : EntitySystem
|
||||
|
||||
for (var pick = 0; pick < set.MaxPicks && ent.Comp.MaxDifficulty > difficulty; pick++)
|
||||
{
|
||||
if (_objectives.GetRandomObjective(mindId, mind, set.Groups) is not {} objective)
|
||||
var remainingDifficulty = ent.Comp.MaxDifficulty - difficulty;
|
||||
if (_objectives.GetRandomObjective(mindId, mind, set.Groups, remainingDifficulty) is not { } objective)
|
||||
continue;
|
||||
|
||||
_mind.AddObjective(mindId, mind, objective);
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if (component.LastPosition.HasValue)
|
||||
{
|
||||
// Check if position is out of range => don't update and disable
|
||||
if (!component.LastPosition.Value.InRange(EntityManager, _transform, userPos, SharedInteractionSystem.InteractionRange))
|
||||
if (!_transform.InRange(component.LastPosition.Value, userPos, SharedInteractionSystem.InteractionRange))
|
||||
{
|
||||
if (component.User is { } userId && component.Enabled)
|
||||
_popup.PopupEntity(Loc.GetString("gas-analyzer-shutoff"), userId, userId);
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
TankPressure = component.Air?.Pressure ?? 0,
|
||||
OutputPressure = initialUpdate ? component.OutputPressure : null,
|
||||
InternalsConnected = component.IsConnected,
|
||||
CanConnectInternals = CanConnectToInternals(component)
|
||||
CanConnectInternals = CanConnectToInternals(ent)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -217,24 +217,24 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
return air;
|
||||
}
|
||||
|
||||
public bool CanConnectToInternals(GasTankComponent component)
|
||||
public bool CanConnectToInternals(Entity<GasTankComponent> ent)
|
||||
{
|
||||
var internals = GetInternalsComponent(component, component.User);
|
||||
return internals != null && internals.BreathTools.Count != 0 && !component.IsValveOpen;
|
||||
TryGetInternalsComp(ent, out _, out var internalsComp, ent.Comp.User);
|
||||
return internalsComp != null && internalsComp.BreathTools.Count != 0 && !ent.Comp.IsValveOpen;
|
||||
}
|
||||
|
||||
public void ConnectToInternals(Entity<GasTankComponent> ent)
|
||||
{
|
||||
var (owner, component) = ent;
|
||||
if (component.IsConnected || !CanConnectToInternals(component))
|
||||
if (component.IsConnected || !CanConnectToInternals(ent))
|
||||
return;
|
||||
|
||||
var internals = GetInternalsComponent(component);
|
||||
if (internals == null)
|
||||
TryGetInternalsComp(ent, out var internalsUid, out var internalsComp, ent.Comp.User);
|
||||
if (internalsUid == null || internalsComp == null)
|
||||
return;
|
||||
|
||||
if (_internals.TryConnectTank((internals.Owner, internals), owner))
|
||||
component.User = internals.Owner;
|
||||
if (_internals.TryConnectTank((internalsUid.Value, internalsComp), owner))
|
||||
component.User = internalsUid.Value;
|
||||
|
||||
_actions.SetToggled(component.ToggleActionEntity, component.IsConnected);
|
||||
|
||||
@@ -243,7 +243,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
return;
|
||||
|
||||
component.ConnectStream = _audioSys.Stop(component.ConnectStream);
|
||||
component.ConnectStream = _audioSys.PlayPvs(component.ConnectSound, component.Owner)?.Entity;
|
||||
component.ConnectStream = _audioSys.PlayPvs(component.ConnectSound, owner)?.Entity;
|
||||
|
||||
UpdateUserInterface(ent);
|
||||
}
|
||||
@@ -251,29 +251,59 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
public void DisconnectFromInternals(Entity<GasTankComponent> ent)
|
||||
{
|
||||
var (owner, component) = ent;
|
||||
|
||||
if (component.User == null)
|
||||
return;
|
||||
|
||||
var internals = GetInternalsComponent(component);
|
||||
TryGetInternalsComp(ent, out var internalsUid, out var internalsComp, component.User);
|
||||
component.User = null;
|
||||
|
||||
_actions.SetToggled(component.ToggleActionEntity, false);
|
||||
|
||||
_internals.DisconnectTank(internals);
|
||||
if (internalsUid != null && internalsComp != null)
|
||||
_internals.DisconnectTank((internalsUid.Value, internalsComp));
|
||||
component.DisconnectStream = _audioSys.Stop(component.DisconnectStream);
|
||||
component.DisconnectStream = _audioSys.PlayPvs(component.DisconnectSound, component.Owner)?.Entity;
|
||||
component.DisconnectStream = _audioSys.PlayPvs(component.DisconnectSound, owner)?.Entity;
|
||||
|
||||
UpdateUserInterface(ent);
|
||||
}
|
||||
|
||||
private InternalsComponent? GetInternalsComponent(GasTankComponent component, EntityUid? owner = null)
|
||||
/// <summary>
|
||||
/// Tries to retrieve the internals component of either the gas tank's user,
|
||||
/// or the gas tank's... containing container
|
||||
/// </summary>
|
||||
/// <param name="user">The user of the gas tank</param>
|
||||
/// <returns>True if internals comp isn't null, false if it is null</returns>
|
||||
private bool TryGetInternalsComp(Entity<GasTankComponent> ent, out EntityUid? internalsUid, out InternalsComponent? internalsComp, EntityUid? user = null)
|
||||
{
|
||||
owner ??= component.User;
|
||||
if (Deleted(component.Owner))return null;
|
||||
if (owner != null) return CompOrNull<InternalsComponent>(owner.Value);
|
||||
return _containers.TryGetContainingContainer(component.Owner, out var container)
|
||||
? CompOrNull<InternalsComponent>(container.Owner)
|
||||
: null;
|
||||
internalsUid = default;
|
||||
internalsComp = default;
|
||||
|
||||
// If the gas tank doesn't exist for whatever reason, don't even bother
|
||||
if (TerminatingOrDeleted(ent.Owner))
|
||||
return false;
|
||||
|
||||
user ??= ent.Comp.User;
|
||||
// Check if the gas tank's user actually has the component that allows them to use a gas tank and mask
|
||||
if (TryComp<InternalsComponent>(user, out var userInternalsComp) && userInternalsComp != null)
|
||||
{
|
||||
internalsUid = user;
|
||||
internalsComp = userInternalsComp;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Yeah I have no clue what this actually does, I appreciate the lack of comments on the original function
|
||||
if (_containers.TryGetContainingContainer((ent.Owner, Transform(ent.Owner)), out var container) && container != null)
|
||||
{
|
||||
if (TryComp<InternalsComponent>(container.Owner, out var containerInternalsComp) && containerInternalsComp != null)
|
||||
{
|
||||
internalsUid = container.Owner;
|
||||
internalsComp = containerInternalsComp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void AssumeAir(Entity<GasTankComponent> ent, GasMixture giver)
|
||||
@@ -321,7 +351,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if(environment != null)
|
||||
_atmosphereSystem.Merge(environment, component.Air);
|
||||
|
||||
_audioSys.PlayPvs(component.RuptureSound, Transform(component.Owner).Coordinates, AudioParams.Default.WithVariation(0.125f));
|
||||
_audioSys.PlayPvs(component.RuptureSound, Transform(owner).Coordinates, AudioParams.Default.WithVariation(0.125f));
|
||||
|
||||
QueueDel(owner);
|
||||
return;
|
||||
|
||||
@@ -132,10 +132,19 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
var ev = new AtmosDeviceUpdateEvent(_atmosphereSystem.AtmosTime, null, null);
|
||||
foreach (var device in _joinedDevices)
|
||||
{
|
||||
DebugTools.Assert(!HasComp<GridAtmosphereComponent>(Transform(device).GridUid));
|
||||
var deviceGrid = Transform(device).GridUid;
|
||||
if (HasComp<GridAtmosphereComponent>(deviceGrid))
|
||||
{
|
||||
RejoinAtmosphere(device);
|
||||
}
|
||||
RaiseLocalEvent(device, ref ev);
|
||||
device.Comp.LastProcess = time;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsJoinedOffGrid(Entity<AtmosDeviceComponent> device)
|
||||
{
|
||||
return _joinedDevices.Contains(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ public sealed class InternalsSystem : EntitySystem
|
||||
{
|
||||
if (force)
|
||||
{
|
||||
DisconnectTank(internals);
|
||||
DisconnectTank((uid, internals));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -199,16 +199,13 @@ public sealed class InternalsSystem : EntitySystem
|
||||
_alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent));
|
||||
}
|
||||
|
||||
public void DisconnectTank(InternalsComponent? component)
|
||||
public void DisconnectTank(Entity<InternalsComponent> ent)
|
||||
{
|
||||
if (component is null)
|
||||
return;
|
||||
if (TryComp(ent.Comp.GasTankEntity, out GasTankComponent? tank))
|
||||
_gasTank.DisconnectFromInternals((ent.Comp.GasTankEntity.Value, tank));
|
||||
|
||||
if (TryComp(component.GasTankEntity, out GasTankComponent? tank))
|
||||
_gasTank.DisconnectFromInternals((component.GasTankEntity.Value, tank));
|
||||
|
||||
component.GasTankEntity = null;
|
||||
_alerts.ShowAlert(component.Owner, component.InternalsAlert, GetSeverity(component));
|
||||
ent.Comp.GasTankEntity = null;
|
||||
_alerts.ShowAlert(ent.Owner, ent.Comp.InternalsAlert, GetSeverity(ent.Comp));
|
||||
}
|
||||
|
||||
public bool TryConnectTank(Entity<InternalsComponent> ent, EntityUid tankEntity)
|
||||
@@ -267,21 +264,21 @@ public sealed class InternalsSystem : EntitySystem
|
||||
|
||||
if (_inventory.TryGetSlotEntity(user, "back", out var backEntity, user.Comp2, user.Comp3) &&
|
||||
TryComp<GasTankComponent>(backEntity, out var backGasTank) &&
|
||||
_gasTank.CanConnectToInternals(backGasTank))
|
||||
_gasTank.CanConnectToInternals((backEntity.Value, backGasTank)))
|
||||
{
|
||||
return (backEntity.Value, backGasTank);
|
||||
}
|
||||
|
||||
if (_inventory.TryGetSlotEntity(user, "suitstorage", out var entity, user.Comp2, user.Comp3) &&
|
||||
TryComp<GasTankComponent>(entity, out var gasTank) &&
|
||||
_gasTank.CanConnectToInternals(gasTank))
|
||||
_gasTank.CanConnectToInternals((entity.Value, gasTank)))
|
||||
{
|
||||
return (entity.Value, gasTank);
|
||||
}
|
||||
|
||||
foreach (var item in _inventory.GetHandOrInventoryEntities((user.Owner, user.Comp1, user.Comp2)))
|
||||
{
|
||||
if (TryComp(item, out gasTank) && _gasTank.CanConnectToInternals(gasTank))
|
||||
if (TryComp(item, out gasTank) && _gasTank.CanConnectToInternals((item, gasTank)))
|
||||
return (item, gasTank);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace Content.Server.Cargo.Systems
|
||||
SubscribeLocalEvent<CargoOrderConsoleComponent, BoundUIOpenedEvent>(OnOrderUIOpened);
|
||||
SubscribeLocalEvent<CargoOrderConsoleComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<CargoOrderConsoleComponent, InteractUsingEvent>(OnInteractUsing);
|
||||
SubscribeLocalEvent<CargoOrderConsoleComponent, BankBalanceUpdatedEvent>(OnOrderBalanceUpdated);
|
||||
Reset();
|
||||
}
|
||||
|
||||
@@ -315,6 +316,15 @@ namespace Content.Server.Cargo.Systems
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
private void OnOrderBalanceUpdated(Entity<CargoOrderConsoleComponent> ent, ref BankBalanceUpdatedEvent args)
|
||||
{
|
||||
if (!_uiSystem.IsUiOpen(ent.Owner, CargoConsoleUiKey.Orders))
|
||||
return;
|
||||
|
||||
UpdateOrderState(ent, args.Station);
|
||||
}
|
||||
|
||||
private void UpdateOrderState(EntityUid consoleUid, EntityUid? station)
|
||||
{
|
||||
if (station == null ||
|
||||
|
||||
@@ -81,18 +81,18 @@ public sealed partial class CargoSystem : SharedCargoSystem
|
||||
public void UpdateBankAccount(EntityUid uid, StationBankAccountComponent component, int balanceAdded)
|
||||
{
|
||||
component.Balance += balanceAdded;
|
||||
var query = EntityQueryEnumerator<CargoOrderConsoleComponent>();
|
||||
var query = EntityQueryEnumerator<BankClientComponent, TransformComponent>();
|
||||
|
||||
while (query.MoveNext(out var oUid, out var _))
|
||||
var ev = new BankBalanceUpdatedEvent(uid, component.Balance);
|
||||
while (query.MoveNext(out var client, out var comp, out var xform))
|
||||
{
|
||||
if (!_uiSystem.IsUiOpen(oUid, CargoConsoleUiKey.Orders))
|
||||
continue;
|
||||
|
||||
var station = _station.GetOwningStation(oUid);
|
||||
var station = _station.GetOwningStation(client, xform);
|
||||
if (station != uid)
|
||||
continue;
|
||||
|
||||
UpdateOrderState(oUid, station);
|
||||
comp.Balance = component.Balance;
|
||||
Dirty(client, comp);
|
||||
RaiseLocalEvent(client, ref ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Content.Server.Charges.Components;
|
||||
/// Something with limited charges that can be recharged automatically.
|
||||
/// Requires LimitedChargesComponent to function.
|
||||
/// </summary>
|
||||
// TODO: no reason this cant be predicted and server system deleted
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
[Access(typeof(ChargesSystem))]
|
||||
public sealed partial class AutoRechargeComponent : Component
|
||||
|
||||
@@ -37,15 +37,17 @@ public sealed class ChargesSystem : SharedChargesSystem
|
||||
args.PushMarkup(Loc.GetString("limited-charges-recharging", ("seconds", timeRemaining)));
|
||||
}
|
||||
|
||||
public override void UseCharge(EntityUid uid, LimitedChargesComponent? comp = null)
|
||||
public override void AddCharges(EntityUid uid, int change, LimitedChargesComponent? comp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp, false))
|
||||
if (!Query.Resolve(uid, ref comp, false))
|
||||
return;
|
||||
|
||||
var startRecharge = comp.Charges == comp.MaxCharges;
|
||||
base.UseCharge(uid, comp);
|
||||
// start the recharge time after first use at full charge
|
||||
if (startRecharge && TryComp<AutoRechargeComponent>(uid, out var recharge))
|
||||
base.AddCharges(uid, change, comp);
|
||||
|
||||
// if a charge was just used from full, start the recharge timer
|
||||
// TODO: probably make this an event instead of having le server system that just does this
|
||||
if (change < 0 && startRecharge && TryComp<AutoRechargeComponent>(uid, out var recharge))
|
||||
recharge.NextChargeTime = _timing.CurTime + recharge.RechargeDuration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ public sealed class AnnounceOnSpawnSystem : EntitySystem
|
||||
private void OnInit(EntityUid uid, AnnounceOnSpawnComponent comp, MapInitEvent args)
|
||||
{
|
||||
var message = Loc.GetString(comp.Message);
|
||||
var sender = comp.Sender != null ? Loc.GetString(comp.Sender) : "Central Command";
|
||||
var sender = comp.Sender != null ? Loc.GetString(comp.Sender) : Loc.GetString("chat-manager-sender-announcement");
|
||||
_chat.DispatchGlobalAnnouncement(message, sender, playSound: true, comp.Sound, comp.Color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,12 +322,14 @@ public sealed partial class ChatSystem : SharedChatSystem
|
||||
/// <param name="colorOverride">Optional color for the announcement message</param>
|
||||
public void DispatchGlobalAnnouncement(
|
||||
string message,
|
||||
string sender = "Central Command",
|
||||
string? sender = null,
|
||||
bool playSound = true,
|
||||
SoundSpecifier? announcementSound = null,
|
||||
Color? colorOverride = null
|
||||
)
|
||||
{
|
||||
sender ??= Loc.GetString("chat-manager-sender-announcement");
|
||||
|
||||
var wrappedMessage = Loc.GetString("chat-manager-sender-announcement-wrap-message", ("sender", sender), ("message", FormattedMessage.EscapeText(message)));
|
||||
_chatManager.ChatMessageToAll(ChatChannel.Radio, message, wrappedMessage, default, false, true, colorOverride);
|
||||
if (playSound)
|
||||
@@ -349,11 +351,13 @@ public sealed partial class ChatSystem : SharedChatSystem
|
||||
public void DispatchStationAnnouncement(
|
||||
EntityUid source,
|
||||
string message,
|
||||
string sender = "Central Command",
|
||||
string? sender = null,
|
||||
bool playDefaultSound = true,
|
||||
SoundSpecifier? announcementSound = null,
|
||||
Color? colorOverride = null)
|
||||
{
|
||||
sender ??= Loc.GetString("chat-manager-sender-announcement");
|
||||
|
||||
var wrappedMessage = Loc.GetString("chat-manager-sender-announcement-wrap-message", ("sender", sender), ("message", FormattedMessage.EscapeText(message)));
|
||||
var station = _stationSystem.GetOwningStation(source);
|
||||
|
||||
|
||||
42
Content.Server/Clock/ClockSystem.cs
Normal file
42
Content.Server/Clock/ClockSystem.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Content.Server.GameTicking.Events;
|
||||
using Content.Shared.Clock;
|
||||
using Content.Shared.Destructible;
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Clock;
|
||||
|
||||
public sealed class ClockSystem : SharedClockSystem
|
||||
{
|
||||
[Dependency] private readonly PvsOverrideSystem _pvsOverride = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RoundStartingEvent>(OnRoundStart);
|
||||
SubscribeLocalEvent<GlobalTimeManagerComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<ClockComponent, BreakageEventArgs>(OnBreak);
|
||||
}
|
||||
|
||||
private void OnRoundStart(RoundStartingEvent ev)
|
||||
{
|
||||
var manager = Spawn();
|
||||
AddComp<GlobalTimeManagerComponent>(manager);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<GlobalTimeManagerComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
ent.Comp.TimeOffset = TimeSpan.FromHours(_robustRandom.NextFloat(0, 24));
|
||||
_pvsOverride.AddGlobalOverride(ent);
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
private void OnBreak(Entity<ClockComponent> ent, ref BreakageEventArgs args)
|
||||
{
|
||||
ent.Comp.StuckTime = GetClockTime(ent);
|
||||
Dirty(ent, ent.Comp);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,9 @@ using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
/*
|
||||
* TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
|
||||
namespace Content.Server.Connection
|
||||
{
|
||||
@@ -274,6 +277,14 @@ namespace Content.Server.Connection
|
||||
}
|
||||
}
|
||||
|
||||
if (_cfg.GetCVar(CCVars.BabyJailEnabled) && adminData == null)
|
||||
{
|
||||
var result = await IsInvalidConnectionDueToBabyJail(userId, e);
|
||||
|
||||
if (result.IsInvalid)
|
||||
return (ConnectionDenyReason.BabyJail, result.Reason, null);
|
||||
}
|
||||
|
||||
var wasInGame = EntitySystem.TryGet<GameTicker>(out var ticker) &&
|
||||
ticker.PlayerGameStatuses.TryGetValue(userId, out var status) &&
|
||||
status == PlayerGameStatus.JoinedGame;
|
||||
@@ -306,6 +317,61 @@ namespace Content.Server.Connection
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<(bool IsInvalid, string Reason)> IsInvalidConnectionDueToBabyJail(NetUserId userId, NetConnectingArgs e)
|
||||
{
|
||||
// If you're whitelisted then bypass this whole thing
|
||||
if (await _db.GetWhitelistStatusAsync(userId))
|
||||
return (false, "");
|
||||
|
||||
// Initial cvar retrieval
|
||||
var showReason = _cfg.GetCVar(CCVars.BabyJailShowReason);
|
||||
var reason = _cfg.GetCVar(CCVars.BabyJailCustomReason);
|
||||
var maxAccountAgeMinutes = _cfg.GetCVar(CCVars.BabyJailMaxAccountAge);
|
||||
var maxPlaytimeMinutes = _cfg.GetCVar(CCVars.BabyJailMaxOverallMinutes);
|
||||
|
||||
// Wait some time to lookup data
|
||||
var record = await _dbManager.GetPlayerRecordByUserId(userId);
|
||||
|
||||
// No player record = new account or the DB is having a skill issue
|
||||
if (record == null)
|
||||
return (false, "");
|
||||
|
||||
var isAccountAgeInvalid = record.FirstSeenTime.CompareTo(DateTimeOffset.Now - TimeSpan.FromMinutes(maxAccountAgeMinutes)) <= 0;
|
||||
if (isAccountAgeInvalid && showReason)
|
||||
{
|
||||
var locAccountReason = reason != string.Empty
|
||||
? reason
|
||||
: Loc.GetString("baby-jail-account-denied-reason",
|
||||
("reason",
|
||||
Loc.GetString(
|
||||
"baby-jail-account-reason-account",
|
||||
("minutes", maxAccountAgeMinutes))));
|
||||
|
||||
return (true, locAccountReason);
|
||||
}
|
||||
|
||||
var overallTime = ( await _db.GetPlayTimes(e.UserId)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall);
|
||||
var isTotalPlaytimeInvalid = overallTime == null || overallTime.TimeSpent.TotalMinutes >= maxPlaytimeMinutes;
|
||||
|
||||
if (isTotalPlaytimeInvalid && showReason)
|
||||
{
|
||||
var locPlaytimeReason = reason != string.Empty
|
||||
? reason
|
||||
: Loc.GetString("baby-jail-account-denied-reason",
|
||||
("reason",
|
||||
Loc.GetString(
|
||||
"baby-jail-account-reason-overall",
|
||||
("minutes", maxPlaytimeMinutes))));
|
||||
|
||||
return (true, locPlaytimeReason);
|
||||
}
|
||||
|
||||
if (!showReason && isTotalPlaytimeInvalid || isAccountAgeInvalid)
|
||||
return (true, Loc.GetString("baby-jail-account-denied"));
|
||||
|
||||
return (false, "");
|
||||
}
|
||||
|
||||
private bool HasTemporaryBypass(NetUserId user)
|
||||
{
|
||||
return _temporaryBypasses.TryGetValue(user, out var time) && time > _gameTiming.RealTime;
|
||||
|
||||
@@ -146,7 +146,7 @@ public sealed partial class DragonSystem : EntitySystem
|
||||
// cant stack rifts near eachother
|
||||
foreach (var (_, riftXform) in EntityQuery<DragonRiftComponent, TransformComponent>(true))
|
||||
{
|
||||
if (riftXform.Coordinates.InRange(EntityManager, _transform, xform.Coordinates, RiftRange))
|
||||
if (_transform.InRange(riftXform.Coordinates, xform.Coordinates, RiftRange))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("carp-rift-proximity", ("proximity", RiftRange)), uid, uid);
|
||||
return;
|
||||
|
||||
@@ -65,8 +65,8 @@ namespace Content.Server.Engineering.EntitySystems
|
||||
|
||||
EntityManager.SpawnEntity(component.Prototype, args.ClickLocation.SnapToGrid(grid));
|
||||
|
||||
if (component.RemoveOnInteract && stackComp == null && !((!EntityManager.EntityExists(uid) ? EntityLifeStage.Deleted : EntityManager.GetComponent<MetaDataComponent>(component.Owner).EntityLifeStage) >= EntityLifeStage.Deleted))
|
||||
EntityManager.DeleteEntity(uid);
|
||||
if (component.RemoveOnInteract && stackComp == null)
|
||||
TryQueueDel(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using Content.Server.Explosion.Components;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
@@ -42,14 +42,15 @@ public sealed partial class TriggerSystem
|
||||
|
||||
private void UpdateTimedCollide(float frameTime)
|
||||
{
|
||||
foreach (var (activeTrigger, triggerOnTimedCollide) in EntityQuery<ActiveTriggerOnTimedCollideComponent, TriggerOnTimedCollideComponent>())
|
||||
var query = EntityQueryEnumerator<ActiveTriggerOnTimedCollideComponent, TriggerOnTimedCollideComponent>();
|
||||
while (query.MoveNext(out var uid, out _, out var triggerOnTimedCollide))
|
||||
{
|
||||
foreach (var (collidingEntity, collidingTimer) in triggerOnTimedCollide.Colliding)
|
||||
{
|
||||
triggerOnTimedCollide.Colliding[collidingEntity] += frameTime;
|
||||
if (collidingTimer > triggerOnTimedCollide.Threshold)
|
||||
{
|
||||
RaiseLocalEvent(activeTrigger.Owner, new TriggerEvent(activeTrigger.Owner, collidingEntity), true);
|
||||
RaiseLocalEvent(uid, new TriggerEvent(uid, collidingEntity), true);
|
||||
triggerOnTimedCollide.Colliding[collidingEntity] -= triggerOnTimedCollide.Threshold;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
using Content.Shared.Extinguisher;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Server.Extinguisher;
|
||||
|
||||
[RegisterComponent]
|
||||
[Access(typeof(FireExtinguisherSystem))]
|
||||
public sealed partial class FireExtinguisherComponent : SharedFireExtinguisherComponent
|
||||
{
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.Fluids.EntitySystems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Extinguisher;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
namespace Content.Server.Extinguisher;
|
||||
|
||||
public sealed class FireExtinguisherSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<FireExtinguisherComponent, ComponentInit>(OnFireExtinguisherInit);
|
||||
SubscribeLocalEvent<FireExtinguisherComponent, UseInHandEvent>(OnUseInHand);
|
||||
SubscribeLocalEvent<FireExtinguisherComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<FireExtinguisherComponent, GetVerbsEvent<InteractionVerb>>(OnGetInteractionVerbs);
|
||||
SubscribeLocalEvent<FireExtinguisherComponent, SprayAttemptEvent>(OnSprayAttempt);
|
||||
}
|
||||
|
||||
private void OnFireExtinguisherInit(Entity<FireExtinguisherComponent> entity, ref ComponentInit args)
|
||||
{
|
||||
if (entity.Comp.HasSafety)
|
||||
{
|
||||
UpdateAppearance((entity.Owner, entity.Comp));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUseInHand(Entity<FireExtinguisherComponent> entity, ref UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
ToggleSafety((entity.Owner, entity.Comp), args.User);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnAfterInteract(Entity<FireExtinguisherComponent> entity, ref AfterInteractEvent args)
|
||||
{
|
||||
if (args.Target == null || !args.CanReach)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (entity.Comp.HasSafety && entity.Comp.Safety)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-safety-on-message"), entity.Owner, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Target is not { Valid: true } target ||
|
||||
!_solutionContainerSystem.TryGetDrainableSolution(target, out var targetSoln, out var targetSolution) ||
|
||||
!_solutionContainerSystem.TryGetRefillableSolution(entity.Owner, out var containerSoln, out var containerSolution))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
// TODO: why is this copy paste shit here just have fire extinguisher cancel transfer when safety is on
|
||||
var transfer = containerSolution.AvailableVolume;
|
||||
if (TryComp<SolutionTransferComponent>(entity.Owner, out var solTrans))
|
||||
{
|
||||
transfer = solTrans.TransferAmount;
|
||||
}
|
||||
transfer = FixedPoint2.Min(transfer, targetSolution.Volume);
|
||||
|
||||
if (transfer > 0)
|
||||
{
|
||||
var drained = _solutionContainerSystem.Drain(target, targetSoln.Value, transfer);
|
||||
_solutionContainerSystem.TryAddSolution(containerSoln.Value, drained);
|
||||
|
||||
_audio.PlayPvs(entity.Comp.RefillSound, entity.Owner);
|
||||
_popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-after-interact-refilled-message", ("owner", entity.Owner)),
|
||||
entity.Owner, args.Target.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGetInteractionVerbs(Entity<FireExtinguisherComponent> entity, ref GetVerbsEvent<InteractionVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
var user = args.User;
|
||||
var verb = new InteractionVerb
|
||||
{
|
||||
Act = () => ToggleSafety((entity.Owner, entity.Comp), user),
|
||||
Text = Loc.GetString("fire-extinguisher-component-verb-text"),
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private void OnSprayAttempt(Entity<FireExtinguisherComponent> entity, ref SprayAttemptEvent args)
|
||||
{
|
||||
if (entity.Comp.HasSafety && entity.Comp.Safety)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-safety-on-message"), entity, args.User);
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAppearance(Entity<FireExtinguisherComponent, AppearanceComponent?> entity)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp2, false))
|
||||
return;
|
||||
|
||||
if (entity.Comp1.HasSafety)
|
||||
{
|
||||
_appearance.SetData(entity, FireExtinguisherVisuals.Safety, entity.Comp1.Safety, entity.Comp2);
|
||||
}
|
||||
}
|
||||
|
||||
public void ToggleSafety(Entity<FireExtinguisherComponent?> extinguisher, EntityUid user)
|
||||
{
|
||||
if (!Resolve(extinguisher, ref extinguisher.Comp))
|
||||
return;
|
||||
|
||||
extinguisher.Comp.Safety = !extinguisher.Comp.Safety;
|
||||
_audio.PlayPvs(extinguisher.Comp.SafetySound, extinguisher, AudioParams.Default.WithVariation(0.125f).WithVolume(-4f));
|
||||
UpdateAppearance((extinguisher.Owner, extinguisher.Comp));
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.Chemistry.EntitySystems;
|
||||
using Content.Server.Extinguisher;
|
||||
using Content.Server.Fluids.Components;
|
||||
using Content.Server.Gravity;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Fluids;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Timing;
|
||||
using Content.Shared.Vapor;
|
||||
@@ -34,7 +34,7 @@ public sealed class SpraySystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SprayComponent, AfterInteractEvent>(OnAfterInteract, after: new[] { typeof(FireExtinguisherSystem) });
|
||||
SubscribeLocalEvent<SprayComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
}
|
||||
|
||||
private void OnAfterInteract(Entity<SprayComponent> entity, ref AfterInteractEvent args)
|
||||
@@ -48,7 +48,7 @@ public sealed class SpraySystem : EntitySystem
|
||||
return;
|
||||
|
||||
var ev = new SprayAttemptEvent(args.User);
|
||||
RaiseLocalEvent(entity, ev);
|
||||
RaiseLocalEvent(entity, ref ev);
|
||||
if (ev.Cancelled)
|
||||
return;
|
||||
|
||||
@@ -148,13 +148,3 @@ public sealed class SpraySystem : EntitySystem
|
||||
_useDelay.TryResetDelay((entity, useDelay));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SprayAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
public EntityUid User;
|
||||
|
||||
public SprayAttemptEvent(EntityUid user)
|
||||
{
|
||||
User = user;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,12 @@ namespace Content.Server.GameTicking
|
||||
jObject["players"] = players; // Corvax-Queue
|
||||
jObject["soft_max_players"] = _cfg.GetCVar(CCVars.SoftMaxPlayers);
|
||||
jObject["panic_bunker"] = _cfg.GetCVar(CCVars.PanicBunkerEnabled);
|
||||
|
||||
/*
|
||||
* TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
|
||||
jObject["baby_jail"] = _cfg.GetCVar(CCVars.BabyJailEnabled);
|
||||
jObject["run_level"] = (int) _runLevel;
|
||||
if (preset != null)
|
||||
jObject["preset"] = Loc.GetString(preset.ModeTitle);
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
using Content.Server.Ninja.Systems;
|
||||
using Content.Shared.Communications;
|
||||
using Content.Shared.Random;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Stores some configuration used by the ninja system.
|
||||
/// Objectives and roundend summary are handled by <see cref="GenericAntagRuleComponent"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(SpaceNinjaSystem))]
|
||||
public sealed partial class NinjaRuleComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// List of threats that can be called in. Copied onto <see cref="CommsHackerComponent"/> when gloves are enabled.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public ProtoId<WeightedRandomPrototype> Threats = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Sound played when making the player a ninja via antag control or ghost role
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundSpecifier? GreetingSound = new SoundPathSpecifier("/Audio/Misc/ninja_greeting.ogg");
|
||||
}
|
||||
@@ -325,7 +325,7 @@ namespace Content.Server.Guardian
|
||||
if (!guardianComponent.GuardianLoose)
|
||||
return;
|
||||
|
||||
if (!guardianXform.Coordinates.InRange(EntityManager, _transform, hostXform.Coordinates, guardianComponent.DistanceAllowed))
|
||||
if (!_transform.InRange(guardianXform.Coordinates, hostXform.Coordinates, guardianComponent.DistanceAllowed))
|
||||
RetractGuardian(hostUid, hostComponent, guardianUid, guardianComponent);
|
||||
}
|
||||
|
||||
|
||||
@@ -402,7 +402,8 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
|
||||
|
||||
var trans = transformQuery.GetComponent(uid);
|
||||
var masterTrans = transformQuery.GetComponent(master);
|
||||
if (!masterTrans.Coordinates.InRange(EntityManager, _transform, trans.Coordinates, 10f))
|
||||
if (!_transform.InRange(masterTrans.Coordinates, trans.Coordinates, 10f)
|
||||
)
|
||||
{
|
||||
Clean(uid, instrument);
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
namespace Content.Server.Item;
|
||||
|
||||
/// <summary>
|
||||
/// Handles whether this item applies a disarm malus when active.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class ItemToggleDisarmMalusComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Item has this modifier to the chance to disarm when activated.
|
||||
/// If null, the value will be inferred from the current malus just before the malus is first deactivated.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadOnly), DataField]
|
||||
public float? ActivatedDisarmMalus = null;
|
||||
|
||||
/// <summary>
|
||||
/// Item has this modifier to the chance to disarm when deactivated. If none is mentioned, it uses the item's default disarm modifier.
|
||||
/// If null, the value will be inferred from the current malus just before the malus is first activated.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadOnly), DataField]
|
||||
public float? DeactivatedDisarmMalus = null;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Content.Server.Item;
|
||||
|
||||
/// <summary>
|
||||
/// Handles whether this item is sharp when toggled on.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class ItemToggleSharpComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
using Content.Server.CombatMode.Disarm;
|
||||
using Content.Server.Kitchen.Components;
|
||||
using Content.Shared.Item.ItemToggle;
|
||||
using Content.Shared.Item.ItemToggle.Components;
|
||||
|
||||
namespace Content.Server.Item;
|
||||
|
||||
public sealed class ItemToggleSystem : SharedItemToggleSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ItemToggleSharpComponent, ItemToggledEvent>(ToggleSharp);
|
||||
SubscribeLocalEvent<ItemToggleDisarmMalusComponent, ItemToggledEvent>(ToggleMalus);
|
||||
}
|
||||
|
||||
private void ToggleSharp(Entity<ItemToggleSharpComponent> ent, ref ItemToggledEvent args)
|
||||
{
|
||||
// TODO generalize this into a "ToggleComponentComponent", though probably with a better name
|
||||
if (args.Activated)
|
||||
EnsureComp<SharpComponent>(ent);
|
||||
else
|
||||
RemCompDeferred<SharpComponent>(ent);
|
||||
}
|
||||
|
||||
private void ToggleMalus(Entity<ItemToggleDisarmMalusComponent> ent, ref ItemToggledEvent args)
|
||||
{
|
||||
if (!TryComp<DisarmMalusComponent>(ent, out var malus))
|
||||
return;
|
||||
|
||||
if (args.Activated)
|
||||
{
|
||||
ent.Comp.DeactivatedDisarmMalus ??= malus.Malus;
|
||||
if (ent.Comp.ActivatedDisarmMalus is {} activatedMalus)
|
||||
malus.Malus = activatedMalus;
|
||||
return;
|
||||
}
|
||||
|
||||
ent.Comp.ActivatedDisarmMalus ??= malus.Malus;
|
||||
if (ent.Comp.DeactivatedDisarmMalus is {} deactivatedMalus)
|
||||
malus.Malus = deactivatedMalus;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,9 @@ namespace Content.Server.Medical.Components;
|
||||
/// <summary>
|
||||
/// After scanning, retrieves the target Uid to use with its related UI.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Requires <c>ItemToggleComponent</c>.
|
||||
/// </remarks>
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
[Access(typeof(HealthAnalyzerSystem), typeof(CryoPodSystem))]
|
||||
public sealed partial class HealthAnalyzerComponent : Component
|
||||
|
||||
@@ -12,6 +12,7 @@ using Content.Shared.DoAfter;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Item.ItemToggle;
|
||||
using Content.Shared.Medical;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mobs;
|
||||
@@ -37,6 +38,7 @@ public sealed class DefibrillatorSystem : EntitySystem
|
||||
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly ElectrocutionSystem _electrocution = default!;
|
||||
[Dependency] private readonly EuiManager _euiManager = default!;
|
||||
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||
[Dependency] private readonly RottingSystem _rotting = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
|
||||
@@ -50,30 +52,10 @@ public sealed class DefibrillatorSystem : EntitySystem
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<DefibrillatorComponent, UseInHandEvent>(OnUseInHand);
|
||||
SubscribeLocalEvent<DefibrillatorComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
|
||||
SubscribeLocalEvent<DefibrillatorComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<DefibrillatorComponent, DefibrillatorZapDoAfterEvent>(OnDoAfter);
|
||||
}
|
||||
|
||||
private void OnUseInHand(EntityUid uid, DefibrillatorComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled || !TryComp(uid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((uid, useDelay)))
|
||||
return;
|
||||
|
||||
if (!TryToggle(uid, component, args.User))
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
_useDelay.TryResetDelay((uid, useDelay));
|
||||
}
|
||||
|
||||
private void OnPowerCellSlotEmpty(EntityUid uid, DefibrillatorComponent component, ref PowerCellSlotEmptyEvent args)
|
||||
{
|
||||
if (!TerminatingOrDeleted(uid))
|
||||
TryDisable(uid, component);
|
||||
}
|
||||
|
||||
private void OnAfterInteract(EntityUid uid, DefibrillatorComponent component, AfterInteractEvent args)
|
||||
{
|
||||
if (args.Handled || args.Target is not { } target)
|
||||
@@ -96,54 +78,12 @@ public sealed class DefibrillatorSystem : EntitySystem
|
||||
Zap(uid, target, args.User, component);
|
||||
}
|
||||
|
||||
public bool TryToggle(EntityUid uid, DefibrillatorComponent? component = null, EntityUid? user = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return false;
|
||||
|
||||
return component.Enabled
|
||||
? TryDisable(uid, component)
|
||||
: TryEnable(uid, component, user);
|
||||
}
|
||||
|
||||
public bool TryEnable(EntityUid uid, DefibrillatorComponent? component = null, EntityUid? user = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return false;
|
||||
|
||||
if (component.Enabled)
|
||||
return false;
|
||||
|
||||
if (!_powerCell.HasActivatableCharge(uid))
|
||||
return false;
|
||||
|
||||
component.Enabled = true;
|
||||
_appearance.SetData(uid, ToggleVisuals.Toggled, true);
|
||||
_audio.PlayPvs(component.PowerOnSound, uid);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryDisable(EntityUid uid, DefibrillatorComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return false;
|
||||
|
||||
if (!component.Enabled)
|
||||
return false;
|
||||
|
||||
component.Enabled = false;
|
||||
_appearance.SetData(uid, ToggleVisuals.Toggled, false);
|
||||
|
||||
_audio.PlayPvs(component.PowerOffSound, uid);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanZap(EntityUid uid, EntityUid target, EntityUid? user = null, DefibrillatorComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return false;
|
||||
|
||||
if (!component.Enabled)
|
||||
if (!_toggle.IsActivated(uid))
|
||||
{
|
||||
if (user != null)
|
||||
_popup.PopupEntity(Loc.GetString("defibrillator-not-on"), uid, user.Value);
|
||||
@@ -257,7 +197,7 @@ public sealed class DefibrillatorSystem : EntitySystem
|
||||
|
||||
// if we don't have enough power left for another shot, turn it off
|
||||
if (!_powerCell.HasActivatableCharge(uid))
|
||||
TryDisable(uid, component);
|
||||
_toggle.TryDeactivate(uid);
|
||||
|
||||
// TODO clean up this clown show above
|
||||
var ev = new TargetDefibrillatedEvent(user, (uid, component));
|
||||
|
||||
@@ -8,6 +8,8 @@ using Content.Shared.DoAfter;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Item.ItemToggle;
|
||||
using Content.Shared.Item.ItemToggle.Components;
|
||||
using Content.Shared.MedicalScanner;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Popups;
|
||||
@@ -26,6 +28,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
||||
[Dependency] private readonly PowerCellSystem _cell = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
@@ -36,7 +39,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
||||
SubscribeLocalEvent<HealthAnalyzerComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<HealthAnalyzerComponent, HealthAnalyzerDoAfterEvent>(OnDoAfter);
|
||||
SubscribeLocalEvent<HealthAnalyzerComponent, EntGotInsertedIntoContainerMessage>(OnInsertedIntoContainer);
|
||||
SubscribeLocalEvent<HealthAnalyzerComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
|
||||
SubscribeLocalEvent<HealthAnalyzerComponent, ItemToggledEvent>(OnToggled);
|
||||
SubscribeLocalEvent<HealthAnalyzerComponent, DroppedEvent>(OnDropped);
|
||||
}
|
||||
|
||||
@@ -62,7 +65,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
||||
|
||||
//Get distance between health analyzer and the scanned entity
|
||||
var patientCoordinates = Transform(patient).Coordinates;
|
||||
if (!patientCoordinates.InRange(EntityManager, _transformSystem, transform.Coordinates, component.MaxScanRange))
|
||||
if (!_transformSystem.InRange(patientCoordinates, transform.Coordinates, component.MaxScanRange))
|
||||
{
|
||||
//Range too far, disable updates
|
||||
StopAnalyzingEntity((uid, component), patient);
|
||||
@@ -111,16 +114,16 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
||||
private void OnInsertedIntoContainer(Entity<HealthAnalyzerComponent> uid, ref EntGotInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (uid.Comp.ScannedEntity is { } patient)
|
||||
StopAnalyzingEntity(uid, patient);
|
||||
_toggle.TryDeactivate(uid.Owner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disable continuous updates once battery is dead
|
||||
/// Disable continuous updates once turned off
|
||||
/// </summary>
|
||||
private void OnPowerCellSlotEmpty(Entity<HealthAnalyzerComponent> uid, ref PowerCellSlotEmptyEvent args)
|
||||
private void OnToggled(Entity<HealthAnalyzerComponent> ent, ref ItemToggledEvent args)
|
||||
{
|
||||
if (uid.Comp.ScannedEntity is { } patient)
|
||||
StopAnalyzingEntity(uid, patient);
|
||||
if (!args.Activated && ent.Comp.ScannedEntity is { } patient)
|
||||
StopAnalyzingEntity(ent, patient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -129,7 +132,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
||||
private void OnDropped(Entity<HealthAnalyzerComponent> uid, ref DroppedEvent args)
|
||||
{
|
||||
if (uid.Comp.ScannedEntity is { } patient)
|
||||
StopAnalyzingEntity(uid, patient);
|
||||
_toggle.TryDeactivate(uid.Owner);
|
||||
}
|
||||
|
||||
private void OpenUserInterface(EntityUid user, EntityUid analyzer)
|
||||
@@ -150,7 +153,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
||||
//Link the health analyzer to the scanned entity
|
||||
healthAnalyzer.Comp.ScannedEntity = target;
|
||||
|
||||
_cell.SetPowerCellDrawEnabled(healthAnalyzer, true);
|
||||
_toggle.TryActivate(healthAnalyzer.Owner);
|
||||
|
||||
UpdateScannedUser(healthAnalyzer, target, true);
|
||||
}
|
||||
@@ -165,7 +168,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
||||
//Unlink the analyzer
|
||||
healthAnalyzer.Comp.ScannedEntity = null;
|
||||
|
||||
_cell.SetPowerCellDrawEnabled(target, false);
|
||||
_toggle.TryDeactivate(healthAnalyzer.Owner);
|
||||
|
||||
UpdateScannedUser(healthAnalyzer, target, false);
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ public sealed class PullController : VirtualController
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedGravitySystem _gravity = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
|
||||
/// <summary>
|
||||
/// If distance between puller and pulled entity lower that this threshold,
|
||||
@@ -133,8 +134,8 @@ public sealed class PullController : VirtualController
|
||||
var range = 2f;
|
||||
var fromUserCoords = coords.WithEntityId(player, EntityManager);
|
||||
var userCoords = new EntityCoordinates(player, Vector2.Zero);
|
||||
|
||||
if (!coords.InRange(EntityManager, TransformSystem, userCoords, range))
|
||||
|
||||
if (!_transformSystem.InRange(coords, userCoords, range))
|
||||
{
|
||||
var direction = fromUserCoords.Position - userCoords.Position;
|
||||
|
||||
|
||||
@@ -8,12 +8,19 @@ namespace Content.Server.NPC.HTN.Preconditions;
|
||||
public sealed partial class CoordinatesInRangePrecondition : HTNPrecondition
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
private SharedTransformSystem _transformSystem = default!;
|
||||
|
||||
[DataField("targetKey", required: true)] public string TargetKey = default!;
|
||||
|
||||
[DataField("rangeKey", required: true)]
|
||||
public string RangeKey = default!;
|
||||
|
||||
public override void Initialize(IEntitySystemManager sysManager)
|
||||
{
|
||||
base.Initialize(sysManager);
|
||||
_transformSystem = sysManager.GetEntitySystem<SharedTransformSystem>();
|
||||
}
|
||||
|
||||
public override bool IsMet(NPCBlackboard blackboard)
|
||||
{
|
||||
if (!blackboard.TryGetValue<EntityCoordinates>(NPCBlackboard.OwnerCoordinates, out var coordinates, _entManager))
|
||||
@@ -22,6 +29,6 @@ public sealed partial class CoordinatesInRangePrecondition : HTNPrecondition
|
||||
if (!blackboard.TryGetValue<EntityCoordinates>(TargetKey, out var target, _entManager))
|
||||
return false;
|
||||
|
||||
return coordinates.InRange(_entManager, _entManager.System<SharedTransformSystem>(), target, blackboard.GetValueOrDefault<float>(RangeKey, _entManager));
|
||||
return _transformSystem.InRange(coordinates, target, blackboard.GetValueOrDefault<float>(RangeKey, _entManager));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,19 @@ namespace Content.Server.NPC.HTN.Preconditions;
|
||||
public sealed partial class CoordinatesNotInRangePrecondition : HTNPrecondition
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
private SharedTransformSystem _transformSystem = default!;
|
||||
|
||||
[DataField("targetKey", required: true)] public string TargetKey = default!;
|
||||
|
||||
[DataField("rangeKey", required: true)]
|
||||
public string RangeKey = default!;
|
||||
|
||||
public override void Initialize(IEntitySystemManager sysManager)
|
||||
{
|
||||
base.Initialize(sysManager);
|
||||
_transformSystem = sysManager.GetEntitySystem<SharedTransformSystem>();
|
||||
}
|
||||
|
||||
public override bool IsMet(NPCBlackboard blackboard)
|
||||
{
|
||||
if (!blackboard.TryGetValue<EntityCoordinates>(NPCBlackboard.OwnerCoordinates, out var coordinates, _entManager))
|
||||
@@ -22,7 +29,7 @@ public sealed partial class CoordinatesNotInRangePrecondition : HTNPrecondition
|
||||
if (!blackboard.TryGetValue<EntityCoordinates>(TargetKey, out var target, _entManager))
|
||||
return false;
|
||||
|
||||
return !coordinates.InRange(_entManager, _entManager.System<SharedTransformSystem>(), target, blackboard.GetValueOrDefault<float>(RangeKey, _entManager));
|
||||
return !_transformSystem.InRange(coordinates, target, blackboard.GetValueOrDefault<float>(RangeKey, _entManager));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,17 @@ namespace Content.Server.NPC.HTN.Preconditions;
|
||||
public sealed partial class TargetInRangePrecondition : HTNPrecondition
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
private SharedTransformSystem _transformSystem = default!;
|
||||
|
||||
[DataField("targetKey", required: true)] public string TargetKey = default!;
|
||||
|
||||
[DataField("rangeKey", required: true)]
|
||||
public string RangeKey = default!;
|
||||
public override void Initialize(IEntitySystemManager sysManager)
|
||||
{
|
||||
base.Initialize(sysManager);
|
||||
_transformSystem = sysManager.GetEntitySystem<SharedTransformSystem>();
|
||||
}
|
||||
|
||||
public override bool IsMet(NPCBlackboard blackboard)
|
||||
{
|
||||
@@ -23,6 +29,7 @@ public sealed partial class TargetInRangePrecondition : HTNPrecondition
|
||||
!_entManager.TryGetComponent<TransformComponent>(target, out var targetXform))
|
||||
return false;
|
||||
|
||||
return coordinates.InRange(_entManager, _entManager.System<SharedTransformSystem>(), targetXform.Coordinates, blackboard.GetValueOrDefault<float>(RangeKey, _entManager));
|
||||
var transformSystem = _entManager.System<SharedTransformSystem>;
|
||||
return _transformSystem.InRange(coordinates, targetXform.Coordinates, blackboard.GetValueOrDefault<float>(RangeKey, _entManager));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,8 @@ public sealed partial class MeleeOperator : HTNOperator, IHtnConditionalShutdown
|
||||
HTNOperatorStatus status;
|
||||
|
||||
if (_entManager.TryGetComponent<NPCMeleeCombatComponent>(owner, out var combat) &&
|
||||
blackboard.TryGetValue<EntityUid>(TargetKey, out var target, _entManager))
|
||||
blackboard.TryGetValue<EntityUid>(TargetKey, out var target, _entManager) &&
|
||||
target != EntityUid.Invalid)
|
||||
{
|
||||
combat.Target = target;
|
||||
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
using Content.Shared.Mobs;
|
||||
|
||||
namespace Content.Server.NPC.Queries.Considerations;
|
||||
|
||||
/// <summary>
|
||||
/// Goes linearly from 1f to 0f, with 0 damage returning 1f and <see cref=TargetState> damage returning 0f
|
||||
/// </summary>
|
||||
public sealed partial class TargetHealthCon : UtilityConsideration
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Which MobState the consideration returns 0f at, defaults to choosing earliest incapacitating MobState
|
||||
/// </summary>
|
||||
[DataField("targetState")]
|
||||
public MobState TargetState = MobState.Invalid;
|
||||
}
|
||||
|
||||
@@ -143,6 +143,9 @@ public sealed class NPCJukeSystem : EntitySystem
|
||||
if (!_melee.TryGetWeapon(uid, out var weaponUid, out var weapon))
|
||||
return;
|
||||
|
||||
if (!HasComp<TransformComponent>(melee.Target))
|
||||
return;
|
||||
|
||||
var cdRemaining = weapon.NextAttack - _timing.CurTime;
|
||||
var attackCooldown = TimeSpan.FromSeconds(1f / _melee.GetAttackRate(weaponUid, uid, weapon));
|
||||
|
||||
|
||||
@@ -7,10 +7,13 @@ using Content.Server.NPC.Queries.Queries;
|
||||
using Content.Server.Nutrition.Components;
|
||||
using Content.Server.Nutrition.EntitySystems;
|
||||
using Content.Server.Storage.Components;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Fluids.Components;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.NPC.Systems;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
@@ -48,6 +51,7 @@ public sealed class NPCUtilitySystem : EntitySystem
|
||||
[Dependency] private readonly WeldableSystem _weldable = default!;
|
||||
[Dependency] private readonly ExamineSystemShared _examine = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
[Dependency] private readonly MobThresholdSystem _thresholdSystem = default!;
|
||||
|
||||
private EntityQuery<PuddleComponent> _puddleQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
@@ -293,8 +297,14 @@ public sealed class NPCUtilitySystem : EntitySystem
|
||||
|
||||
return (float) ev.Count / ev.Capacity;
|
||||
}
|
||||
case TargetHealthCon:
|
||||
case TargetHealthCon con:
|
||||
{
|
||||
if (!TryComp(targetUid, out DamageableComponent? damage))
|
||||
return 0f;
|
||||
if (con.TargetState != MobState.Invalid && _thresholdSystem.TryGetPercentageForState(targetUid, con.TargetState, damage.TotalDamage, out var percentage))
|
||||
return Math.Clamp((float)(1 - percentage), 0f, 1f);
|
||||
if (_thresholdSystem.TryGetIncapPercentage(targetUid, damage.TotalDamage, out var incapPercentage))
|
||||
return Math.Clamp((float)(1 - incapPercentage), 0f, 1f);
|
||||
return 0f;
|
||||
}
|
||||
case TargetInLOSCon:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace Content.Server.Ninja.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Raised on the ninja when the suit has its powercell changed.
|
||||
/// Raised on the ninja and suit when the suit has its powercell changed.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct NinjaBatteryChangedEvent(EntityUid Battery, EntityUid BatteryHolder);
|
||||
|
||||
@@ -33,16 +33,17 @@ public sealed class BatteryDrainerSystem : SharedBatteryDrainerSystem
|
||||
/// Start do after for draining a power source.
|
||||
/// Can't predict PNBC existing so only done on server.
|
||||
/// </summary>
|
||||
private void OnBeforeInteractHand(EntityUid uid, BatteryDrainerComponent comp, BeforeInteractHandEvent args)
|
||||
private void OnBeforeInteractHand(Entity<BatteryDrainerComponent> ent, ref BeforeInteractHandEvent args)
|
||||
{
|
||||
var (uid, comp) = ent;
|
||||
var target = args.Target;
|
||||
if (args.Handled || comp.BatteryUid == null || !HasComp<PowerNetworkBatteryComponent>(target))
|
||||
if (args.Handled || comp.BatteryUid is not {} battery || !HasComp<PowerNetworkBatteryComponent>(target))
|
||||
return;
|
||||
|
||||
// handles even if battery is full so you can actually see the poup
|
||||
args.Handled = true;
|
||||
|
||||
if (_battery.IsFull(comp.BatteryUid.Value))
|
||||
if (_battery.IsFull(battery))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("battery-drainer-full"), uid, uid, PopupType.Medium);
|
||||
return;
|
||||
@@ -59,23 +60,24 @@ public sealed class BatteryDrainerSystem : SharedBatteryDrainerSystem
|
||||
_doAfter.TryStartDoAfter(doAfterArgs);
|
||||
}
|
||||
|
||||
private void OnBatteryChanged(EntityUid uid, BatteryDrainerComponent comp, ref NinjaBatteryChangedEvent args)
|
||||
private void OnBatteryChanged(Entity<BatteryDrainerComponent> ent, ref NinjaBatteryChangedEvent args)
|
||||
{
|
||||
SetBattery(uid, args.Battery, comp);
|
||||
SetBattery((ent, ent.Comp), args.Battery);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnDoAfterAttempt(EntityUid uid, BatteryDrainerComponent comp, DoAfterAttemptEvent<DrainDoAfterEvent> args)
|
||||
protected override void OnDoAfterAttempt(Entity<BatteryDrainerComponent> ent, ref DoAfterAttemptEvent<DrainDoAfterEvent> args)
|
||||
{
|
||||
base.OnDoAfterAttempt(uid, comp, args);
|
||||
base.OnDoAfterAttempt(ent, ref args);
|
||||
|
||||
if (comp.BatteryUid == null || _battery.IsFull(comp.BatteryUid.Value))
|
||||
if (ent.Comp.BatteryUid is not {} battery || _battery.IsFull(battery))
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool TryDrainPower(EntityUid uid, BatteryDrainerComponent comp, EntityUid target)
|
||||
protected override bool TryDrainPower(Entity<BatteryDrainerComponent> ent, EntityUid target)
|
||||
{
|
||||
var (uid, comp) = ent;
|
||||
if (comp.BatteryUid == null || !TryComp<BatteryComponent>(comp.BatteryUid.Value, out var battery))
|
||||
return false;
|
||||
|
||||
@@ -98,6 +100,7 @@ public sealed class BatteryDrainerSystem : SharedBatteryDrainerSystem
|
||||
|
||||
var output = input * comp.DrainEfficiency;
|
||||
_battery.SetCharge(comp.BatteryUid.Value, battery.CurrentCharge + output, battery);
|
||||
// TODO: create effect message or something
|
||||
Spawn("EffectSparks", Transform(target).Coordinates);
|
||||
_audio.PlayPvs(comp.SparkSound, target);
|
||||
_popup.PopupEntity(Loc.GetString("battery-drainer-success", ("battery", target)), uid, uid);
|
||||
|
||||
57
Content.Server/Ninja/Systems/ItemCreatorSystem.cs
Normal file
57
Content.Server/Ninja/Systems/ItemCreatorSystem.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using Content.Server.Ninja.Events;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Ninja.Components;
|
||||
using Content.Shared.Ninja.Systems;
|
||||
using Content.Shared.Popups;
|
||||
|
||||
namespace Content.Server.Ninja.Systems;
|
||||
|
||||
public sealed class ItemCreatorSystem : SharedItemCreatorSystem
|
||||
{
|
||||
[Dependency] private readonly BatterySystem _battery = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ItemCreatorComponent, CreateItemEvent>(OnCreateItem);
|
||||
SubscribeLocalEvent<ItemCreatorComponent, NinjaBatteryChangedEvent>(OnBatteryChanged);
|
||||
}
|
||||
|
||||
private void OnCreateItem(Entity<ItemCreatorComponent> ent, ref CreateItemEvent args)
|
||||
{
|
||||
var (uid, comp) = ent;
|
||||
if (comp.Battery is not {} battery)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
var user = args.Performer;
|
||||
if (!_battery.TryUseCharge(battery, comp.Charge))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString(comp.NoPowerPopup), user, user);
|
||||
return;
|
||||
}
|
||||
|
||||
var ev = new CreateItemAttemptEvent(user);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
if (ev.Cancelled)
|
||||
return;
|
||||
|
||||
// try to put throwing star in hand, otherwise it goes on the ground
|
||||
var star = Spawn(comp.SpawnedPrototype, Transform(user).Coordinates);
|
||||
_hands.TryPickupAnyHand(user, star);
|
||||
}
|
||||
|
||||
private void OnBatteryChanged(Entity<ItemCreatorComponent> ent, ref NinjaBatteryChangedEvent args)
|
||||
{
|
||||
if (ent.Comp.Battery == args.Battery)
|
||||
return;
|
||||
|
||||
ent.Comp.Battery = args.Battery;
|
||||
Dirty(ent, ent.Comp);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,8 @@
|
||||
using Content.Server.Communications;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Ninja.Events;
|
||||
using Content.Server.Objectives.Systems;
|
||||
using Content.Shared.Communications;
|
||||
using Content.Shared.CriminalRecords.Components;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Objectives.Systems;
|
||||
using Content.Shared.Ninja.Components;
|
||||
using Content.Shared.Ninja.Systems;
|
||||
using Content.Shared.Research.Components;
|
||||
using Content.Shared.Toggleable;
|
||||
|
||||
namespace Content.Server.Ninja.Systems;
|
||||
|
||||
@@ -16,89 +11,44 @@ namespace Content.Server.Ninja.Systems;
|
||||
/// </summary>
|
||||
public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem
|
||||
{
|
||||
[Dependency] private readonly EmagProviderSystem _emagProvider = default!;
|
||||
[Dependency] private readonly CodeConditionSystem _codeCondition = default!;
|
||||
[Dependency] private readonly CommsHackerSystem _commsHacker = default!;
|
||||
[Dependency] private readonly SharedStunProviderSystem _stunProvider = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
|
||||
[Dependency] private readonly SpaceNinjaSystem _ninja = default!;
|
||||
|
||||
public override void Initialize()
|
||||
protected override void EnableGloves(Entity<NinjaGlovesComponent> ent, Entity<SpaceNinjaComponent> user)
|
||||
{
|
||||
base.Initialize();
|
||||
base.EnableGloves(ent, user);
|
||||
|
||||
SubscribeLocalEvent<NinjaGlovesComponent, ToggleActionEvent>(OnToggleAction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggle gloves, if the user is a ninja wearing a ninja suit.
|
||||
/// </summary>
|
||||
private void OnToggleAction(EntityUid uid, NinjaGlovesComponent comp, ToggleActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
var user = args.Performer;
|
||||
// need to wear suit to enable gloves
|
||||
if (!TryComp<SpaceNinjaComponent>(user, out var ninja)
|
||||
|| ninja.Suit == null
|
||||
|| !HasComp<NinjaSuitComponent>(ninja.Suit.Value))
|
||||
{
|
||||
Popup.PopupEntity(Loc.GetString("ninja-gloves-not-wearing-suit"), user, user);
|
||||
return;
|
||||
}
|
||||
|
||||
// show its state to the user
|
||||
var enabling = comp.User == null;
|
||||
Appearance.SetData(uid, ToggleVisuals.Toggled, enabling);
|
||||
var message = Loc.GetString(enabling ? "ninja-gloves-on" : "ninja-gloves-off");
|
||||
Popup.PopupEntity(message, user, user);
|
||||
|
||||
if (enabling)
|
||||
{
|
||||
EnableGloves(uid, comp, user, ninja);
|
||||
}
|
||||
else
|
||||
{
|
||||
DisableGloves(uid, comp);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnableGloves(EntityUid uid, NinjaGlovesComponent comp, EntityUid user, SpaceNinjaComponent ninja)
|
||||
{
|
||||
// can't use abilities if suit is not equipped, this is checked elsewhere but just making sure to satisfy nullability
|
||||
if (ninja.Suit == null)
|
||||
if (user.Comp.Suit is not {} suit)
|
||||
return;
|
||||
|
||||
comp.User = user;
|
||||
Dirty(uid, comp);
|
||||
_ninja.AssignGloves(user, uid, ninja);
|
||||
if (!_mind.TryGetMind(user, out var mindId, out var mind))
|
||||
return;
|
||||
|
||||
var drainer = EnsureComp<BatteryDrainerComponent>(user);
|
||||
var stun = EnsureComp<StunProviderComponent>(user);
|
||||
_stunProvider.SetNoPowerPopup(user, "ninja-no-power", stun);
|
||||
foreach (var ability in ent.Comp.Abilities)
|
||||
{
|
||||
// non-objective abilities are added in shared already
|
||||
if (ability.Objective is not {} objId)
|
||||
continue;
|
||||
|
||||
// prevent doing an objective multiple times by toggling gloves after doing them
|
||||
// if it's not tied to an objective always add them anyway
|
||||
if (!_mind.TryFindObjective((mindId, mind), objId, out var obj))
|
||||
{
|
||||
Log.Error($"Ninja glove ability of {ent} referenced missing objective {ability.Objective} of {_mind.MindOwnerLoggingString(mind)}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_objectives.IsCompleted(obj.Value, (mindId, mind)))
|
||||
EntityManager.AddComponents(user, ability.Components);
|
||||
}
|
||||
|
||||
// let abilities that use battery power work
|
||||
if (_ninja.GetNinjaBattery(user, out var battery, out var _))
|
||||
{
|
||||
var ev = new NinjaBatteryChangedEvent(battery.Value, ninja.Suit.Value);
|
||||
var ev = new NinjaBatteryChangedEvent(battery.Value, suit);
|
||||
RaiseLocalEvent(user, ref ev);
|
||||
}
|
||||
|
||||
var emag = EnsureComp<EmagProviderComponent>(user);
|
||||
_emagProvider.SetWhitelist(user, comp.DoorjackWhitelist, emag);
|
||||
|
||||
EnsureComp<ResearchStealerComponent>(user);
|
||||
// prevent calling in multiple threats by toggling gloves after
|
||||
if (!_codeCondition.IsCompleted(user, ninja.TerrorObjective))
|
||||
{
|
||||
var hacker = EnsureComp<CommsHackerComponent>(user);
|
||||
var rule = _ninja.NinjaRule(user);
|
||||
if (rule != null)
|
||||
_commsHacker.SetThreats(user, rule.Threats, hacker);
|
||||
}
|
||||
if (!_codeCondition.IsCompleted(user, ninja.MassArrestObjective))
|
||||
{
|
||||
EnsureComp<CriminalRecordsHackerComponent>(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ using Content.Server.Emp;
|
||||
using Content.Server.Ninja.Events;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Shared.Clothing.EntitySystems;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Ninja.Components;
|
||||
using Content.Shared.Ninja.Systems;
|
||||
@@ -29,15 +28,13 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
||||
|
||||
SubscribeLocalEvent<NinjaSuitComponent, ContainerIsInsertingAttemptEvent>(OnSuitInsertAttempt);
|
||||
SubscribeLocalEvent<NinjaSuitComponent, EmpAttemptEvent>(OnEmpAttempt);
|
||||
SubscribeLocalEvent<NinjaSuitComponent, AttemptStealthEvent>(OnAttemptStealth);
|
||||
SubscribeLocalEvent<NinjaSuitComponent, CreateThrowingStarEvent>(OnCreateThrowingStar);
|
||||
SubscribeLocalEvent<NinjaSuitComponent, RecallKatanaEvent>(OnRecallKatana);
|
||||
SubscribeLocalEvent<NinjaSuitComponent, NinjaEmpEvent>(OnEmp);
|
||||
}
|
||||
|
||||
protected override void NinjaEquippedSuit(EntityUid uid, NinjaSuitComponent comp, EntityUid user, SpaceNinjaComponent ninja)
|
||||
protected override void NinjaEquipped(Entity<NinjaSuitComponent> ent, Entity<SpaceNinjaComponent> user)
|
||||
{
|
||||
base.NinjaEquippedSuit(uid, comp, user, ninja);
|
||||
base.NinjaEquipped(ent, user);
|
||||
|
||||
_ninja.SetSuitPowerAlert(user);
|
||||
}
|
||||
@@ -57,16 +54,15 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
||||
|
||||
// can only upgrade power cell, not swap to recharge instantly otherwise ninja could just swap batteries with flashlights in maints for easy power
|
||||
if (!TryComp<BatteryComponent>(args.EntityUid, out var inserting) || inserting.MaxCharge <= battery.MaxCharge)
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
// tell ninja abilities that use battery to update it so they don't use charge from the old one
|
||||
var user = Transform(uid).ParentUid;
|
||||
if (!HasComp<SpaceNinjaComponent>(user))
|
||||
if (!_ninja.IsNinja(user))
|
||||
return;
|
||||
|
||||
var ev = new NinjaBatteryChangedEvent(args.EntityUid, uid);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
RaiseLocalEvent(user, ref ev);
|
||||
}
|
||||
|
||||
@@ -77,64 +73,22 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
protected override void UserUnequippedSuit(EntityUid uid, NinjaSuitComponent comp, EntityUid user)
|
||||
protected override void UserUnequippedSuit(Entity<NinjaSuitComponent> ent, Entity<SpaceNinjaComponent> user)
|
||||
{
|
||||
base.UserUnequippedSuit(uid, comp, user);
|
||||
base.UserUnequippedSuit(ent, user);
|
||||
|
||||
// remove power indicator
|
||||
_ninja.SetSuitPowerAlert(user);
|
||||
}
|
||||
|
||||
private void OnAttemptStealth(EntityUid uid, NinjaSuitComponent comp, AttemptStealthEvent args)
|
||||
private void OnRecallKatana(Entity<NinjaSuitComponent> ent, ref RecallKatanaEvent args)
|
||||
{
|
||||
var user = args.User;
|
||||
// need 1 second of charge to turn on stealth
|
||||
var chargeNeeded = SuitWattage(uid, comp);
|
||||
// being attacked while cloaked gives no power message since it overloads the power supply or something
|
||||
if (!_ninja.GetNinjaBattery(user, out _, out var battery) || battery.CurrentCharge < chargeNeeded)
|
||||
{
|
||||
Popup.PopupEntity(Loc.GetString("ninja-no-power"), user, user);
|
||||
args.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp.DisableCooldown > GameTiming.CurTime)
|
||||
{
|
||||
Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium);
|
||||
args.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
StealthClothing.SetEnabled(uid, user, true);
|
||||
}
|
||||
|
||||
private void OnCreateThrowingStar(EntityUid uid, NinjaSuitComponent comp, CreateThrowingStarEvent args)
|
||||
{
|
||||
args.Handled = true;
|
||||
var (uid, comp) = ent;
|
||||
var user = args.Performer;
|
||||
if (!_ninja.TryUseCharge(user, comp.ThrowingStarCharge))
|
||||
{
|
||||
Popup.PopupEntity(Loc.GetString("ninja-no-power"), user, user);
|
||||
if (!_ninja.NinjaQuery.TryComp(user, out var ninja) || ninja.Katana == null)
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp.DisableCooldown > GameTiming.CurTime)
|
||||
{
|
||||
Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium);
|
||||
return;
|
||||
}
|
||||
|
||||
// try to put throwing star in hand, otherwise it goes on the ground
|
||||
var star = Spawn(comp.ThrowingStarPrototype, Transform(user).Coordinates);
|
||||
_hands.TryPickupAnyHand(user, star);
|
||||
}
|
||||
|
||||
private void OnRecallKatana(EntityUid uid, NinjaSuitComponent comp, RecallKatanaEvent args)
|
||||
{
|
||||
args.Handled = true;
|
||||
var user = args.Performer;
|
||||
if (!TryComp<SpaceNinjaComponent>(user, out var ninja) || ninja.Katana == null)
|
||||
return;
|
||||
|
||||
var katana = ninja.Katana.Value;
|
||||
var coords = _transform.GetWorldPosition(katana);
|
||||
@@ -146,11 +100,8 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp.DisableCooldown > GameTiming.CurTime)
|
||||
{
|
||||
Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium);
|
||||
if (CheckDisabled(ent, user))
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: teleporting into belt slot
|
||||
var message = _hands.TryPickupAnyHand(user, katana)
|
||||
@@ -159,9 +110,11 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
||||
Popup.PopupEntity(Loc.GetString(message), user, user);
|
||||
}
|
||||
|
||||
private void OnEmp(EntityUid uid, NinjaSuitComponent comp, NinjaEmpEvent args)
|
||||
private void OnEmp(Entity<NinjaSuitComponent> ent, ref NinjaEmpEvent args)
|
||||
{
|
||||
var (uid, comp) = ent;
|
||||
args.Handled = true;
|
||||
|
||||
var user = args.Performer;
|
||||
if (!_ninja.TryUseCharge(user, comp.EmpCharge))
|
||||
{
|
||||
@@ -169,13 +122,9 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp.DisableCooldown > GameTiming.CurTime)
|
||||
{
|
||||
Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium);
|
||||
if (CheckDisabled(ent, user))
|
||||
return;
|
||||
}
|
||||
|
||||
// I don't think this affects the suit battery, but if it ever does in the future add a blacklist for it
|
||||
var coords = _transform.GetMapCoordinates(user);
|
||||
_emp.EmpPulse(coords, comp.EmpRange, comp.EmpConsumption, comp.EmpDuration);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ using Content.Server.Communications;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.CriminalRecords.Systems;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.GenericAntag;
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Server.Objectives.Systems;
|
||||
using Content.Server.Power.Components;
|
||||
@@ -11,7 +10,6 @@ using Content.Server.PowerCell;
|
||||
using Content.Server.Research.Systems;
|
||||
using Content.Server.Roles;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Clothing.EntitySystems;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Mind;
|
||||
@@ -26,11 +24,6 @@ using Robust.Shared.Audio.Systems;
|
||||
|
||||
namespace Content.Server.Ninja.Systems;
|
||||
|
||||
// TODO: when syndiborgs are a thing have a borg converter with 6 second doafter
|
||||
// engi -> saboteur
|
||||
// medi -> idk reskin it
|
||||
// other -> assault
|
||||
|
||||
/// <summary>
|
||||
/// Main ninja system that handles ninja setup, provides helper methods for the rest of the code to use.
|
||||
/// </summary>
|
||||
@@ -44,13 +37,11 @@ public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem
|
||||
[Dependency] private readonly RoleSystem _role = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly StealthClothingSystem _stealthClothing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SpaceNinjaComponent, GenericAntagCreatedEvent>(OnNinjaCreated);
|
||||
SubscribeLocalEvent<SpaceNinjaComponent, EmaggedSomethingEvent>(OnDoorjack);
|
||||
SubscribeLocalEvent<SpaceNinjaComponent, ResearchStolenEvent>(OnResearchStolen);
|
||||
SubscribeLocalEvent<SpaceNinjaComponent, ThreatCalledInEvent>(OnThreatCalledIn);
|
||||
@@ -62,7 +53,7 @@ public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem
|
||||
var query = EntityQueryEnumerator<SpaceNinjaComponent>();
|
||||
while (query.MoveNext(out var uid, out var ninja))
|
||||
{
|
||||
UpdateNinja(uid, ninja, frameTime);
|
||||
SetSuitPowerAlert((uid, ninja));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,31 +71,13 @@ public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem
|
||||
return newCount - oldCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a ninja's gamerule config data.
|
||||
/// If the gamerule was not started then it will be started automatically.
|
||||
/// </summary>
|
||||
public NinjaRuleComponent? NinjaRule(EntityUid uid, GenericAntagComponent? comp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp))
|
||||
return null;
|
||||
|
||||
// mind not added yet so no rule
|
||||
if (comp.RuleEntity == null)
|
||||
return null;
|
||||
|
||||
return CompOrNull<NinjaRuleComponent>(comp.RuleEntity);
|
||||
}
|
||||
|
||||
// TODO: can probably copy paste borg code here
|
||||
/// <summary>
|
||||
/// Update the alert for the ninja's suit power indicator.
|
||||
/// </summary>
|
||||
public void SetSuitPowerAlert(EntityUid uid, SpaceNinjaComponent? comp = null)
|
||||
public void SetSuitPowerAlert(Entity<SpaceNinjaComponent> ent)
|
||||
{
|
||||
if (!Resolve(uid, ref comp, false))
|
||||
return;
|
||||
|
||||
var (uid, comp) = ent;
|
||||
if (comp.Deleted || comp.Suit == null)
|
||||
{
|
||||
_alerts.ClearAlert(uid, comp.SuitPowerAlert);
|
||||
@@ -145,53 +118,6 @@ public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem
|
||||
return GetNinjaBattery(user, out var uid, out var battery) && _battery.TryUseCharge(uid.Value, charge, battery);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set up everything for ninja to work and send the greeting message/sound.
|
||||
/// Objectives are added by <see cref="GenericAntagSystem"/>.
|
||||
/// </summary>
|
||||
private void OnNinjaCreated(EntityUid uid, SpaceNinjaComponent comp, ref GenericAntagCreatedEvent args)
|
||||
{
|
||||
var mindId = args.MindId;
|
||||
var mind = args.Mind;
|
||||
|
||||
if (mind.Session == null)
|
||||
return;
|
||||
|
||||
var config = NinjaRule(uid);
|
||||
if (config == null)
|
||||
return;
|
||||
|
||||
var role = new NinjaRoleComponent
|
||||
{
|
||||
PrototypeId = "SpaceNinja"
|
||||
};
|
||||
_role.MindAddRole(mindId, role, mind);
|
||||
_role.MindPlaySound(mindId, config.GreetingSound, mind);
|
||||
|
||||
var session = mind.Session;
|
||||
_audio.PlayGlobal(config.GreetingSound, Filter.Empty().AddPlayer(session), false, AudioParams.Default);
|
||||
_chatMan.DispatchServerMessage(session, Loc.GetString("ninja-role-greeting"));
|
||||
}
|
||||
|
||||
// TODO: PowerCellDraw, modify when cloak enabled
|
||||
/// <summary>
|
||||
/// Handle constant power drains from passive usage and cloak.
|
||||
/// </summary>
|
||||
private void UpdateNinja(EntityUid uid, SpaceNinjaComponent ninja, float frameTime)
|
||||
{
|
||||
if (ninja.Suit == null)
|
||||
return;
|
||||
|
||||
float wattage = Suit.SuitWattage(ninja.Suit.Value);
|
||||
|
||||
SetSuitPowerAlert(uid, ninja);
|
||||
if (!TryUseCharge(uid, wattage * frameTime))
|
||||
{
|
||||
// ran out of power, uncloak ninja
|
||||
_stealthClothing.SetEnabled(ninja.Suit.Value, uid, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increment greentext when emagging a door.
|
||||
/// </summary>
|
||||
|
||||
@@ -7,6 +7,7 @@ using Content.Server.Roles;
|
||||
using Content.Server.Sticky.Events;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Ninja.Components;
|
||||
using Content.Shared.Ninja.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Ninja.Systems;
|
||||
@@ -14,7 +15,7 @@ namespace Content.Server.Ninja.Systems;
|
||||
/// <summary>
|
||||
/// Prevents planting a spider charge outside of its location and handles greentext.
|
||||
/// </summary>
|
||||
public sealed class SpiderChargeSystem : EntitySystem
|
||||
public sealed class SpiderChargeSystem : SharedSpiderChargeSystem
|
||||
{
|
||||
[Dependency] private readonly MindSystem _mind = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
|
||||
@@ -6,10 +6,11 @@ using Content.Shared.Ninja.Components;
|
||||
using Content.Shared.Ninja.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Stunnable;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.Timing;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Ninja.Systems;
|
||||
|
||||
@@ -20,12 +21,12 @@ public sealed class StunProviderSystem : SharedStunProviderSystem
|
||||
{
|
||||
[Dependency] private readonly BatterySystem _battery = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedStunSystem _stun = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -38,16 +39,18 @@ public sealed class StunProviderSystem : SharedStunProviderSystem
|
||||
/// <summary>
|
||||
/// Stun clicked mobs on the whitelist, if there is enough power.
|
||||
/// </summary>
|
||||
private void OnBeforeInteractHand(EntityUid uid, StunProviderComponent comp, BeforeInteractHandEvent args)
|
||||
private void OnBeforeInteractHand(Entity<StunProviderComponent> ent, ref BeforeInteractHandEvent args)
|
||||
{
|
||||
// TODO: generic check
|
||||
var (uid, comp) = ent;
|
||||
if (args.Handled || comp.BatteryUid == null || !_gloves.AbilityCheck(uid, args, out var target))
|
||||
return;
|
||||
|
||||
if (target == uid || _whitelistSystem.IsWhitelistFail(comp.Whitelist, target))
|
||||
if (target == uid || _whitelist.IsWhitelistFail(comp.Whitelist, target))
|
||||
return;
|
||||
|
||||
if (_timing.CurTime < comp.NextStun)
|
||||
var useDelay = EnsureComp<UseDelayComponent>(uid);
|
||||
if (_useDelay.IsDelayed((uid, useDelay), id: comp.DelayId))
|
||||
return;
|
||||
|
||||
// take charge from battery
|
||||
@@ -63,13 +66,14 @@ public sealed class StunProviderSystem : SharedStunProviderSystem
|
||||
_stun.TryParalyze(target, comp.StunTime, refresh: false);
|
||||
|
||||
// short cooldown to prevent instant stunlocking
|
||||
comp.NextStun = _timing.CurTime + comp.Cooldown;
|
||||
_useDelay.SetLength((uid, useDelay), comp.Cooldown, id: comp.DelayId);
|
||||
_useDelay.TryResetDelay((uid, useDelay), id: comp.DelayId);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnBatteryChanged(EntityUid uid, StunProviderComponent comp, ref NinjaBatteryChangedEvent args)
|
||||
private void OnBatteryChanged(Entity<StunProviderComponent> ent, ref NinjaBatteryChangedEvent args)
|
||||
{
|
||||
SetBattery(uid, args.Battery, comp);
|
||||
SetBattery((ent, ent.Comp), args.Battery);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
if (!isHotEvent.IsHot)
|
||||
return;
|
||||
|
||||
if (TryTransferReagents(entity.Comp, smokable))
|
||||
if (TryTransferReagents(entity, (entity.Owner, smokable)))
|
||||
SetSmokableState(entity, SmokableState.Lit, smokable);
|
||||
args.Handled = true;
|
||||
}
|
||||
@@ -62,7 +62,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
if (!isHotEvent.IsHot)
|
||||
return;
|
||||
|
||||
if (TryTransferReagents(entity.Comp, smokable))
|
||||
if (TryTransferReagents(entity, (entity.Owner, smokable)))
|
||||
SetSmokableState(entity, SmokableState.Lit, smokable);
|
||||
args.Handled = true;
|
||||
}
|
||||
@@ -74,15 +74,15 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
}
|
||||
|
||||
// Convert smokable item into reagents to be smoked
|
||||
private bool TryTransferReagents(SmokingPipeComponent component, SmokableComponent smokable)
|
||||
private bool TryTransferReagents(Entity<SmokingPipeComponent> entity, Entity<SmokableComponent> smokable)
|
||||
{
|
||||
if (component.BowlSlot.Item == null)
|
||||
if (entity.Comp.BowlSlot.Item == null)
|
||||
return false;
|
||||
|
||||
EntityUid contents = component.BowlSlot.Item.Value;
|
||||
EntityUid contents = entity.Comp.BowlSlot.Item.Value;
|
||||
|
||||
if (!TryComp<SolutionContainerManagerComponent>(contents, out var reagents) ||
|
||||
!_solutionContainerSystem.TryGetSolution(smokable.Owner, smokable.Solution, out var pipeSolution, out _))
|
||||
!_solutionContainerSystem.TryGetSolution(smokable.Owner, smokable.Comp.Solution, out var pipeSolution, out _))
|
||||
return false;
|
||||
|
||||
foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((contents, reagents)))
|
||||
@@ -93,7 +93,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
|
||||
EntityManager.DeleteEntity(contents);
|
||||
|
||||
_itemSlotsSystem.SetLock(component.Owner, component.BowlSlot, true); //no inserting more until current runs out
|
||||
_itemSlotsSystem.SetLock(entity.Owner, entity.Comp.BowlSlot, true); //no inserting more until current runs out
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -31,24 +31,24 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
/// <summary>
|
||||
/// Clicked with utensil
|
||||
/// </summary>
|
||||
private void OnAfterInteract(EntityUid uid, UtensilComponent component, AfterInteractEvent ev)
|
||||
private void OnAfterInteract(Entity<UtensilComponent> entity, ref AfterInteractEvent ev)
|
||||
{
|
||||
if (ev.Handled || ev.Target == null || !ev.CanReach)
|
||||
return;
|
||||
|
||||
var result = TryUseUtensil(ev.User, ev.Target.Value, component);
|
||||
var result = TryUseUtensil(ev.User, ev.Target.Value, entity);
|
||||
ev.Handled = result.Handled;
|
||||
}
|
||||
|
||||
public (bool Success, bool Handled) TryUseUtensil(EntityUid user, EntityUid target, UtensilComponent component)
|
||||
public (bool Success, bool Handled) TryUseUtensil(EntityUid user, EntityUid target, Entity<UtensilComponent> utensil)
|
||||
{
|
||||
if (!EntityManager.TryGetComponent(target, out FoodComponent? food))
|
||||
return (false, true);
|
||||
|
||||
//Prevents food usage with a wrong utensil
|
||||
if ((food.Utensil & component.Types) == 0)
|
||||
if ((food.Utensil & utensil.Comp.Types) == 0)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("food-system-wrong-utensil", ("food", target), ("utensil", component.Owner)), user, user);
|
||||
_popupSystem.PopupEntity(Loc.GetString("food-system-wrong-utensil", ("food", target), ("utensil", utensil.Owner)), user, user);
|
||||
return (false, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ using Robust.Shared.Random;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Objectives;
|
||||
|
||||
@@ -180,33 +181,32 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
|
||||
}
|
||||
}
|
||||
|
||||
public EntityUid? GetRandomObjective(EntityUid mindId, MindComponent mind, string objectiveGroupProto)
|
||||
public EntityUid? GetRandomObjective(EntityUid mindId, MindComponent mind, ProtoId<WeightedRandomPrototype> objectiveGroupProto, float maxDifficulty)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<WeightedRandomPrototype>(objectiveGroupProto, out var groups))
|
||||
if (!_prototypeManager.TryIndex(objectiveGroupProto, out var groupsProto))
|
||||
{
|
||||
Log.Error($"Tried to get a random objective, but can't index WeightedRandomPrototype {objectiveGroupProto}");
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO replace whatever the fuck this is with a proper objective selection system
|
||||
// yeah the old 'preventing infinite loops' thing wasn't super elegant either and it mislead people on what exactly it did
|
||||
var tries = 0;
|
||||
while (tries < 20)
|
||||
{
|
||||
var groupName = groups.Pick(_random);
|
||||
// Make a copy of the weights so we don't trash the prototype by removing entries
|
||||
var groups = groupsProto.Weights.ShallowClone();
|
||||
|
||||
while (_random.TryPickAndTake(groups, out var groupName))
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<WeightedRandomPrototype>(groupName, out var group))
|
||||
{
|
||||
Log.Error($"Couldn't index objective group prototype {groupName}");
|
||||
return null;
|
||||
}
|
||||
|
||||
var proto = group.Pick(_random);
|
||||
var objective = TryCreateObjective(mindId, mind, proto);
|
||||
if (objective != null)
|
||||
return objective;
|
||||
|
||||
tries++;
|
||||
var objectives = group.Weights.ShallowClone();
|
||||
while (_random.TryPickAndTake(objectives, out var objectiveProto))
|
||||
{
|
||||
if (TryCreateObjective((mindId, mind), objectiveProto, out var objective)
|
||||
&& Comp<ObjectiveComponent>(objective.Value).Difficulty <= maxDifficulty)
|
||||
return objective;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -35,20 +35,6 @@ public sealed class CodeConditionSystem : EntitySystem
|
||||
return ent.Comp.Completed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if a mob's objective with a certain prototype is completed.
|
||||
/// </summary>
|
||||
public bool IsCompleted(Entity<MindContainerComponent?> mob, string prototype)
|
||||
{
|
||||
if (_mind.GetMind(mob, mob.Comp) is not {} mindId)
|
||||
return false;
|
||||
|
||||
if (!_mind.TryFindObjective(mindId, prototype, out var obj))
|
||||
return false;
|
||||
|
||||
return IsCompleted(obj.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets an objective's completed field.
|
||||
/// </summary>
|
||||
|
||||
@@ -126,20 +126,18 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
||||
var xform = Transform(uid);
|
||||
var mapId = xform.MapID;
|
||||
|
||||
if (mapId != MapId.Nullspace && TryComp(uid, out MapGridComponent? mapGrid))
|
||||
if (mapId != MapId.Nullspace && HasComp<MapGridComponent>(uid))
|
||||
{
|
||||
var setTiles = new List<(Vector2i Index, Tile tile)>();
|
||||
|
||||
foreach (var grid in _mapManager.GetAllMapGrids(mapId))
|
||||
foreach (var grid in _mapManager.GetAllGrids(mapId))
|
||||
{
|
||||
var gridUid = grid.Owner;
|
||||
|
||||
if (!_fixturesQuery.TryGetComponent(gridUid, out var fixtures))
|
||||
if (!_fixturesQuery.TryGetComponent(grid.Owner, out var fixtures))
|
||||
continue;
|
||||
|
||||
// Don't want shuttles flying around now do we.
|
||||
_shuttles.Disable(gridUid);
|
||||
var pTransform = _physics.GetPhysicsTransform(gridUid);
|
||||
_shuttles.Disable(grid.Owner);
|
||||
var pTransform = _physics.GetPhysicsTransform(grid.Owner);
|
||||
|
||||
foreach (var fixture in fixtures.Fixtures.Values)
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user