mirror of
https://github.com/corvax-team/ss14-wl.git
synced 2026-06-09 10:06:46 +02:00
Merge remote-tracking branch 'corvax/master' into upstream
This commit is contained in:
+1
-1
@@ -351,7 +351,7 @@ resharper_csharp_qualified_using_at_nested_scope = false
|
||||
resharper_csharp_prefer_qualified_reference = false
|
||||
resharper_csharp_allow_alias = false
|
||||
|
||||
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props}]
|
||||
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props,slnx}]
|
||||
indent_size = 2
|
||||
|
||||
[nuget.config]
|
||||
|
||||
+2
-5
@@ -5,12 +5,9 @@
|
||||
* @JerryImMouse
|
||||
* @Litogin
|
||||
|
||||
# Translations
|
||||
*.ftl @Morb0 @DIMMoon1 @ficcialfaint
|
||||
|
||||
# Maps
|
||||
/Resources/Prototypes/Maps/** @Morb0 @DIMMoon1 @Ko4ergaPunk
|
||||
/Resources/Maps/** @Morb0 @DIMMoon1 @Ko4ergaPunk
|
||||
/Resources/Prototypes/Maps/** @Morb0 @DIMMoon1 @kvant8
|
||||
/Resources/Maps/** @Morb0 @DIMMoon1 @kvant8
|
||||
/Resources/Prototypes/_WL/Maps/** @0leshe
|
||||
/Resources/Maps/_WL/** @0leshe
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
Небольшие исправления/рефакторинг освобождаются от этого требования. -->
|
||||
|
||||
## Требования
|
||||
<!-- Подтвердите следующее, поставив X в скобках [X]: -->
|
||||
<!-- Подтвердите следующее, поставив X в скобках без пробелов [X]: -->
|
||||
- [ ] Я прочитал(а) и следую [Рекомендациям по оформлению Pull Request и Changelog](https://docs.spacestation14.com/en/general-development/codebase-info/pull-request-guidelines.html).
|
||||
- [ ] Я добавил(а) медиафайлы к этому PR или он не требует демонстрации в игре.
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Get Engine Tag
|
||||
run: |
|
||||
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.3.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Get Engine Tag
|
||||
run: |
|
||||
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
@@ -14,6 +14,7 @@ on:
|
||||
- 'Resources/Prototypes/Chemistry/**.yml'
|
||||
- 'Resources/Prototypes/Recipes/Reactions/**.yml'
|
||||
- 'RobustToolbox/'
|
||||
- 'Resources/Locale/**.ftl'
|
||||
|
||||
jobs:
|
||||
update-wiki:
|
||||
@@ -90,3 +91,13 @@ jobs:
|
||||
api_url: ${{ secrets.WIKI_ROOT_URL }}/api.php
|
||||
username: ${{ secrets.WIKI_BOT_USER }}
|
||||
password: ${{ secrets.WIKI_BOT_PASS }}
|
||||
|
||||
- name: Upload loc.json to wiki
|
||||
uses: jtmullen/mediawiki-edit-action@v0.1.1
|
||||
with:
|
||||
wiki_text_file: ./bin/Content.Server/data/loc.json
|
||||
edit_summary: Update loc.json via GitHub Actions
|
||||
page_name: "${{ secrets.WIKI_PAGE_ROOT }}/loc.json"
|
||||
api_url: ${{ secrets.WIKI_ROOT_URL }}/api.php
|
||||
username: ${{ secrets.WIKI_BOT_USER }}
|
||||
password: ${{ secrets.WIKI_BOT_PASS }}
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# MSbuild binlog files
|
||||
*.binlog
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -11,7 +11,7 @@ import time
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
SOLUTION_PATH = Path("..") / "SpaceStation14.sln"
|
||||
SOLUTION_PATH = Path("..") / "SpaceStation14.slnx"
|
||||
# If this doesn't match the saved version we overwrite them all.
|
||||
CURRENT_HOOKS_VERSION = "4"
|
||||
QUIET = len(sys.argv) == 2 and sys.argv[1] == "--quiet"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Space Station 14 Contributing Guidelines
|
||||
|
||||
Thanks for contributing to Space Station 14.
|
||||
When contributing, be sure to follow our [codebase conventions](https://docs.spacestation14.com/en/general-development/codebase-info/codebase-organization.html) and [PR guidelines](https://docs.spacestation14.com/en/general-development/codebase-info/pull-request-guidelines.html).
|
||||
|
||||
Following these guidelines helps us increase review turnaround time, so be sure to review the linked documents in full.
|
||||
|
||||
The last major guidelines update was on **December 6th, 2025**.
|
||||
|
||||
### Why is this here?
|
||||
We put this here so that GitHub will notify you when submitting a pull request that the PR guidelines have changed, if you haven't read the latest version.
|
||||
@@ -1,17 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
||||
<PropertyGroup>
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<OutputPath>..\bin\Content.Benchmarks\</OutputPath>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<LangVersion>12</LangVersion>
|
||||
<IsTestingPlatformApplication>false</IsTestingPlatformApplication>
|
||||
<Nullable>disable</Nullable>
|
||||
</PropertyGroup>
|
||||
<Import Project="../MSBuild/Content.props" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" />
|
||||
|
||||
<!-- pin transitive deps -->
|
||||
<PackageReference Include="System.Management" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Content.Client\Content.Client.csproj" />
|
||||
@@ -19,10 +22,12 @@
|
||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
|
||||
<ProjectReference Include="..\Content.Tests\Content.Tests.csproj" />
|
||||
<ProjectReference Include="..\Content.IntegrationTests\Content.IntegrationTests.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Benchmarks\Robust.Benchmarks.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Server\Robust.Server.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\RobustToolbox\Imports\Lidgren.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Client.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Server.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Shared.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Benchmarks.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Testing.props" />
|
||||
</Project>
|
||||
|
||||
@@ -51,6 +51,8 @@ public class DestructibleBenchmark
|
||||
private readonly List<Entity<DamageableComponent>> _damageables = new();
|
||||
private readonly List<Entity<DamageableComponent, DestructibleComponent>> _destructbiles = new();
|
||||
|
||||
private TestMapData _currentMapData = default!;
|
||||
|
||||
private DamageSpecifier _damage;
|
||||
|
||||
private TestPair _pair = default!;
|
||||
@@ -70,8 +72,6 @@ public class DestructibleBenchmark
|
||||
_pair = await PoolManager.GetServerClient();
|
||||
var server = _pair.Server;
|
||||
|
||||
var mapdata = await _pair.CreateTestMap();
|
||||
|
||||
_entMan = server.ResolveDependency<IEntityManager>();
|
||||
_protoMan = server.ResolveDependency<IPrototypeManager>();
|
||||
_random = server.ResolveDependency<IRobustRandom>();
|
||||
@@ -86,19 +86,25 @@ public class DestructibleBenchmark
|
||||
_damage = new DamageSpecifier(type, DamageAmount);
|
||||
|
||||
_random.SetSeed(69420); // Randomness needs to be deterministic for benchmarking.
|
||||
}
|
||||
|
||||
[IterationSetup]
|
||||
public void IterationSetup()
|
||||
{
|
||||
var plating = _tileDefMan[TileRef].TileId;
|
||||
var server = _pair.Server;
|
||||
_currentMapData = _pair.CreateTestMap().GetAwaiter().GetResult();
|
||||
|
||||
// We make a rectangular grid of destructible entities, and then damage them all simultaneously to stress test the system.
|
||||
// Needed for managing the performance of destructive effects and damage application.
|
||||
await server.WaitPost(() =>
|
||||
server.WaitPost(() =>
|
||||
{
|
||||
// Set up a thin line of tiles to place our objects on. They should be anchored for a "realistic" scenario...
|
||||
for (var x = 0; x < EntityCount; x++)
|
||||
{
|
||||
for (var y = 0; y < _prototypes.Length; y++)
|
||||
{
|
||||
_map.SetTile(mapdata.Grid, mapdata.Grid, new Vector2i(x, y), new Tile(plating));
|
||||
_map.SetTile(_currentMapData.Grid, _currentMapData.Grid, new Vector2i(x, y), new Tile(plating));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +113,7 @@ public class DestructibleBenchmark
|
||||
var y = 0;
|
||||
foreach (var protoId in _prototypes)
|
||||
{
|
||||
var coords = new EntityCoordinates(mapdata.Grid, x + 0.5f, y + 0.5f);
|
||||
var coords = new EntityCoordinates(_currentMapData.Grid, x + 0.5f, y + 0.5f);
|
||||
_entMan.SpawnEntity(protoId, coords);
|
||||
y++;
|
||||
}
|
||||
@@ -115,12 +121,17 @@ public class DestructibleBenchmark
|
||||
|
||||
var query = _entMan.EntityQueryEnumerator<DamageableComponent, DestructibleComponent>();
|
||||
|
||||
_destructbiles.EnsureCapacity(EntityCount);
|
||||
_damageables.EnsureCapacity(EntityCount);
|
||||
|
||||
while (query.MoveNext(out var uid, out var damageable, out var destructible))
|
||||
{
|
||||
_damageables.Add((uid, damageable));
|
||||
_destructbiles.Add((uid, damageable, destructible));
|
||||
}
|
||||
});
|
||||
})
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
@@ -150,6 +161,26 @@ public class DestructibleBenchmark
|
||||
});
|
||||
}
|
||||
|
||||
[IterationCleanup]
|
||||
public void IterationCleanupAsync()
|
||||
{
|
||||
// We need to nuke the entire map and respawn everything as some destructible effects
|
||||
// spawn entities and whatnot.
|
||||
_pair.Server.WaitPost(() =>
|
||||
{
|
||||
_map.QueueDeleteMap(_currentMapData.MapId);
|
||||
})
|
||||
.Wait();
|
||||
|
||||
// Deletion of entities is often queued (QueueDel) which must be processed by running ticks
|
||||
// or else it will grow infinitely and leak memory.
|
||||
_pair.Server.WaitRunTicks(2)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
|
||||
_destructbiles.Clear();
|
||||
_damageables.Clear();
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
public async Task CleanupAsync()
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Content.IntegrationTests;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Server.Maps;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
|
||||
@@ -23,6 +23,8 @@ public class RaiseEventBenchmark
|
||||
PoolManager.Startup(typeof(BenchSystem).Assembly);
|
||||
_pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
|
||||
var entMan = _pair.Server.EntMan;
|
||||
var fact = _pair.Server.ResolveDependency<IComponentFactory>();
|
||||
var bus = (EntityEventBus)entMan.EventBus;
|
||||
_sys = entMan.System<BenchSystem>();
|
||||
|
||||
_pair.Server.WaitPost(() =>
|
||||
@@ -30,6 +32,8 @@ public class RaiseEventBenchmark
|
||||
var uid = entMan.Spawn();
|
||||
_sys.Ent = new(uid, entMan.GetComponent<TransformComponent>(uid));
|
||||
_sys.Ent2 = new(_sys.Ent.Owner, _sys.Ent.Comp);
|
||||
_sys.NetId = fact.GetRegistration<TransformComponent>().NetID!.Value;
|
||||
_sys.EvSubs = bus.GetNetCompEventHandlers<BenchSystem.BenchEv>();
|
||||
})
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
@@ -60,6 +64,12 @@ public class RaiseEventBenchmark
|
||||
return _sys.RaiseICompEvent();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int RaiseNetEvent()
|
||||
{
|
||||
return _sys.RaiseNetIdEvent();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int RaiseCSharpEvent()
|
||||
{
|
||||
@@ -74,6 +84,8 @@ public class RaiseEventBenchmark
|
||||
public delegate void EntityEventHandler(EntityUid uid, TransformComponent comp, ref BenchEv ev);
|
||||
|
||||
public event EntityEventHandler? OnCSharpEvent;
|
||||
public ushort NetId;
|
||||
internal EntityEventBus.DirectedEventHandler?[] EvSubs = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -92,7 +104,7 @@ public class RaiseEventBenchmark
|
||||
public int RaiseCompEvent()
|
||||
{
|
||||
var ev = new BenchEv();
|
||||
EntityManager.EventBus.RaiseComponentEvent(Ent.Owner, Ent.Comp, ref ev);
|
||||
RaiseComponentEvent(Ent.Owner, Ent.Comp, ref ev);
|
||||
return ev.N;
|
||||
}
|
||||
|
||||
@@ -100,7 +112,16 @@ public class RaiseEventBenchmark
|
||||
{
|
||||
// Raise with an IComponent instead of concrete type
|
||||
var ev = new BenchEv();
|
||||
EntityManager.EventBus.RaiseComponentEvent(Ent2.Owner, Ent2.Comp, ref ev);
|
||||
RaiseComponentEvent(Ent2.Owner, Ent2.Comp, ref ev);
|
||||
return ev.N;
|
||||
}
|
||||
|
||||
public int RaiseNetIdEvent()
|
||||
{
|
||||
// Raise a "IComponent" event using a net-id index delegate array (for PVS & client game-state events)
|
||||
var ev = new BenchEv();
|
||||
ref var unitEv = ref Unsafe.As<BenchEv, EntityEventBus.Unit>(ref ev);
|
||||
EvSubs[NetId]?.Invoke(Ent2.Owner, Ent2.Comp, ref unitEv);
|
||||
return ev.N;
|
||||
}
|
||||
|
||||
@@ -118,6 +139,7 @@ public class RaiseEventBenchmark
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
[ComponentEvent(Exclusive = false)]
|
||||
public struct BenchEv
|
||||
{
|
||||
public int N;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<GridContainer xmlns="https://spacestation14.io"
|
||||
Columns="5"
|
||||
Columns="4"
|
||||
HorizontalAlignment="Center">
|
||||
</GridContainer>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
MinSize="650 290">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<GridContainer Columns="2">
|
||||
<GridContainer Columns="3" HorizontalExpand="True">
|
||||
<GridContainer Name="PrivilegedIdGrid" Columns="3" HorizontalExpand="True">
|
||||
<Label Text="{Loc 'access-overrider-window-privileged-id'}" />
|
||||
<Button Name="PrivilegedIdButton" Access="Public"/>
|
||||
<Label Name="PrivilegedIdLabel" />
|
||||
|
||||
@@ -53,6 +53,8 @@ namespace Content.Client.Access.UI
|
||||
|
||||
public void UpdateState(IPrototypeManager protoManager, AccessOverriderBoundUserInterfaceState state)
|
||||
{
|
||||
PrivilegedIdGrid.Visible = state.ShowPrivilegedIdGrid;
|
||||
|
||||
PrivilegedIdLabel.Text = state.PrivilegedIdName;
|
||||
PrivilegedIdButton.Text = state.IsPrivilegedIdPresent
|
||||
? Loc.GetString("access-overrider-window-eject-button")
|
||||
@@ -77,7 +79,9 @@ namespace Content.Client.Access.UI
|
||||
missingPrivileges.Add(privilege);
|
||||
}
|
||||
|
||||
MissingPrivilegesLabel.Text = Loc.GetString("access-overrider-window-missing-privileges");
|
||||
MissingPrivilegesLabel.Text = state.ShowPrivilegedIdGrid ?
|
||||
Loc.GetString("access-overrider-window-missing-privileges") :
|
||||
Loc.GetString("access-overrider-window-missing-privileges-no-id");
|
||||
MissingPrivilegesText.Text = string.Join(", ", missingPrivileges);
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
? null
|
||||
: _prototypeManager.Index(playerInfo.RoleProto.Value);
|
||||
|
||||
var roleName = Loc.GetString(rolePrototype?.Name ?? RoleTypePrototype.FallbackName);
|
||||
var roleName = rolePrototype?.Name ?? RoleTypePrototype.FallbackName;
|
||||
var roleColor = rolePrototype?.Color ?? RoleTypePrototype.FallbackColor;
|
||||
var roleSymbol = rolePrototype?.Symbol ?? RoleTypePrototype.FallbackSymbol;
|
||||
|
||||
@@ -213,7 +213,7 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
{
|
||||
color = Color.GreenYellow;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, Loc.GetString(playerInfo.StartingJob), uiScale, playerInfo.Connected ? color : colorDisconnected);
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.StartingJob, uiScale, playerInfo.Connected ? color : colorDisconnected);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
color = roleColor;
|
||||
symbol = IsFiltered(playerInfo.RoleProto) ? symbol : string.Empty;
|
||||
text = IsFiltered(playerInfo.RoleProto)
|
||||
? roleName.ToUpper()
|
||||
? Loc.GetString(roleName).ToUpper()
|
||||
: string.Empty;
|
||||
break;
|
||||
case AdminOverlayAntagFormat.Subtype:
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
using Content.Shared.Administration.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Administration.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class KillSignComponent : SharedKillSignComponent;
|
||||
@@ -0,0 +1,7 @@
|
||||
using Content.Shared.Administration.Systems;
|
||||
|
||||
namespace Content.Client.Administration.Systems;
|
||||
|
||||
public sealed class BufferingSystem : SharedBufferingSystem
|
||||
{
|
||||
}
|
||||
@@ -1,46 +1,77 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Administration.Components;
|
||||
using Content.Shared.Administration.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Client.Player;
|
||||
|
||||
namespace Content.Client.Administration.Systems;
|
||||
|
||||
public sealed class KillSignSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<KillSignComponent, ComponentStartup>(KillSignAdded);
|
||||
SubscribeLocalEvent<KillSignComponent, ComponentShutdown>(KillSignRemoved);
|
||||
SubscribeLocalEvent<KillSignComponent, AfterAutoHandleStateEvent>(AfterAutoHandleState);
|
||||
}
|
||||
|
||||
private void KillSignRemoved(EntityUid uid, KillSignComponent component, ComponentShutdown args)
|
||||
private void KillSignRemoved(Entity<KillSignComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
if (!_sprite.LayerMapTryGet((uid, sprite), KillSignKey.Key, out var layer, false))
|
||||
return;
|
||||
|
||||
_sprite.RemoveLayer((uid, sprite), layer);
|
||||
RemoveKillsign(ent);
|
||||
}
|
||||
|
||||
private void KillSignAdded(EntityUid uid, KillSignComponent component, ComponentStartup args)
|
||||
private void KillSignAdded(Entity<KillSignComponent> ent, ref ComponentStartup args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
AddKillsign(ent);
|
||||
}
|
||||
|
||||
private void AfterAutoHandleState(Entity<KillSignComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
// After receiving a new state for the component, we remove the old killsign and build a new one.
|
||||
// This is so changes to the sprite can be displayed live and allowing them to be edited via ViewVariables.
|
||||
// This could just update an existing sprite, but this is both easier and runs rarely anyway.
|
||||
RemoveKillsign(ent);
|
||||
AddKillsign(ent);
|
||||
}
|
||||
|
||||
private void AddKillsign(Entity<KillSignComponent> ent)
|
||||
{
|
||||
// If we hide from owner and we ARE the owner, don't add a killsign.
|
||||
// This could use session specific networking to FULLY hide it, but I am too lazy right now.
|
||||
if (ent.Comp.HideFromOwner && _player.LocalEntity == ent)
|
||||
return;
|
||||
|
||||
if (_sprite.LayerMapTryGet((uid, sprite), KillSignKey.Key, out var _, false))
|
||||
if (!TryComp<SpriteComponent>(ent, out var sprite))
|
||||
return;
|
||||
|
||||
var adj = _sprite.GetLocalBounds((uid, sprite)).Height / 2 + ((1.0f / 32) * 6.0f);
|
||||
if (_sprite.LayerMapTryGet((ent, sprite), KillSignKey.Key, out var _, false))
|
||||
return;
|
||||
|
||||
var layer = _sprite.AddLayer((uid, sprite), new SpriteSpecifier.Rsi(new ResPath("Objects/Misc/killsign.rsi"), "sign"));
|
||||
_sprite.LayerMapSet((uid, sprite), KillSignKey.Key, layer);
|
||||
if (ent.Comp.Sprite == null)
|
||||
return;
|
||||
|
||||
_sprite.LayerSetOffset((uid, sprite), layer, new Vector2(0.0f, adj));
|
||||
sprite.LayerSetShader(layer, "unshaded");
|
||||
var adj = _sprite.GetLocalBounds((ent, sprite)).Height / 2 + ((1.0f / 32) * 6.0f);
|
||||
|
||||
var layer = _sprite.AddLayer((ent, sprite), ent.Comp.Sprite);
|
||||
_sprite.LayerMapSet((ent, sprite), KillSignKey.Key, layer);
|
||||
_sprite.LayerSetScale((ent, sprite), layer, ent.Comp.Scale);
|
||||
_sprite.LayerSetOffset((ent, sprite), layer, ent.Comp.DoOffset ? new Vector2(0.0f, adj) : new Vector2(0.0f, 0.0f));
|
||||
|
||||
if (ent.Comp.ForceUnshaded)
|
||||
sprite.LayerSetShader(layer, "unshaded");
|
||||
}
|
||||
|
||||
private void RemoveKillsign(Entity<KillSignComponent> ent)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(ent, out var sprite))
|
||||
return;
|
||||
|
||||
if (!_sprite.LayerMapTryGet((ent, sprite), KillSignKey.Key, out var layer, false))
|
||||
return;
|
||||
|
||||
_sprite.RemoveLayer((ent, sprite), layer);
|
||||
}
|
||||
|
||||
private enum KillSignKey
|
||||
|
||||
@@ -15,7 +15,7 @@ public sealed class AdminLogLabel : RichTextLabel
|
||||
OnVisibilityChanged += VisibilityChanged;
|
||||
}
|
||||
|
||||
public SharedAdminLog Log { get; }
|
||||
public new SharedAdminLog Log { get; }
|
||||
|
||||
public HSeparator Separator { get; }
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Client.Atmos.Components;
|
||||
using Content.Client.Atmos.EntitySystems;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Atmos.EntitySystems;
|
||||
using Content.Shared.Atmos.Prototypes;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -23,6 +24,7 @@ namespace Content.Client.Atmos.Overlays
|
||||
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IMapManager _mapManager;
|
||||
private readonly SharedAtmosphereSystem _atmosphereSystem;
|
||||
private readonly SharedMapSystem _mapSystem;
|
||||
private readonly SharedTransformSystem _xformSys;
|
||||
|
||||
@@ -54,6 +56,7 @@ namespace Content.Client.Atmos.Overlays
|
||||
{
|
||||
_entManager = entManager;
|
||||
_mapManager = IoCManager.Resolve<IMapManager>();
|
||||
_atmosphereSystem = entManager.System<SharedAtmosphereSystem>();
|
||||
_mapSystem = entManager.System<SharedMapSystem>();
|
||||
_xformSys = xformSys;
|
||||
_shader = protoMan.Index(UnshadedShader).Instance();
|
||||
@@ -67,7 +70,7 @@ namespace Content.Client.Atmos.Overlays
|
||||
|
||||
for (var i = 0; i < _gasCount; i++)
|
||||
{
|
||||
var gasPrototype = protoMan.Index<GasPrototype>(system.VisibleGasId[i].ToString());
|
||||
var gasPrototype = _atmosphereSystem.GetGas(system.VisibleGasId[i]);
|
||||
|
||||
SpriteSpecifier overlay;
|
||||
|
||||
|
||||
@@ -52,14 +52,18 @@ namespace Content.Client.Atmos.UI
|
||||
|
||||
private void OnSelectGasPressed()
|
||||
{
|
||||
if (_window is null) return;
|
||||
if (_window is null)
|
||||
return;
|
||||
|
||||
if (_window.SelectedGas is null)
|
||||
{
|
||||
SendMessage(new GasFilterSelectGasMessage(null));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!int.TryParse(_window.SelectedGas, out var gas)) return;
|
||||
if (!Enum.TryParse<Gas>(_window.SelectedGas, out var gas))
|
||||
return;
|
||||
|
||||
SendMessage(new GasFilterSelectGasMessage(gas));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
using Content.Shared.Bed;
|
||||
|
||||
namespace Content.Client.Bed;
|
||||
|
||||
public sealed class BedSystem : SharedBedSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -7,7 +7,6 @@ using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Serilog;
|
||||
|
||||
namespace Content.Client.Cargo.UI;
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using Content.Client.Message;
|
||||
using Content.Client.RichText;
|
||||
using Content.Client.UserInterface.RichText;
|
||||
using Content.Shared.MassMedia.Systems;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.RichText;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.CartridgeLoader.Cartridges;
|
||||
|
||||
@@ -31,16 +35,17 @@ public sealed partial class NewsReaderUiFragment : BoxContainer
|
||||
Author.Visible = true;
|
||||
|
||||
PageName.Text = article.Title;
|
||||
PageText.SetMarkupPermissive(article.Content);
|
||||
PageText.SetMessage(FormattedMessage.FromMarkupPermissive(article.Content), UserFormattableTags.BaseAllowedTags);
|
||||
|
||||
PageNum.Text = $"{targetNum}/{totalNum}";
|
||||
|
||||
NotificationSwitch.Text = Loc.GetString(notificationOn ? "news-read-ui-notification-on" : "news-read-ui-notification-off");
|
||||
|
||||
string shareTime = article.ShareTime.ToString(@"hh\:mm\:ss");
|
||||
var shareTime = article.ShareTime.ToString(@"hh\:mm\:ss");
|
||||
ShareTime.SetMarkup(Loc.GetString("news-read-ui-time-prefix-text") + " " + shareTime);
|
||||
|
||||
Author.SetMarkup(Loc.GetString("news-read-ui-author-prefix") + " " + (article.Author != null ? article.Author : Loc.GetString("news-read-ui-no-author")));
|
||||
var author = Loc.GetString("news-read-ui-author-prefix") + " " + (article.Author ?? Loc.GetString("news-read-ui-no-author"));
|
||||
Author.SetMessage(FormattedMessage.FromMarkupPermissive(author), UserFormattableTags.BaseAllowedTags);
|
||||
|
||||
Prev.Disabled = targetNum <= 1;
|
||||
Next.Disabled = targetNum >= totalNum;
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
using Content.Client.Chemistry.UI;
|
||||
using Content.Client.Items;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
namespace Content.Client.Chemistry.EntitySystems;
|
||||
|
||||
public sealed class HyposprayStatusControlSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainers = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Subs.ItemStatus<HyposprayComponent>(ent => new HyposprayStatusControl(ent, _solutionContainers));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Content.Client.Chemistry.UI;
|
||||
using Content.Client.Items;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Chemistry.EntitySystems;
|
||||
|
||||
public sealed class InjectorStatusControlSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainers = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Subs.ItemStatus<InjectorComponent>(injector => new InjectorStatusControl(injector, _solutionContainers, _prototypeManager));
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Content.Client.Chemistry.UI;
|
||||
using Content.Client.Items;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
namespace Content.Client.Chemistry.EntitySystems;
|
||||
|
||||
public sealed class InjectorSystem : SharedInjectorSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Subs.ItemStatus<InjectorComponent>(ent => new InjectorStatusControl(ent, SolutionContainer));
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,10 @@ namespace Content.Client.Chemistry.UI
|
||||
(uint) _window.BottleDosage.Value, _window.LabelLine));
|
||||
_window.BufferSortButton.OnPressed += _ => SendMessage(
|
||||
new ChemMasterSortingTypeCycleMessage());
|
||||
_window.OutputBufferDraw.OnPressed += _ => SendMessage(
|
||||
new ChemMasterOutputDrawSourceMessage(ChemMasterDrawSource.Internal));
|
||||
_window.OutputBeakerDraw.OnPressed += _ => SendMessage(
|
||||
new ChemMasterOutputDrawSourceMessage(ChemMasterDrawSource.External));
|
||||
|
||||
for (uint i = 0; i < _window.PillTypeButtons.Length; i++)
|
||||
{
|
||||
|
||||
@@ -79,10 +79,13 @@
|
||||
|
||||
<!-- Packaging -->
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'chem-master-window-packaging-text'}" />
|
||||
<Label Text="{Loc 'chem-master-output-source'}" StyleClasses="LabelSecondaryColor" Margin="0 0 5 0"/>
|
||||
<Button MinSize="80 0" Name="OutputBufferDraw" Access="Public" Text="{Loc 'chem-master-output-buffer-draw'}" ToggleMode="True" StyleClasses="OpenRight" />
|
||||
<Button MinSize="80 0" Name="OutputBeakerDraw" Access="Public" Text="{Loc 'chem-master-output-beaker-draw'}" ToggleMode="True" StyleClasses="OpenLeft" />
|
||||
<Control HorizontalExpand="True"/>
|
||||
<Label Text="{Loc 'chem-master-window-buffer-label'}" />
|
||||
<Label Name="BufferCurrentVolume" StyleClasses="LabelWeak" />
|
||||
<!-- Output Draw Source -->
|
||||
<Label Name="DrawSource"/>
|
||||
<Label Name="BufferCurrentVolume" StyleClasses="LabelSecondaryColor" />
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Wrap the packaging info-->
|
||||
|
||||
@@ -150,7 +150,17 @@ namespace Content.Client.Chemistry.UI
|
||||
// Ensure the Panel Info is updated, including UI elements for Buffer Volume, Output Container and so on
|
||||
UpdatePanelInfo(castState);
|
||||
|
||||
BufferCurrentVolume.Text = $" {castState.BufferCurrentVolume?.Int() ?? 0}u";
|
||||
switch (castState.DrawSource)
|
||||
{
|
||||
case ChemMasterDrawSource.Internal:
|
||||
SetBufferText(castState.BufferCurrentVolume, "chem-master-output-buffer-draw");
|
||||
break;
|
||||
case ChemMasterDrawSource.External:
|
||||
SetBufferText(castState.InputContainerInfo?.CurrentVolume, "chem-master-output-beaker-draw");
|
||||
break;
|
||||
default:
|
||||
throw new($"Chemmaster {castState.OutputContainerInfo} draw source is not set");
|
||||
}
|
||||
|
||||
InputEjectButton.Disabled = castState.InputContainerInfo is null;
|
||||
OutputEjectButton.Disabled = castState.OutputContainerInfo is null;
|
||||
@@ -168,9 +178,14 @@ namespace Content.Client.Chemistry.UI
|
||||
var holdsReagents = output?.Reagents != null;
|
||||
var pillNumberMax = holdsReagents ? 0 : remainingCapacity;
|
||||
var bottleAmountMax = holdsReagents ? remainingCapacity : 0;
|
||||
var bufferVolume = castState.BufferCurrentVolume?.Int() ?? 0;
|
||||
var outputVolume = castState.DrawSource switch
|
||||
{
|
||||
ChemMasterDrawSource.Internal => castState.BufferCurrentVolume?.Int() ?? 0,
|
||||
ChemMasterDrawSource.External => castState.InputContainerInfo?.CurrentVolume.Int() ?? 0,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
PillDosage.Value = (int)Math.Min(bufferVolume, castState.PillDosageLimit);
|
||||
PillDosage.Value = (int)Math.Min(outputVolume, castState.PillDosageLimit);
|
||||
|
||||
PillTypeButtons[castState.SelectedPillType].Pressed = true;
|
||||
|
||||
@@ -186,25 +201,35 @@ namespace Content.Client.Chemistry.UI
|
||||
// Avoid division by zero
|
||||
if (PillDosage.Value > 0)
|
||||
{
|
||||
PillNumber.Value = Math.Min(bufferVolume / PillDosage.Value, pillNumberMax);
|
||||
PillNumber.Value = Math.Min(outputVolume / PillDosage.Value, pillNumberMax);
|
||||
}
|
||||
else
|
||||
{
|
||||
PillNumber.Value = 0;
|
||||
}
|
||||
|
||||
BottleDosage.Value = Math.Min(bottleAmountMax, bufferVolume);
|
||||
BottleDosage.Value = Math.Min(bottleAmountMax, outputVolume);
|
||||
}
|
||||
/// <summary>
|
||||
/// Generate a product label based on reagents in the buffer.
|
||||
/// Generate a product label based on reagents in the buffer or beaker.
|
||||
/// </summary>
|
||||
/// <param name="state">State data sent by the server.</param>
|
||||
private string GenerateLabel(ChemMasterBoundUserInterfaceState state)
|
||||
{
|
||||
if (state.BufferCurrentVolume == 0)
|
||||
if (
|
||||
state.BufferCurrentVolume == 0 && state.DrawSource == ChemMasterDrawSource.Internal ||
|
||||
state.InputContainerInfo?.CurrentVolume == 0 && state.DrawSource == ChemMasterDrawSource.External ||
|
||||
state.InputContainerInfo?.Reagents == null
|
||||
)
|
||||
return "";
|
||||
|
||||
var reagent = state.BufferReagents.OrderBy(r => r.Quantity).First().Reagent;
|
||||
var reagent = (state.DrawSource switch
|
||||
{
|
||||
ChemMasterDrawSource.Internal => state.BufferReagents,
|
||||
ChemMasterDrawSource.External => state.InputContainerInfo.Reagents ?? [],
|
||||
_ => throw new($"Chemmaster {state.OutputContainerInfo} draw source is not set"),
|
||||
}).MinBy(r => r.Quantity)
|
||||
.Reagent;
|
||||
_prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? proto);
|
||||
return proto?.LocalizedName ?? "";
|
||||
}
|
||||
@@ -233,6 +258,8 @@ namespace Content.Client.Chemistry.UI
|
||||
_ => Loc.GetString("chem-master-window-sort-type-none")
|
||||
};
|
||||
|
||||
OutputBufferDraw.Pressed = state.DrawSource == ChemMasterDrawSource.Internal;
|
||||
OutputBeakerDraw.Pressed = state.DrawSource == ChemMasterDrawSource.External;
|
||||
|
||||
if (!state.BufferReagents.Any())
|
||||
{
|
||||
@@ -414,6 +441,12 @@ namespace Content.Client.Chemistry.UI
|
||||
get => LabelLineEdit.Text;
|
||||
set => LabelLineEdit.Text = value;
|
||||
}
|
||||
|
||||
private void SetBufferText(FixedPoint2? volume, string text)
|
||||
{
|
||||
BufferCurrentVolume.Text = $" {volume ?? FixedPoint2.Zero}u";
|
||||
DrawSource.Text = Loc.GetString(text);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ReagentButton : Button
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Chemistry.UI;
|
||||
|
||||
public sealed class HyposprayStatusControl : Control
|
||||
{
|
||||
private readonly Entity<HyposprayComponent> _parent;
|
||||
private readonly RichTextLabel _label;
|
||||
private readonly SharedSolutionContainerSystem _solutionContainers;
|
||||
|
||||
private FixedPoint2 PrevVolume;
|
||||
private FixedPoint2 PrevMaxVolume;
|
||||
private bool PrevOnlyAffectsMobs;
|
||||
|
||||
public HyposprayStatusControl(Entity<HyposprayComponent> parent, SharedSolutionContainerSystem solutionContainers)
|
||||
{
|
||||
_parent = parent;
|
||||
_solutionContainers = solutionContainers;
|
||||
_label = new RichTextLabel { StyleClasses = { StyleClass.ItemStatus } };
|
||||
AddChild(_label);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution))
|
||||
return;
|
||||
|
||||
// only updates the UI if any of the details are different than they previously were
|
||||
if (PrevVolume == solution.Volume
|
||||
&& PrevMaxVolume == solution.MaxVolume
|
||||
&& PrevOnlyAffectsMobs == _parent.Comp.OnlyAffectsMobs)
|
||||
return;
|
||||
|
||||
PrevVolume = solution.Volume;
|
||||
PrevMaxVolume = solution.MaxVolume;
|
||||
PrevOnlyAffectsMobs = _parent.Comp.OnlyAffectsMobs;
|
||||
|
||||
var modeStringLocalized = Loc.GetString((_parent.Comp.OnlyAffectsMobs && _parent.Comp.CanContainerDraw) switch
|
||||
{
|
||||
false => "hypospray-all-mode-text",
|
||||
true => "hypospray-mobs-only-mode-text",
|
||||
});
|
||||
|
||||
_label.SetMarkup(Loc.GetString("hypospray-volume-label",
|
||||
("currentVolume", solution.Volume),
|
||||
("totalVolume", solution.MaxVolume),
|
||||
("modeString", modeStringLocalized)));
|
||||
}
|
||||
}
|
||||
@@ -2,26 +2,32 @@ using Content.Client.Message;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Chemistry.UI;
|
||||
|
||||
public sealed class InjectorStatusControl : Control
|
||||
{
|
||||
private readonly IPrototypeManager _prototypeManager;
|
||||
|
||||
private readonly Entity<InjectorComponent> _parent;
|
||||
private readonly SharedSolutionContainerSystem _solutionContainers;
|
||||
private readonly RichTextLabel _label;
|
||||
|
||||
private FixedPoint2 PrevVolume;
|
||||
private FixedPoint2 PrevMaxVolume;
|
||||
private FixedPoint2 PrevTransferAmount;
|
||||
private InjectorToggleMode PrevToggleState;
|
||||
private FixedPoint2 _prevVolume;
|
||||
private FixedPoint2 _prevMaxVolume;
|
||||
private FixedPoint2? _prevTransferAmount;
|
||||
private InjectorBehavior _prevBehavior;
|
||||
|
||||
public InjectorStatusControl(Entity<InjectorComponent> parent, SharedSolutionContainerSystem solutionContainers)
|
||||
public InjectorStatusControl(Entity<InjectorComponent> parent, SharedSolutionContainerSystem solutionContainers, IPrototypeManager prototypeManager)
|
||||
{
|
||||
_prototypeManager = prototypeManager;
|
||||
|
||||
_parent = parent;
|
||||
_solutionContainers = solutionContainers;
|
||||
_label = new RichTextLabel { StyleClasses = { StyleClass.ItemStatus } };
|
||||
@@ -32,33 +38,38 @@ public sealed class InjectorStatusControl : Control
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution))
|
||||
if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution)
|
||||
|| !_prototypeManager.Resolve(_parent.Comp.ActiveModeProtoId, out var activeMode))
|
||||
return;
|
||||
|
||||
// only updates the UI if any of the details are different than they previously were
|
||||
if (PrevVolume == solution.Volume
|
||||
&& PrevMaxVolume == solution.MaxVolume
|
||||
&& PrevTransferAmount == _parent.Comp.CurrentTransferAmount
|
||||
&& PrevToggleState == _parent.Comp.ToggleState)
|
||||
if (_prevVolume == solution.Volume
|
||||
&& _prevMaxVolume == solution.MaxVolume
|
||||
&& _prevTransferAmount == _parent.Comp.CurrentTransferAmount
|
||||
&& _prevBehavior == activeMode.Behavior)
|
||||
return;
|
||||
|
||||
PrevVolume = solution.Volume;
|
||||
PrevMaxVolume = solution.MaxVolume;
|
||||
PrevTransferAmount = _parent.Comp.CurrentTransferAmount;
|
||||
PrevToggleState = _parent.Comp.ToggleState;
|
||||
_prevVolume = solution.Volume;
|
||||
_prevMaxVolume = solution.MaxVolume;
|
||||
_prevTransferAmount = _parent.Comp.CurrentTransferAmount;
|
||||
_prevBehavior = activeMode.Behavior;
|
||||
|
||||
// Update current volume and injector state
|
||||
var modeStringLocalized = Loc.GetString(_parent.Comp.ToggleState switch
|
||||
// Seeing transfer volume is only important for injectors that can change it.
|
||||
if (activeMode.TransferAmounts.Count > 1 && _parent.Comp.CurrentTransferAmount.HasValue)
|
||||
{
|
||||
InjectorToggleMode.Draw => "injector-draw-text",
|
||||
InjectorToggleMode.Inject => "injector-inject-text",
|
||||
_ => "injector-invalid-injector-toggle-mode"
|
||||
});
|
||||
|
||||
_label.SetMarkup(Loc.GetString("injector-volume-label",
|
||||
("currentVolume", solution.Volume),
|
||||
("totalVolume", solution.MaxVolume),
|
||||
("modeString", modeStringLocalized),
|
||||
("transferVolume", _parent.Comp.CurrentTransferAmount)));
|
||||
_label.SetMarkup(Loc.GetString("injector-volume-transfer-label",
|
||||
("currentVolume", solution.Volume),
|
||||
("totalVolume", solution.MaxVolume),
|
||||
("modeString", Loc.GetString(activeMode.Name)),
|
||||
("transferVolume", _parent.Comp.CurrentTransferAmount.Value)));
|
||||
}
|
||||
else
|
||||
{
|
||||
_label.SetMarkup(Loc.GetString("injector-volume-label",
|
||||
("currentVolume", solution.Volume),
|
||||
("totalVolume", solution.MaxVolume),
|
||||
("modeString", Loc.GetString(activeMode.Name))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Client.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the a <see cref="CCVars.DebugQuickInspect"/> CVar to the name of a component, which allows the client to quickly open a VV window for that component
|
||||
/// by using the Alt+C or Alt+B hotkeys.
|
||||
/// </summary>
|
||||
public sealed class QuickInspectCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
|
||||
public override string Command => "quickinspect";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("shell-wrong-arguments-number"));
|
||||
return;
|
||||
}
|
||||
|
||||
_configurationManager.SetCVar(CCVars.DebugQuickInspect, args[0]);
|
||||
|
||||
var serverKey = _inputManager.GetKeyFunctionButtonString(ContentKeyFunctions.InspectServerComponent);
|
||||
var clientKey = _inputManager.GetKeyFunctionButtonString(ContentKeyFunctions.InspectClientComponent);
|
||||
shell.WriteLine(Loc.GetString($"cmd-quickinspect-success", ("component", args[0]), ("serverKeybind", serverKey), ("clientKeybind", clientKey)));
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
if (args.Length == 1)
|
||||
{
|
||||
// Not ideal since it only shows client-side components, but you can still type in any name you want.
|
||||
// If you know how to get server component names on the client then please fix this.
|
||||
var options = EntityManager.ComponentFactory.AllRegisteredTypes
|
||||
.Select(p => new CompletionOption(
|
||||
EntityManager.ComponentFactory.GetComponentName(p)
|
||||
));
|
||||
|
||||
return CompletionResult.FromOptions(options);
|
||||
}
|
||||
|
||||
return CompletionResult.Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Content.Client.Wall;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Client.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Shows the area in which entities with <see cref="Content.Shared.Wall.WallMountComponent" /> can be interacted from.
|
||||
/// </summary>
|
||||
public sealed class ShowWallmountsCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||
|
||||
public override string Command => "showwallmounts";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var existing = _overlay.RemoveOverlay<WallmountDebugOverlay>();
|
||||
if (!existing)
|
||||
_overlay.AddOverlay(new WallmountDebugOverlay());
|
||||
|
||||
shell.WriteLine(Loc.GetString("cmd-showwallmounts-status", ("status", !existing)));
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,10 @@ namespace Content.Client.Construction.UI
|
||||
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
private readonly ISawmill _sawmill;
|
||||
|
||||
private readonly IConstructionMenuView _constructionView;
|
||||
private readonly EntityWhitelistSystem _whitelistSystem;
|
||||
@@ -90,6 +93,7 @@ namespace Content.Client.Construction.UI
|
||||
_constructionView = new ConstructionMenu();
|
||||
_whitelistSystem = _entManager.System<EntityWhitelistSystem>();
|
||||
_spriteSystem = _entManager.System<SpriteSystem>();
|
||||
_sawmill = _logManager.GetSawmill("construction.ui");
|
||||
|
||||
// This is required so that if we load after the system is initialized, we can bind to it immediately
|
||||
if (_systemManager.TryGetEntitySystem<ConstructionSystem>(out var constructionSystem))
|
||||
@@ -284,7 +288,7 @@ namespace Content.Client.Construction.UI
|
||||
|
||||
if (!_constructionSystem!.TryGetRecipePrototype(recipe.ID, out var targetProtoId))
|
||||
{
|
||||
Logger.Error("Cannot find the target prototype in the recipe cache with the id \"{0}\" of {1}.",
|
||||
_sawmill.Error("Cannot find the target prototype in the recipe cache with the id \"{0}\" of {1}.",
|
||||
recipe.ID,
|
||||
nameof(ConstructionPrototype));
|
||||
continue;
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<LangVersion>12</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>..\bin\Content.Client\</OutputPath>
|
||||
<OutputType Condition="'$(FullRelease)' != 'True'">Exe</OutputType>
|
||||
<WarningsAsErrors>RA0032;nullable</WarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<Configurations>Debug;Release;Tools;DebugOpt</Configurations>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
</PropertyGroup>
|
||||
<Import Project="../MSBuild/Content.props" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Nett" />
|
||||
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" />
|
||||
<PackageReference Include="Pidgin" />
|
||||
<PackageReference Include="Robust.Shared.AuthLib" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\RobustToolbox\Imports\Lidgren.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Client.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Shared.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RobustToolbox\Lidgren.Network\Lidgren.Network.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
|
||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
|
||||
<ProjectReference Include="..\Corvax\Content.Corvax.Interfaces.Shared\Content.Corvax.Interfaces.Shared.csproj" />
|
||||
<ProjectReference Include="..\Corvax\Content.Corvax.Interfaces.Client\Content.Corvax.Interfaces.Client.csproj" />
|
||||
|
||||
@@ -87,9 +87,14 @@ public sealed class TTSSystem : EntitySystem
|
||||
|
||||
if (ev.SourceUid != null)
|
||||
{
|
||||
if (!TryGetEntity(ev.SourceUid.Value, out _))
|
||||
return;
|
||||
var sourceUid = GetEntity(ev.SourceUid.Value);
|
||||
|
||||
if (!Exists(sourceUid) || Deleted(sourceUid))
|
||||
{
|
||||
_contentRoot.RemoveFile(filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
_audio.PlayEntity(audioResource.AudioStream, sourceUid, soundSpecifier, audioParams);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
using Content.Shared.DeviceLinking.Systems;
|
||||
|
||||
namespace Content.Client.DeviceLinking.Systems;
|
||||
|
||||
public sealed class RandomGateSystem : SharedRandomGateSystem;
|
||||
@@ -0,0 +1,37 @@
|
||||
using Content.Shared.DeviceLinking;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.DeviceLinking.UI;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class RandomGateBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private RandomGateSetupWindow? _window;
|
||||
|
||||
public RandomGateBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { }
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_window = this.CreateWindow<RandomGateSetupWindow>();
|
||||
_window.OnApplyPressed += OnProbabilityChanged;
|
||||
}
|
||||
|
||||
private void OnProbabilityChanged(string value)
|
||||
{
|
||||
if (!float.TryParse(value, out var probability))
|
||||
return;
|
||||
|
||||
SendPredictedMessage(new RandomGateProbabilityChangedMessage(probability));
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
if (state is not RandomGateBoundUserInterfaceState castState || _window == null)
|
||||
return;
|
||||
|
||||
_window.SetProbability(castState.SuccessProbability * 100);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'random-gate-menu-setup'}"
|
||||
MinSize="260 115">
|
||||
<BoxContainer Orientation="Vertical" Margin="5" SeparationOverride="10">
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Label Text="{Loc 'random-gate-menu-settings'}" VerticalAlignment="Center" />
|
||||
<Control HorizontalExpand="True" />
|
||||
<LineEdit Name="ProbabilityInput" MinSize="70 0" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Control HorizontalExpand="True" />
|
||||
<Button Name="ApplyButton"
|
||||
Text="{Loc 'random-gate-menu-apply'}"
|
||||
HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
@@ -0,0 +1,28 @@
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.DeviceLinking.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Window for setting up the random gate probability.
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class RandomGateSetupWindow : FancyWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the "Apply" button is pressed.
|
||||
/// </summary>
|
||||
public event Action<string>? OnApplyPressed;
|
||||
|
||||
public RandomGateSetupWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
ApplyButton.OnPressed += _ => OnApplyPressed?.Invoke(ProbabilityInput.Text);
|
||||
}
|
||||
|
||||
public void SetProbability(float probability)
|
||||
{
|
||||
ProbabilityInput.Text = probability.ToString("0.00");
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,6 @@ namespace Content.Client.Entry
|
||||
_componentFactory.IgnoreMissingComponents();
|
||||
|
||||
// Do not add to these, they are legacy.
|
||||
_componentFactory.RegisterClass<SharedGravityGeneratorComponent>();
|
||||
_componentFactory.RegisterClass<SharedAmeControllerComponent>();
|
||||
// Do not add to the above, they are legacy
|
||||
|
||||
@@ -113,7 +112,6 @@ namespace Content.Client.Entry
|
||||
_prototypeManager.RegisterIgnore("htnPrimitive");
|
||||
_prototypeManager.RegisterIgnore("gameMap");
|
||||
_prototypeManager.RegisterIgnore("gameMapPool");
|
||||
_prototypeManager.RegisterIgnore("lobbyBackground");
|
||||
_prototypeManager.RegisterIgnore("gamePreset");
|
||||
_prototypeManager.RegisterIgnore("noiseChannel");
|
||||
_prototypeManager.RegisterIgnore("playerConnectionWhitelist");
|
||||
|
||||
@@ -73,7 +73,19 @@ public sealed class PuddleSystem : SharedPuddleSystem
|
||||
// Maybe someday we'll have clientside prediction for entity spawning, but not today.
|
||||
// Until then, these methods do nothing on the client.
|
||||
/// <inheritdoc/>
|
||||
public override bool TrySplashSpillAt(EntityUid uid, EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true, EntityUid? user = null)
|
||||
public override bool TrySplashSpillAt(Entity<SpillableComponent?> entity, EntityCoordinates coordinates, out EntityUid puddleUid, out Solution solution, bool sound = true, EntityUid? user = null)
|
||||
{
|
||||
puddleUid = EntityUid.Invalid;
|
||||
solution = new Solution();
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool TrySplashSpillAt(EntityUid entity,
|
||||
EntityCoordinates coordinates,
|
||||
Solution spilled,
|
||||
out EntityUid puddleUid,
|
||||
bool sound = true,
|
||||
EntityUid? user = null)
|
||||
{
|
||||
puddleUid = EntityUid.Invalid;
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
using Content.Shared.Fluids.Components;
|
||||
using Content.Shared.Fluids.EntitySystems;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Client.Fluids;
|
||||
|
||||
public sealed class SpraySystem : SharedSpraySystem;
|
||||
@@ -11,6 +11,7 @@ using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Audio;
|
||||
using Content.Shared.GameTicking.Prototypes;
|
||||
|
||||
namespace Content.Client.GameTicking.Managers
|
||||
{
|
||||
@@ -28,7 +29,7 @@ namespace Content.Client.GameTicking.Managers
|
||||
[ViewVariables] public bool AreWeReady { get; private set; }
|
||||
[ViewVariables] public bool IsGameStarted { get; private set; }
|
||||
[ViewVariables] public ResolvedSoundSpecifier? RestartSound { get; private set; }
|
||||
[ViewVariables] public string? LobbyBackground { get; private set; }
|
||||
[ViewVariables] public ProtoId<LobbyBackgroundPrototype>? LobbyBackground { get; private set; }
|
||||
[ViewVariables] public bool DisallowedLateJoin { get; private set; }
|
||||
[ViewVariables] public string? ServerInfoBlob { get; private set; }
|
||||
[ViewVariables] public TimeSpan StartTime { get; private set; }
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Numerics;
|
||||
using Content.Client.Clickable;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.Viewport;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.ComponentTrees;
|
||||
using Robust.Client.GameObjects;
|
||||
@@ -13,6 +14,7 @@ using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Input;
|
||||
@@ -40,6 +42,7 @@ namespace Content.Client.Gameplay
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IViewVariablesManager _vvm = default!;
|
||||
[Dependency] private readonly IConsoleHost _conHost = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
|
||||
private ClickableEntityComparer _comparer = default!;
|
||||
|
||||
@@ -83,6 +86,8 @@ namespace Content.Client.Gameplay
|
||||
_comparer = new ClickableEntityComparer();
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.InspectEntity, new PointerInputCmdHandler(HandleInspect, outsidePrediction: true))
|
||||
.Bind(ContentKeyFunctions.InspectServerComponent, new PointerInputCmdHandler(HandleInspectServerComponent, outsidePrediction: true))
|
||||
.Bind(ContentKeyFunctions.InspectClientComponent, new PointerInputCmdHandler(HandleInspectClientComponent, outsidePrediction: true))
|
||||
.Register<GameplayStateBase>();
|
||||
}
|
||||
|
||||
@@ -99,6 +104,21 @@ namespace Content.Client.Gameplay
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool HandleInspectServerComponent(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
|
||||
{
|
||||
var component = _configurationManager.GetCVar(CCVars.DebugQuickInspect);
|
||||
if (_entityManager.TryGetNetEntity(uid, out var net))
|
||||
_conHost.ExecuteCommand($"vv /entity/{net.Value.Id}/{component}");
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool HandleInspectClientComponent(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
|
||||
{
|
||||
var component = _configurationManager.GetCVar(CCVars.DebugQuickInspect);
|
||||
_conHost.ExecuteCommand($"vv /c/entity/{uid}/{component}");
|
||||
return true;
|
||||
}
|
||||
|
||||
public EntityUid? GetClickedEntity(MapCoordinates coordinates)
|
||||
{
|
||||
return GetClickedEntity(coordinates, _eyeManager.CurrentEye);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.Gravity;
|
||||
|
||||
namespace Content.Client.Gravity;
|
||||
|
||||
public sealed class GravityGeneratorSystem : SharedGravityGeneratorSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -12,14 +12,14 @@ public sealed partial class GravitySystem : SharedGravitySystem
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<SharedGravityGeneratorComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
SubscribeLocalEvent<GravityGeneratorComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
InitializeShake();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the visible state of gravity generators are synced with their sprites.
|
||||
/// </summary>
|
||||
private void OnAppearanceChange(EntityUid uid, SharedGravityGeneratorComponent comp, ref AppearanceChangeEvent args)
|
||||
private void OnAppearanceChange(EntityUid uid, GravityGeneratorComponent comp, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
@@ -42,7 +42,7 @@ public sealed class GuidebookRichPrototypeLink : Control, IPrototypeLinkControl
|
||||
public void SetMessage(FormattedMessage message)
|
||||
{
|
||||
_message = message;
|
||||
_richTextLabel.SetMessage(_message);
|
||||
_richTextLabel.SetMessage(_message, tagsAllowed: null);
|
||||
}
|
||||
|
||||
public IPrototype? LinkedPrototype { get; set; }
|
||||
|
||||
@@ -82,7 +82,7 @@ public sealed partial class DocumentParsingManager
|
||||
}
|
||||
|
||||
msg.Pop();
|
||||
rt.SetMessage(msg);
|
||||
rt.SetMessage(msg, tagsAllowed: null);
|
||||
return rt;
|
||||
},
|
||||
TextParser)
|
||||
|
||||
@@ -18,7 +18,7 @@ public sealed partial class InfoSection : BoxContainer
|
||||
{
|
||||
TitleLabel.Text = title;
|
||||
if (markup)
|
||||
Content.SetMessage(FormattedMessage.FromMarkupOrThrow(text.Trim()));
|
||||
Content.SetMessage(FormattedMessage.FromMarkupOrThrow(text.Trim()), tagsAllowed: null);
|
||||
else
|
||||
Content.SetMessage(text);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Content.Client.Info
|
||||
}
|
||||
public void SetInfoBlob(string markup)
|
||||
{
|
||||
_richTextLabel.SetMessage(FormattedMessage.FromMarkupOrThrow(markup));
|
||||
_richTextLabel.SetMessage(FormattedMessage.FromMarkupOrThrow(markup), tagsAllowed: null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ namespace Content.Client.Input
|
||||
common.AddFunction(ContentKeyFunctions.ZoomIn);
|
||||
common.AddFunction(ContentKeyFunctions.ResetZoom);
|
||||
common.AddFunction(ContentKeyFunctions.InspectEntity);
|
||||
common.AddFunction(ContentKeyFunctions.InspectServerComponent);
|
||||
common.AddFunction(ContentKeyFunctions.InspectClientComponent);
|
||||
common.AddFunction(ContentKeyFunctions.ToggleRoundEndSummaryWindow);
|
||||
|
||||
// Not in engine, because engine cannot check for sanbox/admin status before starting placement.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.Kitchen.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Client.Kitchen.EntitySystems;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class ReagentGrinderSystem : SharedReagentGrinderSystem;
|
||||
|
||||
@@ -20,8 +20,10 @@ namespace Content.Client.Launcher
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IClipboardManager _clipboard = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private LauncherConnectingGui? _control;
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
private Page _currentPage;
|
||||
private string? _connectFailReason;
|
||||
@@ -61,6 +63,8 @@ namespace Content.Client.Launcher
|
||||
{
|
||||
_control = new LauncherConnectingGui(this, _random, _prototypeManager, _cfg, _clipboard);
|
||||
|
||||
_sawmill = _logManager.GetSawmill("launcher-ui");
|
||||
|
||||
_userInterfaceManager.StateRoot.AddChild(_control);
|
||||
|
||||
_clientNetManager.ConnectFailed += OnConnectFailed;
|
||||
@@ -115,12 +119,12 @@ namespace Content.Client.Launcher
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.InfoS("launcher-ui", $"Redial not possible, no Ss14Address");
|
||||
_sawmill.Info($"Redial not possible, no Ss14Address");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorS("launcher-ui", $"Redial exception: {ex}");
|
||||
_sawmill.Error($"Redial exception: {ex}");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Lobby
|
||||
@@ -28,6 +29,7 @@ namespace Content.Client.Lobby
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IVoteManager _voteManager = default!;
|
||||
[Dependency] private readonly ClientsidePlaytimeTrackingManager _playtimeTracking = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||
|
||||
private ClientGameTicker _gameTicker = default!;
|
||||
private ContentAudioSystem _contentAudioSystem = default!;
|
||||
@@ -271,15 +273,22 @@ namespace Content.Client.Lobby
|
||||
|
||||
private void UpdateLobbyBackground()
|
||||
{
|
||||
if (_gameTicker.LobbyBackground != null)
|
||||
if (_protoMan.TryIndex(_gameTicker.LobbyBackground, out var proto))
|
||||
{
|
||||
Lobby!.Background.Texture = _resourceCache.GetResource<TextureResource>(_gameTicker.LobbyBackground );
|
||||
Lobby!.Background.Texture = _resourceCache.GetResource<TextureResource>(proto.Background);
|
||||
|
||||
var markup = Loc.GetString("lobby-state-background-text",
|
||||
("backgroundTitle", Loc.GetString(proto.Title)),
|
||||
("backgroundArtist", Loc.GetString(proto.Artist)));
|
||||
|
||||
Lobby!.LobbyBackground.SetMarkup(markup);
|
||||
}
|
||||
else
|
||||
{
|
||||
Lobby!.Background.Texture = null;
|
||||
}
|
||||
|
||||
Lobby!.LobbyBackground.SetMarkup(Loc.GetString("lobby-state-background-no-background-text"));
|
||||
}
|
||||
}
|
||||
|
||||
private void SetReady(bool newReady)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Prometheus;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
@@ -51,10 +51,13 @@
|
||||
<!-- Vertical Padding-->
|
||||
<Control VerticalExpand="True" />
|
||||
<!-- Left Bot Panel -->
|
||||
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Bottom">
|
||||
<BoxContainer Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Bottom">
|
||||
<info:DevInfoBanner Name="DevInfoBanner" VerticalExpand="false" Margin="3 3 3 3" />
|
||||
<PanelContainer StyleClasses="BackgroundPanel">
|
||||
<RichTextLabel Name="LobbySong" Access="Public" HorizontalAlignment="Center" />
|
||||
<PanelContainer StyleClasses="BackgroundPanel" Margin="3 3 3 3">
|
||||
<BoxContainer Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="3 3 3 3">
|
||||
<RichTextLabel Name="LobbySong" Access="Public" HorizontalAlignment="Left" />
|
||||
<RichTextLabel Name="LobbyBackground" Access="Public" HorizontalAlignment="Left" />
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using Content.Client.Message;
|
||||
using Content.Client.RichText;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.RichText;
|
||||
using Content.Shared.MassMedia.Systems;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.RichText;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -81,7 +84,9 @@ public sealed partial class ArticleEditorPanel : Control
|
||||
|
||||
TextEditPanel.Visible = !_preview;
|
||||
PreviewPanel.Visible = _preview;
|
||||
PreviewLabel.SetMarkupPermissive(Rope.Collapse(ContentField.TextRope));
|
||||
|
||||
var articleBody = Rope.Collapse(ContentField.TextRope);
|
||||
PreviewLabel.SetMessage(FormattedMessage.FromMarkupPermissive(articleBody), UserFormattableTags.BaseAllowedTags);
|
||||
}
|
||||
|
||||
private void OnCancel(BaseButton.ButtonEventArgs eventArgs)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
using Content.Shared.NameIdentifier;
|
||||
|
||||
namespace Content.Client.NameIdentifier;
|
||||
|
||||
public sealed class NameIdentifierSystem : SharedNameIdentifierSystem;
|
||||
@@ -269,6 +269,8 @@ namespace Content.Client.Options.UI.Tabs
|
||||
AddButton(EngineKeyFunctions.ShowDebugMonitors);
|
||||
AddButton(EngineKeyFunctions.HideUI);
|
||||
AddButton(ContentKeyFunctions.InspectEntity);
|
||||
AddButton(ContentKeyFunctions.InspectServerComponent);
|
||||
AddButton(ContentKeyFunctions.InspectClientComponent);
|
||||
|
||||
AddHeader("ui-options-header-text-cursor");
|
||||
AddButton(EngineKeyFunctions.TextCursorLeft);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.RichText;
|
||||
using Content.Shared.Paper;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -38,16 +39,6 @@ namespace Content.Client.Paper.UI
|
||||
// we're able to resize this UI or not. Default to everything enabled:
|
||||
private DragMode _allowedResizeModes = ~DragMode.None;
|
||||
|
||||
private readonly Type[] _allowedTags = new Type[] {
|
||||
typeof(BoldItalicTag),
|
||||
typeof(BoldTag),
|
||||
typeof(BulletTag),
|
||||
typeof(ColorTag),
|
||||
typeof(HeadingTag),
|
||||
typeof(ItalicTag),
|
||||
typeof(MonoTag)
|
||||
};
|
||||
|
||||
public event Action<string>? OnSaved;
|
||||
|
||||
private int _MaxInputLength = -1;
|
||||
@@ -280,7 +271,7 @@ namespace Content.Client.Paper.UI
|
||||
{
|
||||
msg.AddMarkupPermissive("\r\n");
|
||||
}
|
||||
WrittenTextLabel.SetMessage(msg, _allowedTags, DefaultTextColor);
|
||||
WrittenTextLabel.SetMessage(msg, UserFormattableTags.BaseAllowedTags, DefaultTextColor);
|
||||
|
||||
WrittenTextLabel.Visible = !isEditing && state.Text.Length > 0;
|
||||
BlankPaperIndicator.Visible = !isEditing && state.Text.Length == 0;
|
||||
|
||||
@@ -98,10 +98,13 @@ public sealed class ParallaxManager : IParallaxManager
|
||||
}
|
||||
else
|
||||
{
|
||||
layers = await Task.WhenAll(
|
||||
// Explicitly allocate params array to avoid sandbox violation since C# 14.
|
||||
var tasks = new[]
|
||||
{
|
||||
LoadParallaxLayers(parallaxPrototype.Layers, loadedLayers, cancel),
|
||||
LoadParallaxLayers(parallaxPrototype.LayersLQ, loadedLayers, cancel)
|
||||
);
|
||||
LoadParallaxLayers(parallaxPrototype.LayersLQ, loadedLayers, cancel),
|
||||
};
|
||||
layers = await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
cancel.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<!-- Power On/Off -->
|
||||
<Label Text="{Loc 'apc-menu-breaker-label'}" HorizontalExpand="True"
|
||||
StyleClasses="highlight" MinWidth="120"/>
|
||||
<BoxContainer Orientation="Horizontal" MinWidth="90">
|
||||
<BoxContainer Orientation="Horizontal" MinWidth="150">
|
||||
<Button Name="BreakerButton" Text="{Loc 'apc-menu-breaker-button'}" HorizontalExpand="True" ToggleMode="True"/>
|
||||
</BoxContainer>
|
||||
<!--Charging Status-->
|
||||
|
||||
@@ -40,7 +40,14 @@ namespace Content.Client.Power.APC.UI
|
||||
|
||||
if (PowerLabel != null)
|
||||
{
|
||||
PowerLabel.Text = Loc.GetString("apc-menu-power-state-label-text", ("power", castState.Power));
|
||||
if (castState.Tripped)
|
||||
{
|
||||
PowerLabel.Text = Loc.GetString("apc-menu-power-state-label-tripped");
|
||||
}
|
||||
else
|
||||
{
|
||||
PowerLabel.Text = Loc.GetString("apc-menu-power-state-label-text", ("power", castState.Power), ("maxLoad", castState.MaxLoad));
|
||||
}
|
||||
}
|
||||
|
||||
if (ExternalPowerStateLabel != null)
|
||||
|
||||
@@ -18,7 +18,9 @@ public sealed class ActivatableUIRequiresPowerSystem : SharedActivatableUIRequir
|
||||
return;
|
||||
}
|
||||
|
||||
_popup.PopupClient(Loc.GetString("base-computer-ui-component-not-powered", ("machine", ent.Owner)), args.User, args.User);
|
||||
if (!args.Silent)
|
||||
_popup.PopupClient(Loc.GetString("base-computer-ui-component-not-powered", ("machine", ent.Owner)), args.User, args.User);
|
||||
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
using Content.Shared.Power.EntitySystems;
|
||||
|
||||
namespace Content.Client.Power.EntitySystems;
|
||||
|
||||
public sealed class ChargerSystem : SharedChargerSystem;
|
||||
@@ -1,67 +0,0 @@
|
||||
using Content.Shared.PowerCell;
|
||||
using Content.Shared.PowerCell.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.PowerCell;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class PowerCellSystem : SharedPowerCellSystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PowerCellVisualsComponent, AppearanceChangeEvent>(OnPowerCellVisualsChange);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool HasActivatableCharge(EntityUid uid, PowerCellDrawComponent? battery = null, PowerCellSlotComponent? cell = null,
|
||||
EntityUid? user = null)
|
||||
{
|
||||
if (!Resolve(uid, ref battery, ref cell, false))
|
||||
return true;
|
||||
|
||||
return battery.CanUse;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool HasDrawCharge(
|
||||
EntityUid uid,
|
||||
PowerCellDrawComponent? battery = null,
|
||||
PowerCellSlotComponent? cell = null,
|
||||
EntityUid? user = null)
|
||||
{
|
||||
if (!Resolve(uid, ref battery, ref cell, false))
|
||||
return true;
|
||||
|
||||
return battery.CanDraw;
|
||||
}
|
||||
|
||||
private void OnPowerCellVisualsChange(EntityUid uid, PowerCellVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if (!_sprite.LayerExists((uid, args.Sprite), PowerCellVisualLayers.Unshaded))
|
||||
return;
|
||||
|
||||
// If no appearance data is set, rely on whatever existing sprite state is set being correct.
|
||||
if (!_appearance.TryGetData<byte>(uid, PowerCellVisuals.ChargeLevel, out var level, args.Component))
|
||||
return;
|
||||
|
||||
var positiveCharge = level > 0;
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), PowerCellVisualLayers.Unshaded, positiveCharge);
|
||||
|
||||
if (positiveCharge)
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), PowerCellVisualLayers.Unshaded, $"o{level}");
|
||||
}
|
||||
|
||||
private enum PowerCellVisualLayers : byte
|
||||
{
|
||||
Base,
|
||||
Unshaded,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Content.Client.PowerCell;
|
||||
|
||||
/// <summary>
|
||||
/// Sprite layers for power cells.
|
||||
/// For use with the generic visualizer.
|
||||
/// </summary>
|
||||
public enum PowerCellVisualLayers : byte
|
||||
{
|
||||
Base,
|
||||
Unshaded,
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
namespace Content.Client.PowerCell;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class PowerCellVisualsComponent : Component {}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Power.Components;
|
||||
|
||||
namespace Content.Client.PowerCell;
|
||||
|
||||
@@ -9,15 +9,13 @@ public sealed partial class PowerChargerVisualsComponent : Component
|
||||
/// <summary>
|
||||
/// The base sprite state used if the power cell charger does not contain a power cell.
|
||||
/// </summary>
|
||||
[DataField("emptyState")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public string EmptyState = "empty";
|
||||
|
||||
/// <summary>
|
||||
/// The base sprite state used if the power cell charger contains a power cell.
|
||||
/// </summary>
|
||||
[DataField("occupiedState")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public string OccupiedState = "full";
|
||||
|
||||
/// <summary>
|
||||
@@ -27,8 +25,7 @@ public sealed partial class PowerChargerVisualsComponent : Component
|
||||
/// <see cref="CellChargerStatus.Charging"/> Maps to the state used when the charger is charging a power cell.
|
||||
/// <see cref="CellChargerStatus.Charged"/> Maps to the state used when the charger contains a fully charged power cell.
|
||||
/// </summary>
|
||||
[DataField("lightStates")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public Dictionary<CellChargerStatus, string> LightStates = new()
|
||||
{
|
||||
[CellChargerStatus.Off] = "light-off",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Power.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.PowerCell;
|
||||
|
||||
@@ -20,7 +20,7 @@ public sealed class RCDConstructionGhostSystem : EntitySystem
|
||||
[Dependency] private readonly IPlacementManager _placementManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
[Dependency] private readonly HandsSystem _hands = default!;
|
||||
|
||||
|
||||
private Direction _placementDirection = default;
|
||||
|
||||
public override void Update(float frameTime)
|
||||
@@ -42,6 +42,11 @@ public sealed class RCDConstructionGhostSystem : EntitySystem
|
||||
|
||||
var heldEntity = _hands.GetActiveItem(player);
|
||||
|
||||
// Don't open the placement overlay for client-side RCDs.
|
||||
// This may happen when predictively spawning one in your hands.
|
||||
if (heldEntity != null && IsClientSide(heldEntity.Value))
|
||||
return;
|
||||
|
||||
if (!TryComp<RCDComponent>(heldEntity, out var rcd))
|
||||
{
|
||||
// If the player was holding an RCD, but is no longer, cancel placement
|
||||
@@ -69,7 +74,7 @@ public sealed class RCDConstructionGhostSystem : EntitySystem
|
||||
MobUid = heldEntity.Value,
|
||||
PlacementOption = PlacementMode,
|
||||
EntityType = prototype.Prototype,
|
||||
Range = (int) Math.Ceiling(SharedInteractionSystem.InteractionRange),
|
||||
Range = (int)Math.Ceiling(SharedInteractionSystem.InteractionRange),
|
||||
IsTile = (prototype.Mode == RcdMode.ConstructTile),
|
||||
UseEditorContext = false,
|
||||
};
|
||||
|
||||
@@ -35,6 +35,7 @@ public sealed class DoorRemoteStatusControl(Entity<DoorRemoteComponent> ent) : C
|
||||
OperatingMode.OpenClose => "door-remote-open-close-text",
|
||||
OperatingMode.ToggleBolts => "door-remote-toggle-bolt-text",
|
||||
OperatingMode.ToggleEmergencyAccess => "door-remote-emergency-access-text",
|
||||
OperatingMode.ToggleOvercharge => "door-remote-toggle-eletrify-text",
|
||||
_ => "door-remote-invalid-text"
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
using Content.Client.UserInterface.RichText;
|
||||
using Robust.Client.UserInterface.RichText;
|
||||
|
||||
namespace Content.Client.RichText;
|
||||
|
||||
/// <summary>
|
||||
/// Contains rules for what markup tags are allowed to be used by players.
|
||||
/// </summary>
|
||||
public static class UserFormattableTags
|
||||
{
|
||||
/// <summary>
|
||||
/// The basic set of "rich text" formatting tags that shouldn't cause any issues.
|
||||
/// Limit user rich text to these by default.
|
||||
/// </summary>
|
||||
public static readonly Type[] BaseAllowedTags =
|
||||
[
|
||||
typeof(BoldItalicTag),
|
||||
typeof(BoldTag),
|
||||
typeof(BulletTag),
|
||||
typeof(ColorTag),
|
||||
typeof(HeadingTag),
|
||||
typeof(ItalicTag),
|
||||
typeof(MonoTag),
|
||||
];
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
using Content.Shared.Rootable;
|
||||
|
||||
namespace Content.Client.Rootable;
|
||||
|
||||
public sealed class RootableSystem : SharedRootableSystem;
|
||||
@@ -23,7 +23,6 @@ public sealed class IFFConsoleBoundUserInterface : BoundUserInterface
|
||||
|
||||
_window = this.CreateWindowCenteredLeft<IFFConsoleWindow>();
|
||||
_window.ShowIFF += SendIFFMessage;
|
||||
_window.ShowVessel += SendVesselMessage;
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
@@ -44,14 +43,6 @@ public sealed class IFFConsoleBoundUserInterface : BoundUserInterface
|
||||
});
|
||||
}
|
||||
|
||||
private void SendVesselMessage(bool obj)
|
||||
{
|
||||
SendMessage(new IFFShowVesselMessage()
|
||||
{
|
||||
Show = obj,
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
@@ -9,12 +9,6 @@
|
||||
<Button Name="ShowIFFOnButton" Text="{Loc 'iff-console-on'}" StyleClasses="OpenRight" />
|
||||
<Button Name="ShowIFFOffButton" Text="{Loc 'iff-console-off'}" StyleClasses="OpenLeft" />
|
||||
</BoxContainer>
|
||||
|
||||
<Label Name="ShowVesselLabel" Text="{Loc 'iff-console-show-vessel-label'}" HorizontalExpand="True" StyleClasses="highlight" />
|
||||
<BoxContainer Orientation="Horizontal" MinWidth="120">
|
||||
<Button Name="ShowVesselOnButton" Text="{Loc 'iff-console-on'}" StyleClasses="OpenRight" />
|
||||
<Button Name="ShowVesselOffButton" Text="{Loc 'iff-console-off'}" StyleClasses="OpenLeft" />
|
||||
</BoxContainer>
|
||||
</GridContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
|
||||
@@ -13,9 +13,7 @@ public sealed partial class IFFConsoleWindow : FancyWindow,
|
||||
IComputerWindow<IFFConsoleBoundUserInterfaceState>
|
||||
{
|
||||
private readonly ButtonGroup _showIFFButtonGroup = new();
|
||||
private readonly ButtonGroup _showVesselButtonGroup = new();
|
||||
public event Action<bool>? ShowIFF;
|
||||
public event Action<bool>? ShowVessel;
|
||||
|
||||
public IFFConsoleWindow()
|
||||
{
|
||||
@@ -24,11 +22,6 @@ public sealed partial class IFFConsoleWindow : FancyWindow,
|
||||
ShowIFFOnButton.Group = _showIFFButtonGroup;
|
||||
ShowIFFOnButton.OnPressed += args => ShowIFFPressed(true);
|
||||
ShowIFFOffButton.OnPressed += args => ShowIFFPressed(false);
|
||||
|
||||
ShowVesselOffButton.Group = _showVesselButtonGroup;
|
||||
ShowVesselOnButton.Group = _showVesselButtonGroup;
|
||||
ShowVesselOnButton.OnPressed += args => ShowVesselPressed(true);
|
||||
ShowVesselOffButton.OnPressed += args => ShowVesselPressed(false);
|
||||
}
|
||||
|
||||
private void ShowIFFPressed(bool pressed)
|
||||
@@ -36,19 +29,14 @@ public sealed partial class IFFConsoleWindow : FancyWindow,
|
||||
ShowIFF?.Invoke(pressed);
|
||||
}
|
||||
|
||||
private void ShowVesselPressed(bool pressed)
|
||||
{
|
||||
ShowVessel?.Invoke(pressed);
|
||||
}
|
||||
|
||||
public void UpdateState(IFFConsoleBoundUserInterfaceState state)
|
||||
{
|
||||
if ((state.AllowedFlags & IFFFlags.HideLabel) != 0x0)
|
||||
if ((state.AllowedFlags & IFFFlags.HideLabel) != 0x0 || (state.AllowedFlags & IFFFlags.Hide) != 0x0)
|
||||
{
|
||||
ShowIFFOffButton.Disabled = false;
|
||||
ShowIFFOnButton.Disabled = false;
|
||||
|
||||
if ((state.Flags & IFFFlags.HideLabel) != 0x0)
|
||||
if ((state.Flags & IFFFlags.HideLabel) != 0x0 || (state.Flags & IFFFlags.Hide) != 0x0)
|
||||
{
|
||||
ShowIFFOffButton.Pressed = true;
|
||||
}
|
||||
@@ -62,25 +50,5 @@ public sealed partial class IFFConsoleWindow : FancyWindow,
|
||||
ShowIFFOffButton.Disabled = true;
|
||||
ShowIFFOnButton.Disabled = true;
|
||||
}
|
||||
|
||||
if ((state.AllowedFlags & IFFFlags.Hide) != 0x0)
|
||||
{
|
||||
ShowVesselOffButton.Disabled = false;
|
||||
ShowVesselOnButton.Disabled = false;
|
||||
|
||||
if ((state.Flags & IFFFlags.Hide) != 0x0)
|
||||
{
|
||||
ShowVesselOffButton.Pressed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowVesselOnButton.Pressed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowVesselOffButton.Disabled = true;
|
||||
ShowVesselOnButton.Disabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
else
|
||||
{
|
||||
// We'll send the "adjusted" position and server will adjust it back when relevant.
|
||||
var mapCoords = new MapCoordinates(InverseMapPosition(args.RelativePosition), ViewingMap);
|
||||
var mapCoords = new MapCoordinates(InverseMapPosition(args.RelativePixelPosition), ViewingMap);
|
||||
RequestFTL?.Invoke(mapCoords, _ftlAngle);
|
||||
}
|
||||
}
|
||||
@@ -180,7 +180,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
|
||||
// Remove offset so we can floor.
|
||||
var botLeft = new Vector2(0f, 0f);
|
||||
var topRight = botLeft + Size;
|
||||
var topRight = botLeft + PixelSize;
|
||||
|
||||
var flooredBL = botLeft - originBL;
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Content.Shared.Silicons.Borgs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.Silicons.Borgs;
|
||||
@@ -24,31 +23,29 @@ public sealed class BorgBoundUserInterface : BoundUserInterface
|
||||
|
||||
_menu.BrainButtonPressed += () =>
|
||||
{
|
||||
SendMessage(new BorgEjectBrainBuiMessage());
|
||||
SendPredictedMessage(new BorgEjectBrainBuiMessage());
|
||||
};
|
||||
|
||||
_menu.EjectBatteryButtonPressed += () =>
|
||||
{
|
||||
SendMessage(new BorgEjectBatteryBuiMessage());
|
||||
SendPredictedMessage(new BorgEjectBatteryBuiMessage());
|
||||
};
|
||||
|
||||
_menu.NameChanged += name =>
|
||||
{
|
||||
SendMessage(new BorgSetNameBuiMessage(name));
|
||||
SendPredictedMessage(new BorgSetNameBuiMessage(name));
|
||||
};
|
||||
|
||||
_menu.RemoveModuleButtonPressed += module =>
|
||||
{
|
||||
SendMessage(new BorgRemoveModuleBuiMessage(EntMan.GetNetEntity(module)));
|
||||
SendPredictedMessage(new BorgRemoveModuleBuiMessage(EntMan.GetNetEntity(module)));
|
||||
};
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
public override void Update()
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not BorgBuiState msg)
|
||||
return;
|
||||
_menu?.UpdateState(msg);
|
||||
_menu?.UpdateBatteryButton();
|
||||
_menu?.UpdateBrainButton();
|
||||
_menu?.UpdateModulePanel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.NameIdentifier;
|
||||
using Content.Shared.NameModifier.EntitySystems;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Silicons.Borgs;
|
||||
using Content.Shared.Power.EntitySystems;
|
||||
using Content.Shared.PowerCell;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
@@ -20,6 +20,8 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entity = default!;
|
||||
private readonly NameModifierSystem _nameModifier;
|
||||
private readonly PowerCellSystem _powerCell;
|
||||
private readonly SharedBatterySystem _battery;
|
||||
|
||||
public Action? BrainButtonPressed;
|
||||
public Action? EjectBatteryButtonPressed;
|
||||
@@ -41,6 +43,8 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_nameModifier = _entity.System<NameModifierSystem>();
|
||||
_powerCell = _entity.System<PowerCellSystem>();
|
||||
_battery = _entity.System<SharedBatterySystem>();
|
||||
|
||||
_maxNameLength = _cfgManager.GetCVar(CCVars.MaxNameLength);
|
||||
|
||||
@@ -52,8 +56,6 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
NameLineEdit.OnTextChanged += OnNameChanged;
|
||||
NameLineEdit.OnTextEntered += OnNameEntered;
|
||||
NameLineEdit.OnFocusExit += OnNameFocusExit;
|
||||
|
||||
UpdateBrainButton();
|
||||
}
|
||||
|
||||
public void SetEntity(EntityUid entity)
|
||||
@@ -73,6 +75,10 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
NameIdentifierLabel.Visible = false;
|
||||
NameLineEdit.Text = _entity.GetComponent<MetaDataComponent>(Entity).EntityName;
|
||||
}
|
||||
|
||||
UpdateBatteryButton();
|
||||
UpdateBrainButton();
|
||||
UpdateModulePanel();
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
@@ -80,21 +86,24 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
base.FrameUpdate(args);
|
||||
|
||||
AccumulatedTime += args.DeltaSeconds;
|
||||
BorgSprite.OverrideDirection = (Direction) ((int) AccumulatedTime % 4 * 2);
|
||||
}
|
||||
BorgSprite.OverrideDirection = (Direction)((int)AccumulatedTime % 4 * 2);
|
||||
|
||||
public void UpdateState(BorgBuiState state)
|
||||
{
|
||||
EjectBatteryButton.Disabled = !state.HasBattery;
|
||||
ChargeBar.Value = state.ChargePercent;
|
||||
var chargeFraction = 0f;
|
||||
|
||||
if (_powerCell.TryGetBatteryFromSlot(Entity, out var battery))
|
||||
chargeFraction = _battery.GetCharge(battery.Value.AsNullable()) / battery.Value.Comp.MaxCharge;
|
||||
|
||||
ChargeBar.Value = chargeFraction;
|
||||
ChargeLabel.Text = Loc.GetString("borg-ui-charge-label",
|
||||
("charge", (int) MathF.Round(state.ChargePercent * 100)));
|
||||
|
||||
UpdateBrainButton();
|
||||
UpdateModulePanel();
|
||||
("charge", (int)MathF.Round(chargeFraction * 100)));
|
||||
}
|
||||
|
||||
private void UpdateBrainButton()
|
||||
public void UpdateBatteryButton()
|
||||
{
|
||||
EjectBatteryButton.Disabled = !_powerCell.HasBattery(Entity);
|
||||
}
|
||||
|
||||
public void UpdateBrainButton()
|
||||
{
|
||||
if (_entity.TryGetComponent(Entity, out BorgChassisComponent? chassis) && chassis.BrainEntity is { } brain)
|
||||
{
|
||||
@@ -113,7 +122,7 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateModulePanel()
|
||||
public void UpdateModulePanel()
|
||||
{
|
||||
if (!_entity.TryGetComponent(Entity, out BorgChassisComponent? chassis))
|
||||
return;
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
StyleClasses="PanelLight"
|
||||
Margin="5 5 5 0">
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="5 5 0 5">
|
||||
<TextureButton Name="RemoveButton" VerticalAlignment="Top" HorizontalAlignment="Left" Scale="0.5 0.5"/>
|
||||
<SpriteView Name="ModuleView" Margin="0 0 5 0"/>
|
||||
<BoxContainer RectClipContent="True" HorizontalExpand="True">
|
||||
<Label Name="ModuleName" HorizontalExpand="True" HorizontalAlignment="Center"/>
|
||||
</BoxContainer>
|
||||
<TextureButton Name="RemoveButton" VerticalAlignment="Top" HorizontalAlignment="Right" Scale="0.5 0.5"/>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
using Content.Shared.PowerCell.Components;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client.Silicons.Borgs;
|
||||
|
||||
public sealed partial class BorgSystem
|
||||
{
|
||||
// How often to update the battery alert.
|
||||
// Also gets updated instantly when switching bodies or a battery is inserted or removed.
|
||||
private static readonly TimeSpan AlertUpdateDelay = TimeSpan.FromSeconds(0.5f);
|
||||
|
||||
// Don't put this on the component because we only need to track the time for a single entity
|
||||
// and we don't want to TryComp it every single tick.
|
||||
private TimeSpan _nextAlertUpdate = TimeSpan.Zero;
|
||||
private EntityQuery<BorgChassisComponent> _chassisQuery;
|
||||
private EntityQuery<PowerCellSlotComponent> _slotQuery;
|
||||
|
||||
public void InitializeBattery()
|
||||
{
|
||||
SubscribeLocalEvent<BorgChassisComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<BorgChassisComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
|
||||
|
||||
_chassisQuery = GetEntityQuery<BorgChassisComponent>();
|
||||
_slotQuery = GetEntityQuery<PowerCellSlotComponent>();
|
||||
}
|
||||
|
||||
private void OnPlayerAttached(Entity<BorgChassisComponent> ent, ref LocalPlayerAttachedEvent args)
|
||||
{
|
||||
UpdateBatteryAlert((ent.Owner, ent.Comp, null));
|
||||
}
|
||||
|
||||
private void OnPlayerDetached(Entity<BorgChassisComponent> ent, ref LocalPlayerDetachedEvent args)
|
||||
{
|
||||
// Remove all borg related alerts.
|
||||
_alerts.ClearAlert(ent.Owner, ent.Comp.BatteryAlert);
|
||||
_alerts.ClearAlert(ent.Owner, ent.Comp.NoBatteryAlert);
|
||||
}
|
||||
|
||||
private void UpdateBatteryAlert(Entity<BorgChassisComponent, PowerCellSlotComponent?> ent)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp2, false))
|
||||
return;
|
||||
|
||||
if (!_powerCell.TryGetBatteryFromSlot((ent.Owner, ent.Comp2), out var battery))
|
||||
{
|
||||
_alerts.ShowAlert(ent.Owner, ent.Comp1.NoBatteryAlert);
|
||||
return;
|
||||
}
|
||||
|
||||
// Alert levels from 0 to 10.
|
||||
var chargeLevel = (short)MathF.Round(_battery.GetChargeLevel(battery.Value.AsNullable()) * 10f);
|
||||
|
||||
// we make sure 0 only shows if they have absolutely no battery.
|
||||
// also account for floating point imprecision
|
||||
if (chargeLevel == 0 && _powerCell.HasDrawCharge((ent.Owner, null, ent.Comp2)))
|
||||
{
|
||||
chargeLevel = 1;
|
||||
}
|
||||
|
||||
_alerts.ShowAlert(ent.Owner, ent.Comp1.BatteryAlert, chargeLevel);
|
||||
}
|
||||
|
||||
// Periodically update the charge indicator.
|
||||
// We do this with a client-side alert so that we don't have to network the charge level.
|
||||
public void UpdateBattery(float frameTime)
|
||||
{
|
||||
if (_player.LocalEntity is not { } localPlayer)
|
||||
return;
|
||||
|
||||
var curTime = _timing.CurTime;
|
||||
|
||||
if (curTime < _nextAlertUpdate)
|
||||
return;
|
||||
|
||||
_nextAlertUpdate = curTime + AlertUpdateDelay;
|
||||
|
||||
if (!_chassisQuery.TryComp(localPlayer, out var chassis) || !_slotQuery.TryComp(localPlayer, out var slot))
|
||||
return;
|
||||
|
||||
UpdateBatteryAlert((localPlayer, chassis, slot));
|
||||
}
|
||||
}
|
||||
@@ -1,72 +1,93 @@
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Power.EntitySystems;
|
||||
using Content.Shared.PowerCell;
|
||||
using Content.Shared.Silicons.Borgs;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Silicons.Borgs;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed class BorgSystem : SharedBorgSystem
|
||||
public sealed partial class BorgSystem : SharedBorgSystem
|
||||
{
|
||||
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||
[Dependency] private readonly SharedBatterySystem _battery = default!;
|
||||
[Dependency] private readonly AlertsSystem _alerts = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
InitializeBattery();
|
||||
|
||||
SubscribeLocalEvent<BorgChassisComponent, AppearanceChangeEvent>(OnBorgAppearanceChanged);
|
||||
SubscribeLocalEvent<MMIComponent, AppearanceChangeEvent>(OnMMIAppearanceChanged);
|
||||
}
|
||||
|
||||
private void OnBorgAppearanceChanged(EntityUid uid, BorgChassisComponent component, ref AppearanceChangeEvent args)
|
||||
public override void UpdateUI(Entity<BorgChassisComponent?> chassis)
|
||||
{
|
||||
if (_ui.TryGetOpenUi(chassis.Owner, BorgUiKey.Key, out var bui))
|
||||
bui.Update();
|
||||
}
|
||||
|
||||
private void OnBorgAppearanceChanged(Entity<BorgChassisComponent> chassis, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
UpdateBorgAppearance(uid, component, args.Component, args.Sprite);
|
||||
|
||||
UpdateBorgAppearance((chassis.Owner, chassis.Comp, args.Component, args.Sprite));
|
||||
}
|
||||
|
||||
protected override void OnInserted(EntityUid uid, BorgChassisComponent component, EntInsertedIntoContainerMessage args)
|
||||
protected override void OnInserted(Entity<BorgChassisComponent> chassis, ref EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (!component.Initialized)
|
||||
if (!chassis.Comp.Initialized)
|
||||
return;
|
||||
|
||||
base.OnInserted(uid, component, args);
|
||||
UpdateBorgAppearance(uid, component);
|
||||
base.OnInserted(chassis, ref args);
|
||||
UpdateUI(chassis.AsNullable());
|
||||
UpdateBorgAppearance((chassis, chassis.Comp));
|
||||
UpdateBatteryAlert((chassis.Owner, chassis.Comp, null));
|
||||
}
|
||||
|
||||
protected override void OnRemoved(EntityUid uid, BorgChassisComponent component, EntRemovedFromContainerMessage args)
|
||||
protected override void OnRemoved(Entity<BorgChassisComponent> chassis, ref EntRemovedFromContainerMessage args)
|
||||
{
|
||||
if (!component.Initialized)
|
||||
if (!chassis.Comp.Initialized)
|
||||
return;
|
||||
|
||||
base.OnRemoved(uid, component, args);
|
||||
UpdateBorgAppearance(uid, component);
|
||||
base.OnRemoved(chassis, ref args);
|
||||
UpdateUI(chassis.AsNullable());
|
||||
UpdateBorgAppearance((chassis, chassis.Comp));
|
||||
UpdateBatteryAlert((chassis.Owner, chassis.Comp, null));
|
||||
}
|
||||
|
||||
private void UpdateBorgAppearance(EntityUid uid,
|
||||
BorgChassisComponent? component = null,
|
||||
AppearanceComponent? appearance = null,
|
||||
SpriteComponent? sprite = null)
|
||||
private void UpdateBorgAppearance(Entity<BorgChassisComponent?, AppearanceComponent?, SpriteComponent?> ent)
|
||||
{
|
||||
if (!Resolve(uid, ref component, ref appearance, ref sprite))
|
||||
if (!Resolve(ent, ref ent.Comp1, ref ent.Comp2, ref ent.Comp3))
|
||||
return;
|
||||
|
||||
if (_appearance.TryGetData<MobState>(uid, MobStateVisuals.State, out var state, appearance))
|
||||
if (_appearance.TryGetData<MobState>(ent.Owner, MobStateVisuals.State, out var state, ent.Comp2))
|
||||
{
|
||||
if (state != MobState.Alive)
|
||||
{
|
||||
_sprite.LayerSetVisible((uid, sprite), BorgVisualLayers.Light, false);
|
||||
_sprite.LayerSetVisible((ent.Owner, ent.Comp3), BorgVisualLayers.Light, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_appearance.TryGetData<bool>(uid, BorgVisuals.HasPlayer, out var hasPlayer, appearance))
|
||||
if (!_appearance.TryGetData<bool>(ent.Owner, BorgVisuals.HasPlayer, out var hasPlayer, ent.Comp2))
|
||||
hasPlayer = false;
|
||||
|
||||
_sprite.LayerSetVisible((uid, sprite), BorgVisualLayers.Light, component.BrainEntity != null || hasPlayer);
|
||||
_sprite.LayerSetRsiState((uid, sprite), BorgVisualLayers.Light, hasPlayer ? component.HasMindState : component.NoMindState);
|
||||
_sprite.LayerSetVisible((ent.Owner, ent.Comp3), BorgVisualLayers.Light, ent.Comp1.BrainEntity != null || hasPlayer);
|
||||
_sprite.LayerSetRsiState((ent.Owner, ent.Comp3), BorgVisualLayers.Light, hasPlayer ? ent.Comp1.HasMindState : ent.Comp1.NoMindState);
|
||||
}
|
||||
|
||||
private void OnMMIAppearanceChanged(EntityUid uid, MMIComponent component, ref AppearanceChangeEvent args)
|
||||
@@ -107,4 +128,10 @@ public sealed class BorgSystem : SharedBorgSystem
|
||||
borg.Comp.HasMindState = hasMindState;
|
||||
borg.Comp.NoMindState = noMindState;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
UpdateBattery(frameTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,5 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Client.Storage.Components;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.Foldable;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Lock;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Content.Shared.Storage.EntitySystems;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.GameStates;
|
||||
using Content.Shared.Storage.EntitySystems;
|
||||
|
||||
namespace Content.Client.Storage.Systems;
|
||||
|
||||
public sealed class EntityStorageSystem : SharedEntityStorageSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<EntityStorageComponent, EntityUnpausedEvent>(OnEntityUnpausedEvent);
|
||||
SubscribeLocalEvent<EntityStorageComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<EntityStorageComponent, ComponentStartup>(OnComponentStartup);
|
||||
SubscribeLocalEvent<EntityStorageComponent, ActivateInWorldEvent>(OnInteract, after: new[] { typeof(LockSystem) });
|
||||
SubscribeLocalEvent<EntityStorageComponent, LockToggleAttemptEvent>(OnLockToggleAttempt);
|
||||
SubscribeLocalEvent<EntityStorageComponent, DestructionEventArgs>(OnDestruction);
|
||||
SubscribeLocalEvent<EntityStorageComponent, GetVerbsEvent<InteractionVerb>>(AddToggleOpenVerb);
|
||||
SubscribeLocalEvent<EntityStorageComponent, ContainerRelayMovementEntityEvent>(OnRelayMovement);
|
||||
SubscribeLocalEvent<EntityStorageComponent, FoldAttemptEvent>(OnFoldAttempt);
|
||||
|
||||
SubscribeLocalEvent<EntityStorageComponent, ComponentGetState>(OnGetState);
|
||||
SubscribeLocalEvent<EntityStorageComponent, ComponentHandleState>(OnHandleState);
|
||||
}
|
||||
|
||||
public override bool ResolveStorage(EntityUid uid, [NotNullWhen(true)] ref EntityStorageComponent? component)
|
||||
{
|
||||
if (component != null)
|
||||
return true;
|
||||
|
||||
TryComp<EntityStorageComponent>(uid, out var storage);
|
||||
component = storage;
|
||||
return component != null;
|
||||
}
|
||||
}
|
||||
public sealed class EntityStorageSystem : SharedEntityStorageSystem;
|
||||
|
||||
@@ -14,10 +14,10 @@ public static class ColorExtensions
|
||||
{
|
||||
DebugTools.Assert(lightness is >= 0.0f and <= 1.0f);
|
||||
|
||||
var oklab = Color.ToLab(c);
|
||||
var oklab = c.LabFromSrgb();
|
||||
oklab.X = lightness;
|
||||
|
||||
return Color.FromLab(oklab);
|
||||
return oklab.LabToSrgb();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -25,10 +25,10 @@ public static class ColorExtensions
|
||||
/// </summary>
|
||||
public static Color NudgeLightness(this Color c, float lightnessShift)
|
||||
{
|
||||
var oklab = Color.ToLab(c);
|
||||
var oklab = c.LabFromSrgb();
|
||||
oklab.X = Math.Clamp(oklab.X + lightnessShift, 0, 1);
|
||||
|
||||
return Color.FromLab(oklab);
|
||||
return oklab.LabToSrgb();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,12 +39,12 @@ public static class ColorExtensions
|
||||
/// </remarks>
|
||||
public static Color NudgeChroma(this Color c, float chromaShift)
|
||||
{
|
||||
var oklab = Color.ToLab(c);
|
||||
var oklab = c.LabFromSrgb();
|
||||
var oklch = Color.ToLch(oklab);
|
||||
|
||||
oklch.Y = Math.Clamp(oklch.Y + chromaShift, 0, 1);
|
||||
|
||||
return Color.FromLab(Color.FromLch(oklch));
|
||||
return Color.FromLch(oklch).LabToSrgb();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -54,10 +54,43 @@ public static class ColorExtensions
|
||||
{
|
||||
DebugTools.Assert(factor is >= 0.0f and <= 1.0f);
|
||||
|
||||
var okFrom = Color.ToLab(from);
|
||||
var okTo = Color.ToLab(to);
|
||||
var okFrom = from.LabFromSrgb();
|
||||
var okTo = to.LabFromSrgb();
|
||||
|
||||
var blended = Vector4.Lerp(okFrom, okTo, factor);
|
||||
return Color.FromLab(blended);
|
||||
return blended.LabToSrgb();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a nonlinear sRGB ("normal") color to OkLAB.
|
||||
/// </summary>
|
||||
public static Vector4 LabFromSrgb(this Color from)
|
||||
{
|
||||
return Color.ToLab(Color.FromSrgb(from));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts OkLAB to a nonlinear sRGB ("normal") color.
|
||||
/// </summary>
|
||||
public static Color LabToSrgb(this Vector4 from)
|
||||
{
|
||||
return Color.ToSrgb(Color.FromLab(from).SimpleClipGamut());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clips the gamut of the color so that all color channels are in the range 0 -> 1.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This uses no clever perceptual techniques, it literally just clamps the individual channels.
|
||||
/// </remarks>
|
||||
public static Color SimpleClipGamut(this Color from)
|
||||
{
|
||||
return new Color
|
||||
{
|
||||
R = Math.Clamp(from.R, 0, 1),
|
||||
G = Math.Clamp(from.G, 0, 1),
|
||||
B = Math.Clamp(from.B, 0, 1),
|
||||
A = from.A,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
Title="{Loc 'thief-backpack-window-title'}"
|
||||
MinSize="700 700">
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
|
||||
<!-- First Informational panel -->
|
||||
@@ -25,7 +24,6 @@
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</PanelContainer>
|
||||
|
||||
<!-- Third approve button panel -->
|
||||
<PanelContainer Margin="10">
|
||||
<Button Name="ApproveButton"
|
||||
|
||||
@@ -46,7 +46,8 @@ public sealed partial class ThiefBackpackMenu : FancyWindow
|
||||
selectedNumber++;
|
||||
}
|
||||
|
||||
Description.Text = Loc.GetString("thief-backpack-window-description", ("maxCount", state.MaxSelectedSets));
|
||||
Title = Loc.GetString(state.ToolName);
|
||||
Description.Text = Loc.GetString(state.ToolDesc, ("maxCount", state.MaxSelectedSets));
|
||||
SelectedSets.Text = Loc.GetString("thief-backpack-window-selected", ("selectedCount", selectedNumber), ("maxCount", state.MaxSelectedSets));
|
||||
ApproveButton.Disabled = selectedNumber != state.MaxSelectedSets;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
using Content.Shared.Tips;
|
||||
|
||||
namespace Content.Client.Tips;
|
||||
|
||||
public sealed class TipsSystem : SharedTipsSystem;
|
||||
@@ -28,7 +28,16 @@ public class ListContainer : Control
|
||||
/// Called when creating a button on the UI.
|
||||
/// The provided <see cref="ListContainerButton"/> is the generated button that Controls should be parented to.
|
||||
/// </summary>
|
||||
public Action<ListData, ListContainerButton>? GenerateItem;
|
||||
public Action<ListData, ListContainerButton>? GenerateItem
|
||||
{
|
||||
get => _generateItem;
|
||||
set {
|
||||
_generateItem = value;
|
||||
// Invalidate _itemHeight so we recalculate the size of children the next
|
||||
// time PopulateList() is called
|
||||
_itemHeight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BaseButton.OnPressed"/>
|
||||
public Action<BaseButton.ButtonEventArgs, ListData>? ItemPressed;
|
||||
@@ -59,6 +68,7 @@ public class ListContainer : Control
|
||||
private bool _updateChildren = false;
|
||||
private bool _suppressScrollValueChanged;
|
||||
private ButtonGroup? _buttonGroup;
|
||||
public Action<ListData, ListContainerButton>? _generateItem;
|
||||
|
||||
public int ScrollSpeedY { get; set; } = 50;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Content.Client.UserInterface.RichText;
|
||||
/// <summary>
|
||||
/// Sets the font to a monospaced variant
|
||||
/// </summary>
|
||||
public sealed class MonoTag : IMarkupTag
|
||||
public sealed class MonoTag : IMarkupTagHandler
|
||||
{
|
||||
public static readonly ProtoId<FontPrototype> MonoFont = "Monospace";
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user